@moberg_hr/work-tree 1.5.0 → 1.7.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/bin.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/bin.ts","../src/cli.ts","../src/commands/config.ts","../src/core/config.ts","../src/core/git.ts","../src/core/logger.ts","../src/core/claude-md.ts","../src/utils/platform.ts","../src/core/ai-launcher.ts","../src/commands/init.ts","../src/core/setup-completions.ts","../src/commands/tree.ts","../src/core/resolve.ts","../src/core/worktree.ts","../src/core/history.ts","../src/core/fs-safe.ts","../src/core/claude-activity.ts","../src/core/port-allocator.ts","../src/core/copy-files.ts","../src/commands/remove.ts","../src/commands/list.ts","../src/commands/status.ts","../src/utils/format.ts","../src/commands/recent.ts","../src/commands/resume.ts","../src/commands/prune.ts","../src/core/prunable-scan.ts","../src/commands/sync.ts","../src/tui-ink/index.tsx","../src/tui-ink/App.tsx","../src/core/pr.ts","../src/core/jira.ts","../src/core/tasks.ts","../src/tui/session.ts","../src/core/hook-server.ts","../src/core/settings-editor.ts","../src/core/notifier.ts","../src/core/status-hooks.ts","../src/tui-ink/renderer-lines.ts","../src/tui-ink/Sidebar.tsx","../src/tui-ink/TerminalPane.tsx","../src/tui-ink/StatusBar.tsx","../src/commands/dash.ts","../src/commands/completion.ts","../src/commands/todo.ts","../src/commands/hydrate.ts","../src/core/hydrate.ts","../src/commands/diff.ts","../src/core/diff-pipeline.ts","../src/core/diff-parse.ts","../src/core/lcov.ts","../src/core/repo-spec.ts","../src/core/diff-scope.ts","../src/core/comment-server.ts","../src/core/comment-store.ts","../src/core/diff-server.ts","../src/core/fs-watcher.ts","../src/core/web-static.ts","../src/core/spa-handler.ts","../src/core/comment-schemas.ts","../src/core/review-poll.ts","../src/core/static-renderer.ts","../src/commands/web.ts","../src/core/web-server.ts","../src/core/web-state.ts","../src/core/pty-pool.ts","../src/core/comment-file-store.ts","../src/core/pending-delivery.ts","../src/core/session-meta.ts","../src/core/session-comment-routes.ts","../src/core/panes-routes.ts","../src/core/worktree-routes.ts","../src/core/scope-routes.ts","../src/core/checkpoint.ts","../src/core/scope-manager.ts","../src/core/terminal-routes.ts","../src/core/terminal-ws.ts","../src/core/command-hook-installer.ts","../src/commands/hook.ts","../src/commands/run.ts","../src/core/fleet.ts","../src/commands/broadcast.ts","../src/core/broadcast.ts","../src/completions/index.ts","../src/version.ts"],"sourcesContent":["import fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport chalk from 'chalk';\r\nimport { run } from './cli.js';\r\nimport { installConsoleLogger, debug } from './core/logger.js';\r\nimport { getConfigDir } from './core/config.js';\r\n\r\n// Install debug logging — all console.log/error/warn also write to ~/.work/debug.log\r\ninstallConsoleLogger();\r\ndebug('--- work started', process.argv.slice(2).join(' '), '---');\r\n\r\n// Force color support — this is an interactive CLI, and some Windows terminals\r\n// (e.g. PowerShell via conhost) don't set isTTY on spawned .cmd shims.\r\nif (!process.env.NO_COLOR && chalk.level === 0) {\r\n chalk.level = 1;\r\n}\r\n\r\nfunction handleFatalError(err: unknown): void {\r\n if (err instanceof Error && err.name === 'ExitPromptError') {\r\n console.log('\\nCancelled.');\r\n process.exit(0);\r\n }\r\n // node-pty can throw async errors for already-exited PTYs — non-fatal in dash mode\r\n if (err instanceof Error && err.message?.includes('pty that has already exited')) {\r\n try {\r\n fs.appendFileSync(path.join(getConfigDir(), 'debug.log'),\r\n `${new Date().toISOString()} [WARN] Ignored async node-pty error: ${err.message}\\n`);\r\n } catch { /* */ }\r\n return;\r\n }\r\n try {\r\n const msg = err instanceof Error ? err.stack || err.message : String(err);\r\n fs.appendFileSync(path.join(getConfigDir(), 'debug.log'),\r\n `${new Date().toISOString()} [FATAL] handleFatalError: ${msg}\\n`);\r\n } catch { /* */ }\r\n console.error(err);\r\n process.exit(1);\r\n}\r\n\r\nprocess.on('uncaughtException', handleFatalError);\r\nprocess.on('unhandledRejection', handleFatalError);\r\n\r\nrun(process.argv.slice(2));\r\n","import chalk from 'chalk';\nimport yargs from 'yargs';\nimport { configCommand } from './commands/config.js';\nimport { initCommand } from './commands/init.js';\nimport { treeCommand } from './commands/tree.js';\nimport { removeCommand } from './commands/remove.js';\nimport { listCommand } from './commands/list.js';\nimport { statusCommand } from './commands/status.js';\nimport { recentCommand } from './commands/recent.js';\nimport { resumeCommand } from './commands/resume.js';\nimport { pruneCommand } from './commands/prune.js';\nimport { syncCommand } from './commands/sync.js';\nimport { dashCommand } from './commands/dash.js';\nimport { completionCommand } from './commands/completion.js';\nimport { todoCommand } from './commands/todo.js';\nimport { hydrateCommand } from './commands/hydrate.js';\nimport { diffCommand } from './commands/diff.js';\nimport { webCommand } from './commands/web.js';\nimport { hookCommand } from './commands/hook.js';\nimport { runCommand } from './commands/run.js';\nimport { broadcastCommand } from './commands/broadcast.js';\nimport { completionHandler } from './completions/index.js';\nimport { VERSION } from './version.js';\n\nfunction showHelp() {\n console.log('');\n console.log(chalk.cyan(`Work - Git Worktree Manager ${chalk.gray(`v${VERSION}`)}`));\n console.log(chalk.cyan('============================'));\n console.log('');\n console.log(chalk.green('Usage:'));\n console.log(' work init - Set up configuration');\n console.log(' work config <action> - Manage configuration');\n console.log(' work list [project|group] - List all worktrees');\n console.log(' work tree|t <project|group> <branch> - Create/switch to worktree');\n console.log(' work tree <project|group> <branch> --base <branch> - Create from a specific base branch');\n console.log(' work tree <project|group> <branch> --open - Also open VS Code');\n console.log(' work tree <project|group> <branch> --unsafe - Skip AI tool permission checks');\n console.log(' work remove <project|group> <branch> - Remove worktree');\n console.log(' work remove <project|group> <branch> --force - Force remove worktree');\n console.log(' work status [project|group] [branch] - Show worktree status');\n console.log(' work status --prune - Remove stale entries');\n console.log(' work recent [count] - List recent sessions');\n console.log(' work resume - Resume a recent session');\n console.log(' work dash - Interactive session dashboard');\n console.log(' work web - Browser dashboard (one tab, every session)');\n console.log(' work prune - Remove merged worktrees');\n console.log(' work prune --force - Remove all merged (no prompt)');\n console.log(' work sync - Fetch all repos and prune merged (non-interactive)');\n console.log(' work sync --dry-run - Show what sync would prune, remove nothing');\n console.log(' work sync --force - Also remove worktrees with uncommitted/unpushed changes');\n console.log(' work sync --include-squash - Also prune squash-merged branches (lower confidence)');\n console.log(' work hydrate - Seed history from worktrees on disk');\n console.log(' work diff [base] - Open a GitHub-PR-style diff in your browser');\n console.log(' work run <cmd...> - Run a command in every worktree');\n console.log(' work run <cmd...> --target <alias> --parallel - Filter + run concurrently');\n console.log(' work broadcast <prompt> - Queue a prompt to every live session');\n console.log(' work todo - List tasks');\n console.log(' work todo add <text> - Add a task');\n console.log(' work todo done <id> - Mark task complete');\n console.log(' work todo rm <id> - Remove a task');\n console.log(' work completion --install - Install shell completions');\n console.log('');\n console.log(chalk.green('Config Actions:'));\n console.log(' work config add <alias> <path> - Add a repository');\n console.log(' work config remove <alias> - Remove a repository');\n console.log(' work config list - List repos and groups');\n console.log(' work config group add <name> <alias1> <alias2> ... - Create a repository group');\n console.log(' work config group remove <name> - Remove a repository group');\n console.log(' work config group regen <name> - Regenerate group CLAUDE.md');\n console.log('');\n console.log(chalk.green('Examples:'));\n console.log(' work init');\n console.log(' work list');\n console.log(' work list ai');\n console.log(' work tree ai feature/login');\n console.log(' work tree frontend feature/login --open');\n console.log(' work tree ai feature/hotfix --unsafe');\n console.log(' work remove ai feature/login');\n console.log('');\n console.log(chalk.gray(' # Groups (multi-repo worktrees):'));\n console.log(' work config group add fullstack api frontend');\n console.log(' work tree fullstack feature/login');\n console.log(' work remove fullstack feature/login');\n console.log('');\n}\n\nexport function run(argv: string[]) {\n // Show custom colored help when no args given\n if (argv.length === 0) {\n showHelp();\n return;\n }\n\n const cli = yargs(argv)\n .scriptName('work')\n .usage('$0 <command> [options]')\n .command(initCommand)\n .command(configCommand)\n .command(treeCommand)\n .command(removeCommand)\n .command(listCommand)\n .command(statusCommand)\n .command(recentCommand)\n .command(resumeCommand)\n .command(pruneCommand)\n .command(syncCommand)\n .command(dashCommand)\n .command(todoCommand)\n .command(hydrateCommand)\n .command(diffCommand)\n .command(webCommand)\n .command(hookCommand)\n .command(runCommand)\n .command(broadcastCommand)\n .command(completionCommand)\n // Hidden: yargs uses this internally for --get-yargs-completions\n .completion('__completions', false as any, completionHandler)\n .demandCommand(1, 'You need to specify a command. Run work --help for usage.')\n .strict()\n .fail((msg, err, yargs) => {\n if (err?.name === 'ExitPromptError') {\n console.log('\\nCancelled.');\n process.exit(0);\n }\n if (msg) {\n yargs.showHelp();\n console.error('\\n' + msg);\n }\n if (err) console.error(err);\n process.exit(1);\n })\n .help()\n .alias('h', 'help')\n .version(VERSION)\n .alias('v', 'version')\n .wrap(Math.min(100, process.stdout.columns || 80));\n\n cli.parse();\n}\n","import fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport chalk from 'chalk';\r\nimport type { ArgumentsCamelCase, CommandModule } from 'yargs';\r\nimport {\r\n loadConfig,\r\n saveConfig,\r\n getConfigPath,\r\n getConfigDir,\r\n ensureConfig,\r\n} from '../core/config.js';\r\nimport { isGitRepo } from '../core/git.js';\r\nimport { generateGroupClaudeMd } from '../core/claude-md.js';\r\nimport { openInEditor } from '../utils/platform.js';\r\n\r\nexport const configCommand: CommandModule = {\r\n command: 'config <action>',\r\n describe: 'Manage configuration',\r\n builder: (yargs) =>\r\n yargs\r\n .showHelpOnFail(true)\r\n .positional('action', {\r\n describe: 'Config action to perform',\r\n choices: [\r\n 'add',\r\n 'remove',\r\n 'list',\r\n 'group',\r\n 'show',\r\n 'edit',\r\n ] as const,\r\n type: 'string',\r\n demandOption: true,\r\n })\r\n .option('args', {\r\n type: 'array',\r\n string: true,\r\n hidden: true,\r\n })\r\n .strict(false),\r\n handler: (argv) => {\r\n const action = argv.action as string;\r\n // Collect all extra positional args after the action\r\n const extra = (argv._ as string[]).slice(1); // slice off 'config'\r\n\r\n switch (action) {\r\n case 'add':\r\n handleAdd(extra);\r\n break;\r\n case 'remove':\r\n handleRemove(extra);\r\n break;\r\n case 'list':\r\n handleList();\r\n break;\r\n case 'group':\r\n handleGroup(extra);\r\n break;\r\n case 'show':\r\n handleShow();\r\n break;\r\n case 'edit':\r\n handleEdit();\r\n break;\r\n default:\r\n showConfigHelp();\r\n }\r\n },\r\n};\r\n\r\nfunction handleAdd(args: string[]): void {\r\n const [alias, repoPath] = args;\r\n if (!alias || !repoPath) {\r\n console.error('Usage: work config add <alias> <path>');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n if (!fs.existsSync(repoPath)) {\r\n console.error(`Path does not exist: ${repoPath}`);\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n if (!isGitRepo(repoPath)) {\r\n console.error(`Path is not a git repository: ${repoPath}`);\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n const config = ensureConfig();\r\n config.repos[alias] = repoPath;\r\n saveConfig(config);\r\n console.log(chalk.green(`Added: ${alias} -> ${repoPath}`));\r\n}\r\n\r\nfunction handleRemove(args: string[]): void {\r\n const [alias] = args;\r\n if (!alias) {\r\n console.error('Usage: work config remove <alias>');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n const config = ensureConfig();\r\n\r\n if (!(alias in config.repos)) {\r\n console.error(`Repository alias not found: ${alias}`);\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n delete config.repos[alias];\r\n saveConfig(config);\r\n console.log(chalk.green(`Removed: ${alias}`));\r\n}\r\n\r\nfunction handleList(): void {\r\n const config = loadConfig();\r\n if (!config) {\r\n console.log(\r\n chalk.yellow(\r\n 'No configuration found. Run \"work init\" to set up.',\r\n ),\r\n );\r\n return;\r\n }\r\n\r\n console.log('');\r\n console.log(chalk.cyan('Work Configuration'));\r\n console.log(chalk.cyan('=================='));\r\n console.log(chalk.green(`Worktrees Root: ${config.worktreesRoot}`));\r\n console.log('');\r\n console.log(chalk.green('Repositories:'));\r\n\r\n const repoKeys = Object.keys(config.repos);\r\n if (repoKeys.length === 0) {\r\n console.log(chalk.gray(' (none configured)'));\r\n } else {\r\n for (const key of repoKeys) {\r\n console.log(` ${key} -> ${config.repos[key]}`);\r\n }\r\n }\r\n console.log('');\r\n\r\n console.log(chalk.green('Groups:'));\r\n const groupKeys = Object.keys(config.groups);\r\n if (groupKeys.length === 0) {\r\n console.log(chalk.gray(' (none configured)'));\r\n } else {\r\n for (const key of groupKeys) {\r\n const aliases = config.groups[key].join(', ');\r\n console.log(` ${key} -> [${aliases}]`);\r\n }\r\n }\r\n console.log('');\r\n}\r\n\r\nfunction handleGroup(args: string[]): void {\r\n const [subAction, ...rest] = args;\r\n switch (subAction) {\r\n case 'add':\r\n handleAddGroup(rest);\r\n break;\r\n case 'remove':\r\n handleRemoveGroup(rest);\r\n break;\r\n case 'regen':\r\n handleRegenGroup(rest);\r\n break;\r\n default:\r\n showGroupHelp();\r\n }\r\n}\r\n\r\nfunction handleAddGroup(args: string[]): void {\r\n const [groupName, ...repoAliases] = args;\r\n if (!groupName) {\r\n console.error(\r\n 'Usage: work config group add <name> <alias1> <alias2> [alias3...]',\r\n );\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n if (repoAliases.length < 2) {\r\n console.error('A group must contain at least 2 repository aliases.');\r\n console.log(\r\n chalk.yellow(\r\n 'Usage: work config group add <name> <alias1> <alias2> [alias3...]',\r\n ),\r\n );\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n const config = ensureConfig();\r\n\r\n // Validate: all aliases exist in repos\r\n for (const alias of repoAliases) {\r\n if (!(alias in config.repos)) {\r\n console.error(`Repository alias not found: ${alias}`);\r\n console.log(\r\n chalk.yellow(\r\n `Available aliases: ${Object.keys(config.repos).join(', ')}`,\r\n ),\r\n );\r\n process.exitCode = 1;\r\n return;\r\n }\r\n }\r\n\r\n // Validate: group name doesn't collide with repo aliases\r\n if (groupName in config.repos) {\r\n console.error(\r\n `Group name '${groupName}' conflicts with an existing repository alias.`,\r\n );\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n // Validate: group name doesn't collide with repo folder names\r\n const repoFolderNames = Object.values(config.repos).map((p) =>\r\n path.basename(p),\r\n );\r\n if (repoFolderNames.includes(groupName)) {\r\n console.error(\r\n `Group name '${groupName}' conflicts with a repository folder name.`,\r\n );\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n config.groups[groupName] = repoAliases;\r\n saveConfig(config);\r\n console.log(\r\n chalk.green(\r\n `Added group: ${groupName} -> [${repoAliases.join(', ')}]`,\r\n ),\r\n );\r\n\r\n // Generate combined CLAUDE.md\r\n generateGroupClaudeMd(groupName, repoAliases, config);\r\n}\r\n\r\nfunction handleRemoveGroup(args: string[]): void {\r\n const [groupName] = args;\r\n if (!groupName) {\r\n console.error('Usage: work config group remove <name>');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n const config = ensureConfig();\r\n\r\n if (!(groupName in config.groups)) {\r\n console.error(`Group not found: ${groupName}`);\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n delete config.groups[groupName];\r\n saveConfig(config);\r\n\r\n // Delete the .claude.md file\r\n const claudeMdPath = path.join(getConfigDir(), `${groupName}.claude.md`);\r\n if (fs.existsSync(claudeMdPath)) {\r\n fs.unlinkSync(claudeMdPath);\r\n console.log(`Deleted: ${claudeMdPath}`);\r\n }\r\n\r\n console.log(chalk.green(`Removed group: ${groupName}`));\r\n}\r\n\r\nfunction handleRegenGroup(args: string[]): void {\r\n const [groupName] = args;\r\n if (!groupName) {\r\n console.error('Usage: work config group regen <name>');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n const config = ensureConfig();\r\n\r\n if (!(groupName in config.groups)) {\r\n console.error(`Group not found: ${groupName}`);\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n const repoAliases = config.groups[groupName];\r\n generateGroupClaudeMd(groupName, repoAliases, config);\r\n}\r\n\r\nfunction handleShow(): void {\r\n const configPath = getConfigPath();\r\n if (fs.existsSync(configPath)) {\r\n console.log(chalk.green(`Config file: ${configPath}`));\r\n console.log('');\r\n const content = fs.readFileSync(configPath, 'utf-8');\r\n console.log(content);\r\n } else {\r\n console.log(\r\n chalk.yellow(\r\n 'No configuration file found. Run \"work init\" to set up.',\r\n ),\r\n );\r\n }\r\n}\r\n\r\nfunction handleEdit(): void {\r\n const configPath = getConfigPath();\r\n if (!fs.existsSync(configPath)) {\r\n console.error(\r\n 'No configuration file found. Run \"work init\" first.',\r\n );\r\n process.exitCode = 1;\r\n return;\r\n }\r\n openInEditor(configPath);\r\n}\r\n\r\nfunction showConfigHelp(): void {\r\n console.log(chalk.yellow('Usage: work config <action>'));\r\n console.log('');\r\n console.log(chalk.green('Actions:'));\r\n console.log(\r\n ' add <alias> <path> - Add a repository',\r\n );\r\n console.log(\r\n ' remove <alias> - Remove a repository',\r\n );\r\n console.log(\r\n ' list - List all configured repositories and groups',\r\n );\r\n console.log(\r\n ' group <sub> - Manage groups (add, remove, regen)',\r\n );\r\n console.log(\r\n ' show - Show configuration file contents',\r\n );\r\n console.log(\r\n ' edit - Open configuration file in editor',\r\n );\r\n}\r\n\r\nfunction showGroupHelp(): void {\r\n console.log(chalk.yellow('Usage: work config group <action>'));\r\n console.log('');\r\n console.log(chalk.green('Actions:'));\r\n console.log(\r\n ' add <name> <alias1> <alias2> [...] - Create a repository group',\r\n );\r\n console.log(\r\n ' remove <name> - Remove a repository group',\r\n );\r\n console.log(\r\n ' regen <name> - Regenerate group CLAUDE.md',\r\n );\r\n}\r\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\n\n/**\n * An opt-in shell command that runs when a session changes status. The command\n * runs with the session's directory as its cwd (passed as the spawn `cwd`\n * option, never interpolated into the command string).\n */\nexport interface StatusHook {\n on: 'idle' | 'needs_input';\n command: string;\n}\n\nexport interface WorkConfig {\n worktreesRoot: string;\n repos: Record<string, string>;\n groups: Record<string, string[]>;\n copyFiles: string[];\n /**\n * AI tool command to launch in worktrees. May include extra args, e.g.\n * \"claude\" (default), \"gemini\", \"codex\", or \"my-tool --some-flag\".\n */\n aiCommand?: string;\n /**\n * Per-tool flag overrides. Defaults come from the preset matching the\n * binary in `aiCommand` (see AI_TOOL_PRESETS in core/ai-launcher.ts).\n * Set any value to an empty string to disable that flag for the configured tool.\n */\n aiCommandFlags?: {\n /** Flag for skipping permission checks. */\n unsafe?: string;\n /** Flag for resuming the most recent session. */\n resume?: string;\n /** Flag for passing an initial prompt as a file path. */\n promptFile?: string;\n /** Flag for passing an inline prompt; empty string = positional arg. */\n prompt?: string;\n };\n /** Editor command for opening worktrees. Default: \"code\" */\n editor?: string;\n /**\n * Range of dev-server ports to allocate to worktrees (inclusive).\n * Each worktree gets a stable port exposed as $PORT to the launched process.\n * Default when unset: { start: 3000, end: 3099 }.\n */\n portRange?: { start: number; end: number };\n /**\n * Opt-in desktop notifications. When true, the dashboard fires an OS\n * notification when a session goes idle or needs input. Default: off.\n */\n notifications?: boolean;\n /**\n * Opt-in shell commands run when a session changes status (idle /\n * needs_input). Each command runs with the session dir as its cwd.\n * Generalizes `notifications`; both paths work independently. Default: none.\n */\n statusHooks?: StatusHook[];\n}\n\n/** Lowest port we allow to be configured (avoid privileged ports < 1024). */\nconst MIN_PORT = 1024;\n/** Highest valid TCP port. */\nconst MAX_PORT = 65535;\n\n/**\n * Validate a configured port range. Returns the normalized range when it is a\n * pair of integers with `MIN_PORT <= start <= end <= MAX_PORT`, otherwise\n * undefined (callers then fall back to the default range). Rejects non-integers,\n * privileged/out-of-bounds ports, and reversed ranges.\n */\nexport function validatePortRange(\n value: unknown,\n): { start: number; end: number } | undefined {\n if (!value || typeof value !== 'object') return undefined;\n const { start, end } = value as { start?: unknown; end?: unknown };\n if (typeof start !== 'number' || typeof end !== 'number') return undefined;\n if (!Number.isInteger(start) || !Number.isInteger(end)) return undefined;\n if (start < MIN_PORT || end > MAX_PORT) return undefined;\n if (start > end) return undefined;\n return { start, end };\n}\n\nexport function getConfigDir(): string {\n const dir = path.join(os.homedir(), '.work');\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n return dir;\n}\n\nexport function getConfigPath(): string {\n return path.join(getConfigDir(), 'config.json');\n}\n\nexport function loadConfig(): WorkConfig | null {\n const configPath = getConfigPath();\n if (!fs.existsSync(configPath)) {\n return null;\n }\n\n try {\n const raw = fs.readFileSync(configPath, 'utf-8');\n const parsed = JSON.parse(raw);\n return {\n worktreesRoot: parsed.worktreesRoot ?? '',\n repos: parsed.repos ?? {},\n groups: parsed.groups ?? {},\n copyFiles: parsed.copyFiles ?? [],\n aiCommand: parsed.aiCommand,\n aiCommandFlags: parsed.aiCommandFlags,\n editor: parsed.editor,\n portRange: validatePortRange(parsed.portRange),\n notifications: parsed.notifications === true,\n statusHooks: Array.isArray(parsed.statusHooks) ? parsed.statusHooks : [],\n };\n } catch {\n return null;\n }\n}\n\nexport function saveConfig(config: WorkConfig): void {\n const configPath = getConfigPath();\n fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');\n}\n\nexport function ensureConfig(): WorkConfig {\n const config = loadConfig();\n if (!config) {\n throw new Error('Configuration not found. Run \"work init\" to set up.');\n }\n return config;\n}\n","import spawn from 'cross-spawn';\r\nimport { execFile } from 'node:child_process';\r\nimport path from 'node:path';\r\nimport { debug } from './logger.js';\r\n\r\nexport interface GitResult {\r\n stdout: string;\r\n stderr: string;\r\n exitCode: number;\r\n}\r\n\r\n/** Run a git command synchronously in the given cwd. */\r\nexport function git(args: string[], cwd: string): GitResult {\r\n debug('git', args.join(' '), `cwd=${cwd}`);\r\n const result = spawn.sync('git', args, {\r\n cwd,\r\n encoding: 'utf-8',\r\n stdio: ['pipe', 'pipe', 'pipe'],\r\n // Avoid flashing a console window on Windows for every git call —\r\n // matters most when this runs inside the detached `wd --watch` daemon.\r\n windowsHide: true,\r\n });\r\n\r\n const r = {\r\n stdout: (result.stdout ?? '').toString().trim(),\r\n stderr: (result.stderr ?? '').toString().trim(),\r\n exitCode: result.status ?? 1,\r\n };\r\n if (r.exitCode !== 0) {\r\n debug('git failed', { args, exitCode: r.exitCode, stderr: r.stderr });\r\n }\r\n return r;\r\n}\r\n\r\nexport interface WorktreeEntry {\r\n path: string;\r\n branch: string;\r\n head: string;\r\n}\r\n\r\n/** Parse `git worktree list --porcelain` output into structured entries. */\r\nexport function parseWorktreeList(cwd: string): WorktreeEntry[] {\r\n const result = git(['worktree', 'list', '--porcelain'], cwd);\r\n if (result.exitCode !== 0) return [];\r\n\r\n const entries: WorktreeEntry[] = [];\r\n let current: Partial<WorktreeEntry> = {};\r\n\r\n for (const line of result.stdout.split('\\n')) {\r\n const worktreeMatch = line.match(/^worktree (.+)$/);\r\n if (worktreeMatch) {\r\n if (current.path) {\r\n entries.push({\r\n path: current.path,\r\n branch: current.branch ?? '',\r\n head: current.head ?? '',\r\n });\r\n }\r\n current = { path: worktreeMatch[1] };\r\n continue;\r\n }\r\n\r\n const headMatch = line.match(/^HEAD (.+)$/);\r\n if (headMatch) {\r\n current.head = headMatch[1].substring(0, 7);\r\n continue;\r\n }\r\n\r\n const branchMatch = line.match(/^branch (.+)$/);\r\n if (branchMatch) {\r\n current.branch = branchMatch[1].replace(/^refs\\/heads\\//, '');\r\n }\r\n }\r\n\r\n // Push last entry\r\n if (current.path) {\r\n entries.push({\r\n path: current.path,\r\n branch: current.branch ?? '',\r\n head: current.head ?? '',\r\n });\r\n }\r\n\r\n return entries;\r\n}\r\n\r\n/** Check if a local branch exists. */\r\nexport function localBranchExists(branch: string, cwd: string): boolean {\r\n const result = git(['rev-parse', '--verify', branch], cwd);\r\n return result.exitCode === 0;\r\n}\r\n\r\n/** Check if a remote tracking branch exists. */\r\nexport function remoteBranchExists(branch: string, cwd: string): boolean {\r\n const result = git(['rev-parse', '--verify', `origin/${branch}`], cwd);\r\n return result.exitCode === 0;\r\n}\r\n\r\n/** Check if path is a valid git worktree/repo. */\r\nexport function isGitRepo(cwd: string): boolean {\r\n const result = git(['rev-parse', '--is-inside-work-tree'], cwd);\r\n return result.exitCode === 0 && result.stdout === 'true';\r\n}\r\n\r\n/** Get the absolute toplevel of the worktree containing cwd, or null if not a repo. */\r\nexport function getWorktreeRoot(cwd: string): string | null {\r\n const result = git(['rev-parse', '--show-toplevel'], cwd);\r\n if (result.exitCode !== 0 || !result.stdout) return null;\r\n return result.stdout;\r\n}\r\n\r\n/**\r\n * Get the MAIN repo working directory for a (possibly linked) worktree.\r\n * `--git-common-dir` points at the main repo's `.git` dir; the main repo root\r\n * is its parent when the basename is `.git`, otherwise the path itself.\r\n * Returns null on failure.\r\n */\r\nexport function getMainRepoRoot(cwd: string): string | null {\r\n const result = git(\r\n ['rev-parse', '--path-format=absolute', '--git-common-dir'],\r\n cwd,\r\n );\r\n if (result.exitCode !== 0 || !result.stdout) return null;\r\n const commonDir = result.stdout;\r\n if (path.basename(commonDir) === '.git') {\r\n return path.dirname(commonDir);\r\n }\r\n return commonDir;\r\n}\r\n\r\n/** Get current branch name. */\r\nexport function getCurrentBranch(cwd: string): string {\r\n const result = git(['branch', '--show-current'], cwd);\r\n return result.stdout;\r\n}\r\n\r\n/** Check for uncommitted changes (returns porcelain status). */\r\nexport function getStatus(cwd: string): string {\r\n const result = git(['status', '--porcelain'], cwd);\r\n return result.stdout;\r\n}\r\n\r\n/** Get the default branch from origin/HEAD (e.g. \"main\" or \"master\"). */\r\nexport function getDefaultBranch(cwd: string): string | null {\r\n const result = git(['symbolic-ref', 'refs/remotes/origin/HEAD'], cwd);\r\n if (result.exitCode === 0 && result.stdout) {\r\n // Output is like \"refs/remotes/origin/main\"\r\n return result.stdout.replace(/^refs\\/remotes\\/origin\\//, '');\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * Result of checking a branch against a single base ref.\r\n *\r\n * 'merged' is a high-confidence true merge (the branch tip is reachable from\r\n * base via a merge/fast-forward). 'squash-merged' is the lower-confidence\r\n * heuristic match from {@link checkSquashMerged} — the branch's squashed patch\r\n * already appears in base, but there is no real merge commit linking the two.\r\n * Callers that prune unattended should treat 'squash-merged' as opt-in only.\r\n */\r\ntype MergeCheck = 'merged' | 'squash-merged' | 'stale' | 'unrelated';\r\n\r\n/**\r\n * Check if a branch is truly merged into a base ref (not just a stale branch\r\n * sitting behind base with no unique commits).\r\n *\r\n * Returns 'stale' if the branch tip is on the base's first-parent chain\r\n * (no unique work). Returns 'merged' if the branch has unique commits that\r\n * are all reachable from base. Returns 'squash-merged' if only the squash\r\n * heuristic matched. Returns 'unrelated' otherwise.\r\n */\r\nfunction checkMergeStatus(branch: string, baseRef: string, cwd: string): MergeCheck {\r\n // 1. Branch must be an ancestor of base (all its commits are in base)\r\n const branchInBase = git(['merge-base', '--is-ancestor', branch, baseRef], cwd);\r\n if (branchInBase.exitCode !== 0) {\r\n // Not a regular merge — check for squash merge (low-confidence heuristic).\r\n if (checkSquashMerged(branch, baseRef, cwd)) return 'squash-merged';\r\n return 'unrelated';\r\n }\r\n\r\n // 2. If base is also ancestor of branch, they're at the same commit\r\n // (fast-forward merge or empty branch) — nothing left to merge.\r\n const baseInBranch = git(['merge-base', '--is-ancestor', baseRef, branch], cwd);\r\n if (baseInBranch.exitCode === 0) return 'merged';\r\n\r\n // 3. Branch is ancestor of base — but is it on the main line (stale) or\r\n // only reachable via a merge commit (truly merged)?\r\n // A stale branch's tip sits directly on the first-parent chain.\r\n const tip = git(['rev-parse', branch], cwd);\r\n if (tip.exitCode !== 0) return 'unrelated';\r\n const mainLine = git(['rev-list', '--first-parent', '-n', '500', baseRef], cwd);\r\n if (mainLine.exitCode === 0 && mainLine.stdout.includes(tip.stdout)) {\r\n return 'stale';\r\n }\r\n\r\n return 'merged';\r\n}\r\n\r\n/**\r\n * Detect squash merges by creating a temporary dangling commit that represents\r\n * the branch's tree squashed onto the merge-base, then checking if that patch\r\n * is already present in the base ref via `git cherry`.\r\n */\r\nfunction checkSquashMerged(branch: string, baseRef: string, cwd: string): boolean {\r\n const mb = git(['merge-base', baseRef, branch], cwd);\r\n if (mb.exitCode !== 0) return false;\r\n\r\n const tree = git(['rev-parse', `${branch}^{tree}`], cwd);\r\n if (tree.exitCode !== 0) return false;\r\n\r\n const dangling = git(['commit-tree', tree.stdout, '-p', mb.stdout, '-m', ''], cwd);\r\n if (dangling.exitCode !== 0) return false;\r\n\r\n const cherry = git(['cherry', baseRef, dangling.stdout], cwd);\r\n if (cherry.exitCode !== 0) return false;\r\n\r\n // If the output starts with \"-\", the patch is already in baseRef (squash-merged)\r\n return cherry.stdout.startsWith('-');\r\n}\r\n\r\n/** Confidence level for a positive merge result. */\r\nexport type MergeConfidence = 'merged' | 'squash-merged';\r\n\r\nexport interface MergeCheckResult {\r\n merged: boolean;\r\n /** The base ref it matched against (e.g. \"origin/main\"), or null if not merged. */\r\n into: string | null;\r\n /**\r\n * How the match was determined when `merged` is true: 'merged' is a\r\n * high-confidence true merge; 'squash-merged' is the lower-confidence\r\n * squash heuristic. Null when not merged.\r\n */\r\n confidence: MergeConfidence | null;\r\n}\r\n\r\nexport function isBranchMerged(\r\n branch: string,\r\n cwd: string,\r\n baseBranch?: string,\r\n): MergeCheckResult {\r\n const defaultBranch = getDefaultBranch(cwd);\r\n const bases = baseBranch\r\n ? [baseBranch]\r\n : [defaultBranch, 'main', 'master'].filter(\r\n (b): b is string => b !== null,\r\n );\r\n\r\n // Deduplicate (e.g. default branch is \"main\" which is already in the list)\r\n const uniqueBases = [...new Set(bases)];\r\n\r\n for (const base of uniqueBases) {\r\n // Check remote base branch first (most up-to-date after fetch)\r\n if (remoteBranchExists(base, cwd)) {\r\n const baseRef = `origin/${base}`;\r\n const status = checkMergeStatus(branch, baseRef, cwd);\r\n if (status === 'merged') return { merged: true, into: baseRef, confidence: 'merged' };\r\n if (status === 'squash-merged')\r\n return { merged: true, into: baseRef, confidence: 'squash-merged' };\r\n if (status === 'stale') return { merged: false, into: null, confidence: null };\r\n }\r\n\r\n // Fall back to local base branch\r\n if (localBranchExists(base, cwd)) {\r\n const status = checkMergeStatus(branch, base, cwd);\r\n if (status === 'merged') return { merged: true, into: base, confidence: 'merged' };\r\n if (status === 'squash-merged')\r\n return { merged: true, into: base, confidence: 'squash-merged' };\r\n if (status === 'stale') return { merged: false, into: null, confidence: null };\r\n }\r\n }\r\n\r\n return { merged: false, into: null, confidence: null };\r\n}\r\n\r\n/** Fetch latest remote refs for a repo and ensure origin/HEAD is set. */\r\nexport function fetchRemote(cwd: string): void {\r\n git(['fetch', '--quiet'], cwd);\r\n\r\n // Ensure origin/HEAD is set so getDefaultBranch works\r\n if (!getDefaultBranch(cwd)) {\r\n git(['remote', 'set-head', 'origin', '--auto'], cwd);\r\n }\r\n}\r\n\r\n/** Async version of fetchRemote — non-blocking. */\r\nexport async function fetchRemoteAsync(cwd: string): Promise<void> {\r\n await new Promise<void>((resolve, reject) => {\r\n execFile('git', ['fetch', '--quiet'], { cwd, timeout: 30000 }, (err) => {\r\n if (err) reject(err);\r\n else resolve();\r\n });\r\n });\r\n if (!getDefaultBranch(cwd)) {\r\n git(['remote', 'set-head', 'origin', '--auto'], cwd);\r\n }\r\n}\r\n\r\n/** Rebase a branch onto the default branch. Returns error message or null on success. */\r\nexport function rebaseOntoMain(branch: string, cwd: string): string | null {\r\n const defaultBranch = getDefaultBranch(cwd) ?? 'main';\r\n // Fetch latest first\r\n git(['fetch', '--quiet'], cwd);\r\n const base = remoteBranchExists(defaultBranch, cwd) ? `origin/${defaultBranch}` : defaultBranch;\r\n const result = git(['rebase', base, branch], cwd);\r\n if (result.exitCode !== 0) {\r\n // Abort the failed rebase\r\n git(['rebase', '--abort'], cwd);\r\n return result.stderr || 'Rebase failed';\r\n }\r\n return null;\r\n}\r\n\r\n/** Count merge conflicts between a branch and the default branch. Returns 0 if clean. */\r\nexport function countConflicts(branch: string, cwd: string): number {\r\n const defaultBranch = getDefaultBranch(cwd) ?? 'main';\r\n const base = remoteBranchExists(defaultBranch, cwd) ? `origin/${defaultBranch}` : defaultBranch;\r\n\r\n // Use merge-tree to simulate merge without changing worktree\r\n const mergeBase = git(['merge-base', base, branch], cwd);\r\n if (mergeBase.exitCode !== 0) return 0;\r\n\r\n const result = git(['merge-tree', mergeBase.stdout, base, branch], cwd);\r\n // Count conflict markers in merge-tree output\r\n const conflicts = (result.stdout.match(/^<<<<<<< /gm) || []).length;\r\n return conflicts;\r\n}\r\n\r\n/** Check for unpushed commits. Returns the log output or empty string. */\r\nexport function getUnpushedCommits(cwd: string): string {\r\n const branch = getCurrentBranch(cwd);\r\n if (!branch) return '';\r\n\r\n const upstreamResult = git(\r\n ['rev-parse', '--abbrev-ref', `${branch}@{upstream}`],\r\n cwd,\r\n );\r\n if (upstreamResult.exitCode !== 0) return '';\r\n\r\n const upstream = upstreamResult.stdout;\r\n const logResult = git(['log', '--oneline', `${upstream}..HEAD`], cwd);\r\n return logResult.stdout;\r\n}\r\n","import fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport { getConfigDir } from './config.js';\r\n\r\nconst MAX_LOG_SIZE = 5 * 1024 * 1024; // 5MB\r\n\r\nlet logPath: string | null = null;\r\nlet logStream: fs.WriteStream | null = null;\r\n\r\nfunction ensureLogStream(): fs.WriteStream | null {\r\n if (logStream) return logStream;\r\n try {\r\n const dir = getConfigDir();\r\n // Make sure the parent directory exists before opening a stream into\r\n // it — under tests (mocked homedir) or first-run conditions the .work\r\n // dir may not have been created yet, and createWriteStream's failure\r\n // is async (emits 'error') which would otherwise become an unhandled\r\n // exception in the host process.\r\n fs.mkdirSync(dir, { recursive: true });\r\n logPath = path.join(dir, 'debug.log');\r\n\r\n // Rotate if too large\r\n try {\r\n const stat = fs.statSync(logPath);\r\n if (stat.size > MAX_LOG_SIZE) {\r\n const prev = logPath + '.1';\r\n try { fs.unlinkSync(prev); } catch { /* */ }\r\n fs.renameSync(logPath, prev);\r\n }\r\n } catch { /* file doesn't exist yet */ }\r\n\r\n const stream = fs.createWriteStream(logPath, { flags: 'a' });\r\n // Best-effort logging — async stream errors must not crash the host.\r\n stream.on('error', () => { /* */ });\r\n logStream = stream;\r\n return logStream;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nfunction timestamp(): string {\r\n return new Date().toISOString();\r\n}\r\n\r\n// Strip ANSI escape codes for clean log output\r\nfunction stripAnsi(str: string): string {\r\n return str.replace(/\\x1b\\[[0-9;]*m/g, '');\r\n}\r\n\r\nexport function debugLog(level: 'INFO' | 'ERROR' | 'DEBUG' | 'WARN', ...args: unknown[]): void {\r\n const stream = ensureLogStream();\r\n if (!stream) return;\r\n const msg = args.map((a) => typeof a === 'string' ? stripAnsi(a) : JSON.stringify(a)).join(' ');\r\n stream.write(`${timestamp()} [${level}] ${msg}\\n`);\r\n}\r\n\r\n/**\r\n * Patch console.log and console.error to also write to the debug log.\r\n * Call once at startup.\r\n */\r\nexport function installConsoleLogger(): void {\r\n const origLog = console.log.bind(console);\r\n const origError = console.error.bind(console);\r\n const origWarn = console.warn.bind(console);\r\n\r\n console.log = (...args: unknown[]) => {\r\n origLog(...args);\r\n debugLog('INFO', ...args);\r\n };\r\n\r\n console.error = (...args: unknown[]) => {\r\n origError(...args);\r\n debugLog('ERROR', ...args);\r\n };\r\n\r\n console.warn = (...args: unknown[]) => {\r\n origWarn(...args);\r\n debugLog('WARN', ...args);\r\n };\r\n}\r\n\r\n/** Write a debug-only message (not shown to user). */\r\nexport function debug(...args: unknown[]): void {\r\n debugLog('DEBUG', ...args);\r\n}\r\n\r\n/** Get the log file path. */\r\nexport function getLogPath(): string {\r\n ensureLogStream();\r\n return logPath ?? path.join(getConfigDir(), 'debug.log');\r\n}\r\n\r\n/** Flush and close the log stream. */\r\nexport function closeLog(): void {\r\n if (logStream) {\r\n logStream.end();\r\n logStream = null;\r\n }\r\n}\r\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport spawn from 'cross-spawn';\nimport chalk from 'chalk';\nimport type { WorkConfig } from './config.js';\nimport { getConfigDir } from './config.js';\n\n/**\n * Generate a combined CLAUDE.md for a group using `claude -p`.\n * Falls back to a concatenated template if the Claude CLI call fails.\n */\nexport function generateGroupClaudeMd(\n groupName: string,\n repoAliases: string[],\n config: WorkConfig,\n): void {\n const outputPath = path.join(getConfigDir(), `${groupName}.claude.md`);\n\n // Build prompt with each repo's CLAUDE.md\n const promptParts: string[] = [];\n promptParts.push(\n 'You are generating a CLAUDE.md file for a multi-repository workspace.',\n );\n promptParts.push(\n 'The workspace contains the following repositories as subdirectories:',\n );\n promptParts.push('');\n\n for (const alias of repoAliases) {\n const repoPath = config.repos[alias];\n const repoName = path.basename(repoPath);\n const claudeMdPath = path.join(repoPath, 'CLAUDE.md');\n\n promptParts.push(`## Repository: ${repoName}/ (alias: ${alias})`);\n\n if (fs.existsSync(claudeMdPath)) {\n const content = fs.readFileSync(claudeMdPath, 'utf-8');\n promptParts.push('### CLAUDE.md contents:');\n promptParts.push('```');\n promptParts.push(content);\n promptParts.push('```');\n } else {\n promptParts.push('(no CLAUDE.md found)');\n }\n promptParts.push('');\n }\n\n promptParts.push('Generate a combined CLAUDE.md for this workspace that:');\n promptParts.push(\n '1. Explains the workspace structure (which subdirectories contain which repos)',\n );\n promptParts.push(\n \"2. Merges and synthesizes the instructions from all repos' CLAUDE.md files\",\n );\n promptParts.push(\n '3. Notes any cross-repo relationships or considerations',\n );\n promptParts.push(\n '4. Keeps all specific technical instructions (build commands, test commands, etc.) organized by repository',\n );\n promptParts.push('');\n promptParts.push(\n 'Output ONLY the markdown content for the combined CLAUDE.md, with no additional commentary.',\n );\n\n const prompt = promptParts.join('\\n');\n\n console.log(\n chalk.cyan(\n `Generating combined CLAUDE.md for group '${groupName}'...`,\n ),\n );\n console.log(\n chalk.gray('(This will call Claude to generate the combined file)'),\n );\n\n // Try claude -p\n const result = spawn.sync('claude', ['-p'], {\n input: prompt,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n\n let content: string;\n\n if (result.status !== 0 || !result.stdout?.trim()) {\n console.log(\n chalk.yellow(\n 'Failed to generate CLAUDE.md via Claude. Creating a basic template instead.',\n ),\n );\n content = buildFallbackTemplate(groupName, repoAliases, config);\n } else {\n content = result.stdout.trim();\n }\n\n fs.writeFileSync(outputPath, content, 'utf-8');\n console.log(chalk.green(`Saved: ${outputPath}`));\n}\n\nfunction buildFallbackTemplate(\n groupName: string,\n repoAliases: string[],\n config: WorkConfig,\n): string {\n const parts: string[] = [];\n parts.push(`# Multi-Repository Workspace: ${groupName}`);\n parts.push('');\n parts.push('This workspace contains the following repositories:');\n parts.push('');\n\n for (const alias of repoAliases) {\n const repoPath = config.repos[alias];\n const repoName = path.basename(repoPath);\n parts.push(`- **${repoName}/** (alias: ${alias})`);\n }\n\n parts.push('');\n parts.push('## Per-Repository Instructions');\n parts.push('');\n\n for (const alias of repoAliases) {\n const repoPath = config.repos[alias];\n const repoName = path.basename(repoPath);\n const claudeMdPath = path.join(repoPath, 'CLAUDE.md');\n\n parts.push(`### ${repoName}`);\n\n if (fs.existsSync(claudeMdPath)) {\n const content = fs.readFileSync(claudeMdPath, 'utf-8');\n parts.push(content);\n } else {\n parts.push('(no CLAUDE.md found)');\n }\n parts.push('');\n }\n\n return parts.join('\\n');\n}\n","import spawn from 'cross-spawn';\nimport { buildAiLaunchArgs, type AiLaunchOpts, type AiToolSpec } from '../core/ai-launcher.js';\n\n/** Get the platform-appropriate default editor. */\nexport function getEditor(): string {\n return (\n process.env.EDITOR ??\n process.env.VISUAL ??\n (process.platform === 'win32' ? 'notepad' : 'vi')\n );\n}\n\n/** Open a file in the user's editor. */\nexport function openInEditor(filePath: string): void {\n const editor = getEditor();\n spawn.sync(editor, [filePath], { stdio: 'inherit' });\n}\n\n/** Open a URL in the default browser. */\nexport function openUrl(url: string): void {\n const cmd = process.platform === 'win32' ? 'cmd' : process.platform === 'darwin' ? 'open' : 'xdg-open';\n const args = process.platform === 'win32' ? ['/c', 'start', '', url] : [url];\n spawn(cmd, args, { stdio: 'ignore', detached: true }).unref();\n}\n\n/** Open VS Code in the given directory. */\nexport function openVSCode(dir: string): void {\n spawn.sync('code', ['.'], { cwd: dir, stdio: 'inherit' });\n}\n\n/**\n * Launch the configured AI tool in the given directory.\n *\n * When `port` is provided it is injected as `$PORT` into the launched process\n * so dev servers started by parallel agent sessions don't collide.\n */\nexport function launchAi(\n cwd: string,\n tool: AiToolSpec,\n opts: AiLaunchOpts = {},\n port?: number,\n): void {\n const { cmd, args } = buildAiLaunchArgs(tool, opts);\n const spawnOpts: Parameters<typeof spawn.sync>[2] =\n port !== undefined\n ? { cwd, stdio: 'inherit', env: { ...process.env, PORT: String(port) } }\n : { cwd, stdio: 'inherit' };\n spawn.sync(cmd, args, spawnOpts);\n}\n","import fs from 'node:fs';\nimport type { WorkConfig } from './config.js';\n\n/** Resolved AI tool spec ready for spawning. */\nexport interface AiToolSpec {\n /** Executable name. */\n cmd: string;\n /** Arguments always prepended (from any extra tokens in `aiCommand`). */\n baseArgs: string[];\n /** Flag injected when `--unsafe` is used. Empty string disables. */\n unsafeFlag: string;\n /** Flag injected when resuming a prior session. Empty string disables. */\n resumeFlag: string;\n /**\n * Flag used to pass an initial prompt as a file (followed by the file path).\n * Empty string disables — prompt-file content will be inlined via `promptFlag`\n * instead, if available.\n */\n promptFileFlag: string;\n /**\n * Flag used to pass an inline prompt (followed by the prompt string).\n * Empty string means the prompt is appended as a positional argument\n * (Claude's behavior).\n */\n promptFlag: string;\n}\n\nexport interface AiLaunchOpts {\n unsafe?: boolean;\n resume?: boolean;\n promptFile?: string;\n /** Inline prompt (used by CLI `--prompt`). */\n initialPrompt?: string;\n}\n\n/**\n * Per-tool flag presets. Looked up by the binary in `aiCommand`. Users can\n * override any individual flag via `config.aiCommandFlags`.\n */\nexport const AI_TOOL_PRESETS: Record<string, {\n unsafe: string;\n resume: string;\n promptFile: string;\n prompt: string;\n}> = {\n claude: {\n unsafe: '--dangerously-skip-permissions',\n resume: '--continue',\n promptFile: '--prompt-file',\n prompt: '', // positional\n },\n opencode: {\n unsafe: '', // no equivalent — permissions are configured elsewhere\n resume: '--continue',\n promptFile: '', // no file flag — content gets inlined via promptFlag\n prompt: '--prompt',\n },\n};\n\n/** Display names for the init picker. */\nexport const KNOWN_TOOLS = [\n { name: 'Claude Code (claude)', value: 'claude' },\n { name: 'OpenCode (opencode)', value: 'opencode' },\n] as const;\n\n/**\n * Resolve the configured AI tool. Falls back to the `claude` preset so default\n * behavior is preserved when no `aiCommand` is set.\n */\nexport function getAiTool(config: Pick<WorkConfig, 'aiCommand' | 'aiCommandFlags'>): AiToolSpec {\n const command = (config.aiCommand ?? 'claude').trim();\n const parts = command.split(/\\s+/).filter(Boolean);\n const cmd = parts[0] || 'claude';\n const baseArgs = parts.slice(1);\n const preset = AI_TOOL_PRESETS[cmd] ?? AI_TOOL_PRESETS.claude;\n const overrides = config.aiCommandFlags ?? {};\n return {\n cmd,\n baseArgs,\n unsafeFlag: overrides.unsafe ?? preset.unsafe,\n resumeFlag: overrides.resume ?? preset.resume,\n promptFileFlag: overrides.promptFile ?? preset.promptFile,\n promptFlag: overrides.prompt ?? preset.prompt,\n };\n}\n\n/** Build the final `{ cmd, args }` to spawn for the given launch options. */\nexport function buildAiLaunchArgs(tool: AiToolSpec, opts: AiLaunchOpts = {}): { cmd: string; args: string[] } {\n const args = [...tool.baseArgs];\n if (opts.unsafe && tool.unsafeFlag) args.push(tool.unsafeFlag);\n if (opts.resume && tool.resumeFlag) args.push(tool.resumeFlag);\n\n let inlinePrompt = opts.initialPrompt;\n if (opts.promptFile) {\n if (tool.promptFileFlag) {\n args.push(tool.promptFileFlag, opts.promptFile);\n } else if (tool.promptFlag) {\n // Tool has no prompt-file flag — read the file and pass inline instead.\n try { inlinePrompt = fs.readFileSync(opts.promptFile, 'utf-8'); }\n catch { /* leave inlinePrompt as-is */ }\n }\n }\n if (inlinePrompt) {\n if (tool.promptFlag) args.push(tool.promptFlag, inlinePrompt);\n else args.push(inlinePrompt);\n }\n return { cmd: tool.cmd, args };\n}\n","import path from 'node:path';\nimport os from 'node:os';\nimport chalk from 'chalk';\nimport { input, confirm, select } from '@inquirer/prompts';\nimport type { CommandModule } from 'yargs';\nimport {\n loadConfig,\n saveConfig,\n getConfigPath,\n type WorkConfig,\n} from '../core/config.js';\nimport { isGitRepo } from '../core/git.js';\nimport { KNOWN_TOOLS } from '../core/ai-launcher.js';\nimport {\n setupCompletions,\n printCompletionResults,\n printManualInstructions,\n} from '../core/setup-completions.js';\n\nexport const initCommand: CommandModule = {\n command: 'init',\n describe: 'Set up work configuration interactively',\n handler: async () => {\n console.log('');\n console.log(chalk.cyan('Welcome to Work - Git Worktree Manager'));\n console.log(chalk.cyan('======================================'));\n console.log('');\n\n const configPath = getConfigPath();\n let config = loadConfig();\n\n if (config) {\n console.log(\n chalk.yellow(\n `Configuration file already exists at: ${configPath}`,\n ),\n );\n const overwrite = await confirm({\n message:\n 'Do you want to reconfigure? This will keep existing repos.',\n default: false,\n });\n\n if (!overwrite) {\n console.log('Initialization cancelled.');\n return;\n }\n }\n\n if (!config) {\n config = {\n worktreesRoot: '',\n repos: {},\n groups: {},\n copyFiles: [\n '*.Development.json',\n '*.Local.json',\n '.claude/settings.local.json',\n ],\n };\n }\n\n // Configure worktrees root\n console.log(chalk.green('Where should all worktrees be created?'));\n const defaultRoot =\n config.worktreesRoot ||\n path.join(path.dirname(os.homedir()), 'worktrees');\n\n const worktreesInput = await input({\n message: 'Worktrees root directory',\n default: defaultRoot,\n });\n\n config.worktreesRoot = worktreesInput || defaultRoot;\n\n console.log('');\n console.log(\n chalk.green(\n `Great! Worktrees will be created in: ${config.worktreesRoot}`,\n ),\n );\n console.log('');\n\n // AI tool selection\n console.log(chalk.green('Which AI tool should be launched in worktrees?'));\n const currentTool = (config.aiCommand ?? 'claude').trim().split(/\\s+/)[0];\n const knownChoice = KNOWN_TOOLS.find((t) => t.value === currentTool);\n const toolChoice = await select<string>({\n message: 'AI tool',\n choices: [\n ...KNOWN_TOOLS.map((t) => ({ name: t.name, value: t.value })),\n { name: 'Custom (enter command manually)', value: '__custom__' },\n ],\n default: knownChoice ? knownChoice.value : '__custom__',\n });\n\n if (toolChoice === '__custom__') {\n const customCmd = await input({\n message: 'Command to launch (with any base args)',\n default: config.aiCommand ?? 'claude',\n });\n config.aiCommand = customCmd.trim() || 'claude';\n } else {\n config.aiCommand = toolChoice;\n }\n console.log(chalk.green(`AI tool set to: ${config.aiCommand}`));\n console.log('');\n\n // Add repositories\n console.log(chalk.green('Now let\\'s add your repositories.'));\n console.log(\n chalk.gray('(You can add more later with: work config add <alias> <path>)'),\n );\n console.log('');\n\n let addMore = true;\n let repoCount = 1;\n\n while (addMore) {\n console.log(chalk.yellow(`Repository #${repoCount}:`));\n\n const alias = await input({\n message: \" Alias (short name, e.g., 'ai', 'frontend')\",\n });\n\n if (!alias.trim()) {\n console.log(chalk.red('Alias cannot be empty. Skipping.'));\n continue;\n }\n\n const repoPath = await input({\n message: ' Repository path',\n });\n\n if (!repoPath.trim()) {\n console.log(chalk.red('Repository path cannot be empty. Skipping.'));\n continue;\n }\n\n // Validate path is a git repo\n if (!isGitRepo(repoPath)) {\n console.log(\n chalk.red(\n `Path is not a git repository (or does not exist): ${repoPath}`,\n ),\n );\n continue;\n }\n\n config.repos[alias.trim()] = repoPath.trim();\n console.log(\n chalk.green(` Added: ${alias.trim()} -> ${repoPath.trim()}`),\n );\n console.log('');\n\n repoCount++;\n\n addMore = await confirm({\n message: 'Add another repository?',\n default: false,\n });\n }\n\n // Save configuration\n saveConfig(config);\n\n console.log('');\n console.log(chalk.green(`Configuration saved to: ${configPath}`));\n\n // Tab completions\n console.log('');\n console.log(chalk.green('Tab Completions'));\n const installCompletions = await confirm({\n message: 'Set up tab completions?',\n default: true,\n });\n\n if (installCompletions) {\n const results = setupCompletions();\n if (results.length > 0) {\n printCompletionResults(results);\n console.log('');\n console.log(\n chalk.gray(' Restart your shell for completions to take effect.'),\n );\n } else {\n printManualInstructions();\n }\n }\n\n console.log('');\n console.log(\n chalk.cyan('You\\'re all set! Try: work tree <project> <branch>'),\n );\n console.log('');\n },\n};\n","import fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport os from 'node:os';\r\nimport chalk from 'chalk';\r\nimport spawn from 'cross-spawn';\r\n\r\nexport interface ShellProfile {\r\n shell: string;\r\n profilePath: string;\r\n completionLine: string;\r\n}\r\n\r\nexport type InstallResult =\r\n | 'installed'\r\n | 'created-file'\r\n | 'already-exists'\r\n | 'error';\r\n\r\nexport interface CompletionResult {\r\n profile: ShellProfile;\r\n status: InstallResult;\r\n error?: string;\r\n}\r\n\r\nconst PS_COMPLETION_LINE =\r\n 'work completion --shell powershell | Out-String | Invoke-Expression';\r\nconst BASH_COMPLETION_LINE = 'eval \"$(work completion)\"';\r\nconst ZSH_COMPLETION_LINE = 'eval \"$(work completion --shell zsh)\"';\r\nconst FISH_COMPLETION_SCRIPT = `# work tab completions\r\nfunction __work_complete\r\n set -l cmd (commandline -opc)\r\n set -l cur (commandline -ct)\r\n work --get-yargs-completions $cmd $cur 2>/dev/null\r\nend\r\n\r\ncomplete -c work -f -a '(__work_complete)'`;\r\nconst MARKER = '# work tab completions';\r\nconst LEGACY_MARKER = '# work2 tab completions';\r\n\r\n/** Get the Windows Documents folder, handling OneDrive redirection. */\r\nfunction getWindowsDocumentsFolder(): string | null {\r\n // Try pwsh first, fall back to powershell.exe\r\n for (const exe of ['pwsh', 'powershell']) {\r\n const result = spawn.sync(\r\n exe,\r\n ['-NoProfile', '-Command', \"[Environment]::GetFolderPath('MyDocuments')\"],\r\n { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] },\r\n );\r\n if (result.status === 0 && result.stdout) {\r\n const dir = result.stdout.toString().trim();\r\n if (dir) return dir;\r\n }\r\n }\r\n return null;\r\n}\r\n\r\n/** Check if an executable exists on PATH. */\r\nfunction executableExists(name: string): boolean {\r\n const cmd = process.platform === 'win32' ? 'where' : 'which';\r\n const result = spawn.sync(cmd, [name], {\r\n encoding: 'utf-8',\r\n stdio: ['pipe', 'pipe', 'pipe'],\r\n });\r\n return result.status === 0;\r\n}\r\n\r\n/** Detect available shell profiles for completion installation. */\r\nexport function detectShellProfiles(): ShellProfile[] {\r\n const profiles: ShellProfile[] = [];\r\n\r\n if (process.platform === 'win32') {\r\n const docs = getWindowsDocumentsFolder();\r\n if (!docs) return profiles;\r\n\r\n if (executableExists('pwsh')) {\r\n profiles.push({\r\n shell: 'PowerShell 7',\r\n profilePath: path.join(\r\n docs,\r\n 'PowerShell',\r\n 'Microsoft.PowerShell_profile.ps1',\r\n ),\r\n completionLine: PS_COMPLETION_LINE,\r\n });\r\n }\r\n\r\n if (executableExists('powershell')) {\r\n profiles.push({\r\n shell: 'PowerShell 5.1',\r\n profilePath: path.join(\r\n docs,\r\n 'WindowsPowerShell',\r\n 'Microsoft.PowerShell_profile.ps1',\r\n ),\r\n completionLine: PS_COMPLETION_LINE,\r\n });\r\n }\r\n } else {\r\n const shell = process.env.SHELL ?? '';\r\n const home = os.homedir();\r\n\r\n if (shell.includes('fish')) {\r\n // Fish uses a dedicated completions directory\r\n const fishConfigDir = process.env.XDG_CONFIG_HOME\r\n ? path.join(process.env.XDG_CONFIG_HOME, 'fish')\r\n : path.join(home, '.config', 'fish');\r\n profiles.push({\r\n shell: 'Fish',\r\n profilePath: path.join(fishConfigDir, 'completions', 'work.fish'),\r\n completionLine: FISH_COMPLETION_SCRIPT,\r\n });\r\n } else if (shell.includes('zsh')) {\r\n profiles.push({\r\n shell: 'Zsh',\r\n profilePath: path.join(home, '.zshrc'),\r\n completionLine: ZSH_COMPLETION_LINE,\r\n });\r\n } else if (shell.includes('bash')) {\r\n profiles.push({\r\n shell: 'Bash',\r\n profilePath: path.join(home, '.bashrc'),\r\n completionLine: BASH_COMPLETION_LINE,\r\n });\r\n }\r\n }\r\n\r\n return profiles;\r\n}\r\n\r\n/**\r\n * Strip a legacy `work2` completion block from a non-fish profile. Matches the\r\n * marker line and the next line (the install command). Returns content unchanged\r\n * if no legacy block is present.\r\n */\r\nfunction stripLegacyBlock(content: string): string {\r\n if (!content.includes(LEGACY_MARKER)) return content;\r\n const lines = content.split(/\\r?\\n/);\r\n const out: string[] = [];\r\n for (let i = 0; i < lines.length; i++) {\r\n if (lines[i].trim() === LEGACY_MARKER) {\r\n // Skip marker line and the following install line (if present)\r\n if (i + 1 < lines.length && /work2\\b/.test(lines[i + 1])) i++;\r\n continue;\r\n }\r\n out.push(lines[i]);\r\n }\r\n return out.join('\\n').replace(/\\n{3,}/g, '\\n\\n');\r\n}\r\n\r\n/** Idempotently install the completion line into a shell profile. */\r\nexport function installCompletionLine(profile: ShellProfile): CompletionResult {\r\n try {\r\n let createdFile = false;\r\n const isFish = profile.shell === 'Fish';\r\n\r\n let existingContent = '';\r\n if (fs.existsSync(profile.profilePath)) {\r\n existingContent = fs.readFileSync(profile.profilePath, 'utf-8');\r\n if (existingContent.includes(MARKER)) {\r\n return { profile, status: 'already-exists' };\r\n }\r\n } else {\r\n createdFile = true;\r\n }\r\n\r\n // Ensure parent directory exists\r\n fs.mkdirSync(path.dirname(profile.profilePath), { recursive: true });\r\n\r\n if (isFish) {\r\n // Fish: write the complete completion script to its own file\r\n fs.writeFileSync(profile.profilePath, profile.completionLine + '\\n');\r\n } else {\r\n // Bash/Zsh/PowerShell: strip any legacy `work2` block, then append new one.\r\n const cleaned = stripLegacyBlock(existingContent);\r\n const snippet = `\\n${MARKER}\\n${profile.completionLine}\\n`;\r\n fs.writeFileSync(profile.profilePath, cleaned + snippet);\r\n }\r\n\r\n return { profile, status: createdFile ? 'created-file' : 'installed' };\r\n } catch (err) {\r\n return {\r\n profile,\r\n status: 'error',\r\n error: err instanceof Error ? err.message : String(err),\r\n };\r\n }\r\n}\r\n\r\n/** Detect shells and install completions into each profile. */\r\nexport function setupCompletions(): CompletionResult[] {\r\n const profiles = detectShellProfiles();\r\n return profiles.map((p) => installCompletionLine(p));\r\n}\r\n\r\n/** Print colored results for each completion installation. */\r\nexport function printCompletionResults(results: CompletionResult[]): void {\r\n for (const r of results) {\r\n const label = `${r.profile.shell} (${r.profile.profilePath})`;\r\n\r\n switch (r.status) {\r\n case 'installed':\r\n console.log(chalk.green(` ✓ ${label} — completions added`));\r\n break;\r\n case 'created-file':\r\n console.log(\r\n chalk.green(` ✓ ${label} — profile created with completions`),\r\n );\r\n break;\r\n case 'already-exists':\r\n console.log(chalk.gray(` · ${label} — already has completions`));\r\n break;\r\n case 'error':\r\n console.log(chalk.red(` ✗ ${label} — ${r.error}`));\r\n break;\r\n }\r\n }\r\n}\r\n\r\n/** Print manual instructions when no shells are detected. */\r\nexport function printManualInstructions(): void {\r\n console.log(\r\n chalk.yellow(\r\n ' Could not detect shell profiles. Add completions manually:',\r\n ),\r\n );\r\n console.log('');\r\n console.log(chalk.gray(' PowerShell — add to $PROFILE:'));\r\n console.log(` ${PS_COMPLETION_LINE}`);\r\n console.log('');\r\n console.log(chalk.gray(' Bash/Zsh — add to ~/.bashrc or ~/.zshrc:'));\r\n console.log(` ${BASH_COMPLETION_LINE}`);\r\n console.log('');\r\n console.log(chalk.gray(' Fish — run:'));\r\n console.log(` work completion --shell fish > ~/.config/fish/completions/work.fish`);\r\n}\r\n","import fs from 'node:fs';\nimport chalk from 'chalk';\nimport type { CommandModule } from 'yargs';\nimport { ensureConfig } from '../core/config.js';\nimport { resolveProjectTarget, getAllTargetNames, resolveFromCwd } from '../core/resolve.js';\nimport { setupWorktree } from '../core/worktree.js';\nimport { getAiTool } from '../core/ai-launcher.js';\nimport { getCurrentBranch } from '../core/git.js';\nimport { upsertSession } from '../core/history.js';\nimport { openVSCode, launchAi } from '../utils/platform.js';\n\nexport const treeCommand: CommandModule = {\n command: ['tree [target] [branch]', 't [target] [branch]'],\n describe: 'Create or switch to a worktree and launch the configured AI tool',\n builder: (yargs) =>\n yargs\n .showHelpOnFail(true)\n .positional('target', {\n describe: 'Project alias or group name',\n type: 'string',\n })\n .positional('branch', {\n describe: 'Branch name (e.g., feature/login). Omit to work on the base repo.',\n type: 'string',\n })\n .option('here', {\n describe: 'Infer target and branch from the current worktree directory',\n type: 'boolean',\n default: false,\n })\n .option('open', {\n describe: 'Open VS Code in the worktree after creation',\n type: 'boolean',\n default: false,\n })\n .option('unsafe', {\n describe: 'Launch the AI tool with its skip-permissions flag (default: --dangerously-skip-permissions)',\n type: 'boolean',\n default: false,\n })\n .option('base', {\n describe: 'Create the new branch from this base branch instead of HEAD',\n type: 'string',\n })\n .option('prompt', {\n describe: 'Initial prompt to send to the AI tool on startup',\n type: 'string',\n })\n .option('prompt-file', {\n describe: 'File containing the initial prompt (deleted after reading)',\n type: 'string',\n })\n .option('jira-key', {\n describe: 'Link a Jira issue key to this worktree session',\n type: 'string',\n hidden: true,\n })\n .option('setup-only', {\n describe: 'Create worktree without launching the AI tool (used by dashboard)',\n type: 'boolean',\n default: false,\n hidden: true,\n }),\n handler: async (argv) => {\n let targetName = argv.target as string | undefined;\n let branchName = argv.branch as string | undefined;\n const here = argv.here as boolean;\n const open = argv.open as boolean;\n const unsafe = argv.unsafe as boolean;\n const setupOnly = argv['setup-only'] as boolean;\n const baseBranch = argv.base as string | undefined;\n const jiraKey = argv['jira-key'] as string | undefined;\n const promptFile = argv['prompt-file'] as string | undefined;\n let initialPrompt = argv.prompt as string | undefined;\n\n // --prompt-file takes precedence: read and delete the temp file\n if (promptFile) {\n try {\n initialPrompt = fs.readFileSync(promptFile, 'utf-8');\n fs.unlinkSync(promptFile);\n } catch {\n // ignore — fall through to --prompt or no prompt\n }\n }\n\n const config = ensureConfig();\n\n // --here: infer target and branch from the current worktree directory\n if (here) {\n if (targetName) {\n console.error('Cannot combine --here with an explicit target.');\n process.exitCode = 1;\n return;\n }\n const inferred = resolveFromCwd(config, process.cwd());\n if ('error' in inferred) {\n console.error(inferred.error);\n process.exitCode = 1;\n return;\n }\n targetName = inferred.target;\n branchName = inferred.isBaseRepo ? undefined : inferred.branch;\n console.log(\n chalk.cyan(\n `Resolved from current directory: ${targetName}${branchName ? ' @ ' + branchName : ' (base repo)'}`,\n ),\n );\n } else if (!targetName) {\n console.error(\n 'Specify a target, or use --here to infer it from the current directory.',\n );\n process.exitCode = 1;\n return;\n }\n\n // --base requires a branch name\n if (baseBranch && !branchName) {\n console.error('--base requires a branch name');\n console.log(\n chalk.yellow(\n `Usage: work tree ${targetName} <branch> --base ${baseBranch}`,\n ),\n );\n process.exitCode = 1;\n return;\n }\n\n // Resolve project target (for validation and base-repo handling)\n const target = resolveProjectTarget(targetName, config);\n if (!target) {\n const allNames = getAllTargetNames(config);\n console.error(`Project or group not found: ${targetName}`);\n console.log(chalk.yellow(`Available: ${allNames.join(', ')}`));\n console.log(\n chalk.yellow(\n 'Add a new project with: work config add <alias> <path>',\n ),\n );\n process.exitCode = 1;\n return;\n }\n\n // No branch specified — work directly on the base repo\n if (!branchName) {\n if (target.isGroup) {\n console.error('Branch is required for group targets.');\n console.log(chalk.yellow(`Usage: work tree ${targetName} <branch>`));\n process.exitCode = 1;\n return;\n }\n\n const repoPath = config.repos[targetName];\n if (!repoPath) {\n console.error(`Repository path not configured for: ${targetName}`);\n process.exitCode = 1;\n return;\n }\n if (!fs.existsSync(repoPath)) {\n console.error(`Repository path does not exist: ${repoPath}`);\n process.exitCode = 1;\n return;\n }\n\n console.log(chalk.cyan(`Working on base repo: ${targetName}`));\n console.log(`Repo path: ${repoPath}`);\n\n const currentBranch = getCurrentBranch(repoPath) ?? '(detached)';\n await upsertSession(targetName, false, currentBranch, [repoPath], jiraKey);\n\n if (open) openVSCode(repoPath);\n if (!setupOnly) {\n const tool = getAiTool(config);\n console.log(`Starting ${tool.cmd}...`);\n launchAi(repoPath, tool, { unsafe, initialPrompt });\n }\n return;\n }\n\n // Create/switch worktree via shared core logic\n const result = await setupWorktree(targetName, branchName, config, baseBranch, jiraKey);\n if (!result) {\n process.exitCode = 1;\n return;\n }\n\n if (open) {\n for (const p of result.paths) {\n openVSCode(p);\n }\n }\n\n console.log(`Worktree path: ${result.launchDir}`);\n if (!setupOnly) {\n const tool = getAiTool(config);\n console.log(`Starting ${tool.cmd}...`);\n launchAi(result.launchDir, tool, { unsafe, initialPrompt }, result.port);\n }\n },\n};\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { WorkConfig } from './config.js';\nimport {\n getWorktreeRoot,\n getMainRepoRoot,\n getCurrentBranch,\n} from './git.js';\n\nexport interface ProjectTarget {\n isGroup: boolean;\n name: string;\n repoAliases: string[];\n}\n\n/**\n * Resolve whether a name is a group or single repo.\n * Returns null if the name is not found in either.\n */\nexport function resolveProjectTarget(\n name: string,\n config: WorkConfig,\n): ProjectTarget | null {\n // Check if it's a group\n if (name in config.groups) {\n return {\n isGroup: true,\n name,\n repoAliases: [...config.groups[name]],\n };\n }\n\n // Check if it's a repo\n if (name in config.repos) {\n return {\n isGroup: false,\n name,\n repoAliases: [name],\n };\n }\n\n return null;\n}\n\n/** Get all available project/group names. */\nexport function getAllTargetNames(config: WorkConfig): string[] {\n return [\n ...Object.keys(config.repos),\n ...Object.keys(config.groups),\n ];\n}\n\n/**\n * Match a worktree path against the configured worktrees root to determine the\n * target. The path must be strictly under `worktreesRoot`. The first path\n * segment is either a group name (checked first) or a repo folder basename.\n *\n * Both paths are canonicalized via `realpath` before comparison: git's\n * `--show-toplevel` returns a fully symlink-resolved path, while\n * `config.worktreesRoot` may contain symlinked components (e.g. macOS\n * `/tmp` → `/private/tmp`). Without this they would mismatch. `realpath` is\n * injectable for testability and defaults to fs.realpathSync (falling back to\n * path.resolve if it throws). Returns null if no match.\n */\nexport function matchTargetByWorktreePath(\n config: WorkConfig,\n absWorktreePath: string,\n realpath: (p: string) => string = safeRealpath,\n): { target: string; isGroup: boolean } | null {\n const root = realpath(config.worktreesRoot);\n const wt = realpath(absWorktreePath);\n\n const rel = path.relative(root, wt);\n // Must be strictly under root (not the root itself, not outside it).\n if (!rel || rel === '' || rel.startsWith('..') || path.isAbsolute(rel)) {\n return null;\n }\n\n const segments = rel.split(path.sep).filter(Boolean);\n if (segments.length === 0) return null;\n const firstSeg = segments[0];\n\n // Groups checked first.\n if (firstSeg in config.groups) {\n return { target: firstSeg, isGroup: true };\n }\n\n for (const alias of Object.keys(config.repos)) {\n if (path.basename(config.repos[alias]) === firstSeg) {\n return { target: alias, isGroup: false };\n }\n }\n\n return null;\n}\n\n/**\n * Reverse-map a main-repo root path to a single-repo alias by comparing\n * canonicalized paths. `realpath` is injectable for testability and defaults\n * to fs.realpathSync (falling back to path.resolve if it throws).\n * Cannot resolve a group from git alone. Returns null if no match.\n */\nexport function matchTargetByRepoRoot(\n config: WorkConfig,\n repoRoot: string,\n realpath: (p: string) => string = (p) => {\n try {\n return fs.realpathSync(p);\n } catch {\n return path.resolve(p);\n }\n },\n): { target: string; isGroup: false } | null {\n const target = realpath(repoRoot);\n for (const alias of Object.keys(config.repos)) {\n if (realpath(config.repos[alias]) === target) {\n return { target: alias, isGroup: false };\n }\n }\n return null;\n}\n\n/** Canonicalize a path via fs.realpathSync, falling back to path.resolve. */\nfunction safeRealpath(p: string): string {\n try {\n return fs.realpathSync(p);\n } catch {\n return path.resolve(p);\n }\n}\n\n/**\n * Resolve the target, branch, and base-repo status from the current working\n * directory's git worktree. Uses git. Returns an `{ error }` object on failure.\n */\nexport function resolveFromCwd(\n config: WorkConfig,\n cwd: string,\n):\n | { target: string; isGroup: boolean; branch: string; isBaseRepo: boolean }\n | { error: string } {\n const worktreeRoot = getWorktreeRoot(cwd);\n if (!worktreeRoot) {\n return { error: 'Not inside a git repository.' };\n }\n\n const branch = getCurrentBranch(worktreeRoot);\n if (!branch) {\n return {\n error: 'Current worktree is in a detached HEAD state; cannot infer branch.',\n };\n }\n\n let match = matchTargetByWorktreePath(config, worktreeRoot);\n\n if (!match) {\n const mainRoot = getMainRepoRoot(worktreeRoot);\n if (mainRoot) {\n match = matchTargetByRepoRoot(config, mainRoot);\n }\n }\n\n if (!match) {\n const aliases = Object.keys(config.repos).join(', ');\n return {\n error: `Current directory does not belong to any configured work repo or group. Configured repos: ${aliases}`,\n };\n }\n\n let isBaseRepo = false;\n if (!match.isGroup) {\n const repoPath = config.repos[match.target];\n if (repoPath && safeRealpath(repoPath) === safeRealpath(worktreeRoot)) {\n isBaseRepo = true;\n }\n }\n\n return {\n target: match.target,\n isGroup: match.isGroup,\n branch,\n isBaseRepo,\n };\n}\n","import fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport chalk from 'chalk';\r\nimport { debug } from './logger.js';\r\nimport type { WorkConfig } from './config.js';\r\nimport { getConfigDir } from './config.js';\r\nimport { resolveProjectTarget } from './resolve.js';\r\nimport { upsertSessionWithPort } from './history.js';\r\nimport {\r\n git,\r\n parseWorktreeList,\r\n localBranchExists,\r\n remoteBranchExists,\r\n isGitRepo,\r\n getCurrentBranch,\r\n getStatus,\r\n getUnpushedCommits,\r\n} from './git.js';\r\nimport { copyConfigFiles } from './copy-files.js';\r\n\r\n/**\r\n * Create a single git worktree for one repo.\r\n * Returns true on success, false on failure.\r\n *\r\n * When `baseBranch` is provided, the new branch is created from that base\r\n * instead of HEAD. Only valid for new branches — errors if the target branch\r\n * already exists locally or on remote.\r\n */\r\nexport function createSingleWorktree(\r\n repoPath: string,\r\n worktreePath: string,\r\n branchName: string,\r\n config: WorkConfig,\r\n baseBranch?: string,\r\n): boolean {\r\n debug('createSingleWorktree', { repoPath, worktreePath, branchName, baseBranch });\r\n\r\n // Check if the worktree already exists at the target path (idempotent re-run)\r\n if (fs.existsSync(worktreePath)) {\r\n if (isGitRepo(worktreePath)) {\r\n const currentBranch = getCurrentBranch(worktreePath);\r\n if (currentBranch === branchName) {\r\n console.log(\r\n chalk.yellow(` Worktree already exists at: ${worktreePath}`),\r\n );\r\n return true;\r\n }\r\n }\r\n }\r\n\r\n // Check if the branch is already checked out in another worktree\r\n const worktrees = parseWorktreeList(repoPath);\r\n const existingForBranch = worktrees.find(\r\n (wt) => wt.branch === branchName && wt.path !== worktreePath,\r\n );\r\n\r\n if (existingForBranch) {\r\n console.log(\r\n chalk.red(\r\n ` Branch '${branchName}' is already checked out in a worktree at: ${existingForBranch.path}`,\r\n ),\r\n );\r\n console.log(\r\n chalk.red(' Remove that worktree first, or use the existing one.'),\r\n );\r\n return false;\r\n }\r\n\r\n // Create parent directory\r\n const parentDir = path.dirname(worktreePath);\r\n fs.mkdirSync(parentDir, { recursive: true });\r\n\r\n // Fetch remote refs first so origin/* is up to date even if pull fails below\r\n git(['fetch', '--quiet'], repoPath);\r\n\r\n // Pull latest changes for current branch in main repo\r\n const baseRepoBranch = getCurrentBranch(repoPath);\r\n const baseBranchLabel = baseRepoBranch ?? '(detached HEAD)';\r\n console.log(` Pulling latest changes for main repo (on ${baseBranchLabel})...`);\r\n if (baseRepoBranch && !['master', 'main', 'dev'].includes(baseRepoBranch)) {\r\n console.log(\r\n chalk.yellow(\r\n ` ⚠ Warning: base repo is on '${baseRepoBranch}', not master/main/dev`,\r\n ),\r\n );\r\n }\r\n const baseRepoPull = git(['pull', '--quiet'], repoPath);\r\n const baseRepoPullFailed = baseRepoPull.exitCode !== 0;\r\n if (baseRepoPullFailed) {\r\n console.log(\r\n chalk.yellow(\r\n ` ⚠ Could not pull '${baseBranchLabel}' (uncommitted changes, conflicts, or no upstream).`,\r\n ),\r\n );\r\n const firstErrLine = baseRepoPull.stderr.split('\\n')[0];\r\n if (firstErrLine) console.log(chalk.gray(` ${firstErrLine}`));\r\n }\r\n\r\n const hasLocal = localBranchExists(branchName, repoPath);\r\n const hasRemote = remoteBranchExists(branchName, repoPath);\r\n\r\n // --base requires a brand-new branch\r\n if (baseBranch && (hasLocal || hasRemote)) {\r\n console.log(\r\n chalk.red(\r\n ` Cannot use --base: branch '${branchName}' already exists ${hasLocal ? 'locally' : 'on remote'}`,\r\n ),\r\n );\r\n return false;\r\n }\r\n\r\n // Pull latest changes if branch exists locally\r\n if (hasLocal) {\r\n console.log(` Pulling latest changes for ${branchName}...`);\r\n const prevBranch = getCurrentBranch(repoPath);\r\n git(['checkout', branchName, '--quiet'], repoPath);\r\n const branchPull = git(['pull', '--quiet'], repoPath);\r\n if (branchPull.exitCode !== 0) {\r\n console.log(\r\n chalk.yellow(\r\n ` ⚠ Could not pull '${branchName}'. Worktree may be behind origin.`,\r\n ),\r\n );\r\n const firstErrLine = branchPull.stderr.split('\\n')[0];\r\n if (firstErrLine) console.log(chalk.gray(` ${firstErrLine}`));\r\n }\r\n if (prevBranch) {\r\n git(['checkout', prevBranch, '--quiet'], repoPath);\r\n }\r\n }\r\n\r\n // Create worktree\r\n let result;\r\n let branchSource: 'local' | 'remote' | 'new' = 'new';\r\n if (hasLocal || hasRemote) {\r\n if (hasRemote && !hasLocal) {\r\n branchSource = 'remote';\r\n result = git(\r\n [\r\n 'worktree',\r\n 'add',\r\n worktreePath,\r\n '-b',\r\n branchName,\r\n '--track',\r\n `origin/${branchName}`,\r\n ],\r\n repoPath,\r\n );\r\n } else {\r\n branchSource = 'local';\r\n result = git(\r\n ['worktree', 'add', worktreePath, branchName],\r\n repoPath,\r\n );\r\n }\r\n } else if (baseBranch) {\r\n // Validate the base branch exists\r\n const baseLocal = localBranchExists(baseBranch, repoPath);\r\n const baseRemote = remoteBranchExists(baseBranch, repoPath);\r\n\r\n if (!baseLocal && !baseRemote) {\r\n console.log(\r\n chalk.red(\r\n ` Base branch '${baseBranch}' does not exist locally or on remote`,\r\n ),\r\n );\r\n return false;\r\n }\r\n\r\n const baseRef = baseLocal ? baseBranch : `origin/${baseBranch}`;\r\n result = git(\r\n ['worktree', 'add', worktreePath, '-b', branchName, baseRef],\r\n repoPath,\r\n );\r\n } else {\r\n // If pulling the base repo branch failed, use origin/<baseRepoBranch> as the\r\n // source so the new branch isn't created from a stale local HEAD.\r\n const fallbackToRemote =\r\n baseRepoPullFailed &&\r\n !!baseRepoBranch &&\r\n remoteBranchExists(baseRepoBranch, repoPath);\r\n if (fallbackToRemote) {\r\n console.log(\r\n chalk.cyan(` Using origin/${baseRepoBranch} as base (local '${baseRepoBranch}' is stale)`),\r\n );\r\n result = git(\r\n ['worktree', 'add', worktreePath, '-b', branchName, `origin/${baseRepoBranch}`],\r\n repoPath,\r\n );\r\n } else {\r\n result = git(\r\n ['worktree', 'add', worktreePath, '-b', branchName],\r\n repoPath,\r\n );\r\n }\r\n }\r\n\r\n if (result.exitCode !== 0) {\r\n debug('git worktree add failed', { exitCode: result.exitCode, stdout: result.stdout, stderr: result.stderr });\r\n console.log(chalk.red(' Failed to create worktree'));\r\n if (result.stderr) {\r\n console.log(chalk.red(` ${result.stderr}`));\r\n }\r\n return false;\r\n }\r\n\r\n // Copy configuration files from main repo\r\n if (config.copyFiles && config.copyFiles.length > 0) {\r\n copyConfigFiles(repoPath, worktreePath, config.copyFiles);\r\n }\r\n\r\n if (branchSource === 'remote') {\r\n console.log(chalk.cyan(` Tracking remote branch origin/${branchName}`));\r\n } else if (branchSource === 'local') {\r\n console.log(chalk.cyan(` Using existing local branch ${branchName}`));\r\n } else if (baseBranch) {\r\n console.log(chalk.cyan(` Created new branch ${branchName} from ${baseBranch}`));\r\n } else {\r\n console.log(chalk.cyan(` Created new branch ${branchName}`));\r\n }\r\n\r\n console.log(chalk.green(` Created worktree: ${worktreePath}`));\r\n return true;\r\n}\r\n\r\n/**\r\n * Remove a single git worktree.\r\n * Returns true on success, false if blocked (uncommitted/unpushed changes).\r\n */\r\nexport function removeSingleWorktree(\r\n repoPath: string,\r\n worktreePath: string,\r\n branchName: string,\r\n force: boolean,\r\n): boolean {\r\n if (!fs.existsSync(worktreePath)) {\r\n console.log(\r\n chalk.yellow(` Worktree does not exist at: ${worktreePath}`),\r\n );\r\n return true; // Nothing to remove is success\r\n }\r\n\r\n // Check if it's a valid git worktree\r\n if (!isGitRepo(worktreePath)) {\r\n fs.rmSync(worktreePath, { recursive: true, force: true });\r\n git(['worktree', 'prune'], repoPath);\r\n console.log(` Removed invalid worktree directory: ${worktreePath}`);\r\n return true;\r\n }\r\n\r\n if (!force) {\r\n // Check for uncommitted changes\r\n const status = getStatus(worktreePath);\r\n if (status) {\r\n console.log(\r\n chalk.yellow(` Uncommitted changes in: ${worktreePath}`),\r\n );\r\n console.log(status);\r\n return false;\r\n }\r\n\r\n // Check for unpushed commits\r\n const unpushed = getUnpushedCommits(worktreePath);\r\n if (unpushed) {\r\n console.log(\r\n chalk.yellow(` Unpushed commits in: ${worktreePath}`),\r\n );\r\n console.log(unpushed);\r\n return false;\r\n }\r\n }\r\n\r\n const args = force\r\n ? ['worktree', 'remove', worktreePath, '--force']\r\n : ['worktree', 'remove', worktreePath];\r\n\r\n const result = git(args, repoPath);\r\n\r\n if (result.exitCode === 0) {\r\n console.log(chalk.green(` Removed worktree: ${worktreePath}`));\r\n return true;\r\n } else {\r\n console.log(chalk.red(` Failed to remove worktree: ${worktreePath}`));\r\n if (result.stderr) {\r\n console.log(chalk.red(` ${result.stderr}`));\r\n }\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Result of setupWorktree — everything needed to launch an AI session.\r\n */\r\nexport interface WorktreeSetupResult {\r\n /** Directory to launch the AI tool in. */\r\n launchDir: string;\r\n /** All worktree paths created/found (for session tracking). */\r\n paths: string[];\r\n /** Whether the target is a group. */\r\n isGroup: boolean;\r\n /** Stable dev-server port allocated to this worktree, if allocation succeeded. */\r\n port?: number;\r\n}\r\n\r\n/**\r\n * High-level worktree setup: resolve target, create worktree(s), copy group\r\n * CLAUDE.md, and record the session. Used by both the CLI command and the TUI.\r\n *\r\n * Returns the setup result on success, or null on failure.\r\n */\r\nexport async function setupWorktree(\r\n targetName: string,\r\n branchName: string,\r\n config: WorkConfig,\r\n baseBranch?: string,\r\n jiraKey?: string,\r\n): Promise<WorktreeSetupResult | null> {\r\n debug('setupWorktree', { targetName, branchName, baseBranch, jiraKey });\r\n const target = resolveProjectTarget(targetName, config);\r\n if (!target) { debug('setupWorktree: target not found', targetName); return null; }\r\n\r\n const workTreeDirName = branchName.replace(/\\//g, '-');\r\n\r\n if (target.isGroup) {\r\n return setupGroupWorktree(target.name, target.repoAliases, branchName, workTreeDirName, config, baseBranch, jiraKey);\r\n } else {\r\n return setupSingleWorktree(targetName, branchName, workTreeDirName, config, baseBranch, jiraKey);\r\n }\r\n}\r\n\r\nasync function setupGroupWorktree(\r\n groupName: string,\r\n repoAliases: string[],\r\n branchName: string,\r\n workTreeDirName: string,\r\n config: WorkConfig,\r\n baseBranch?: string,\r\n jiraKey?: string,\r\n): Promise<WorktreeSetupResult | null> {\r\n const groupWorktreePath = path.join(config.worktreesRoot, groupName, workTreeDirName);\r\n\r\n // Pre-validate --base across all repos before creating anything\r\n if (baseBranch) {\r\n const missingBase: string[] = [];\r\n const branchExists: string[] = [];\r\n\r\n for (const alias of repoAliases) {\r\n const repoPath = config.repos[alias];\r\n if (!localBranchExists(baseBranch, repoPath) && !remoteBranchExists(baseBranch, repoPath)) {\r\n missingBase.push(alias);\r\n }\r\n if (localBranchExists(branchName, repoPath) || remoteBranchExists(branchName, repoPath)) {\r\n branchExists.push(alias);\r\n }\r\n }\r\n\r\n if (missingBase.length > 0) {\r\n console.error(`Base branch '${baseBranch}' not found in: ${missingBase.join(', ')}`);\r\n return null;\r\n }\r\n if (branchExists.length > 0) {\r\n console.error(`Cannot use --base: branch '${branchName}' already exists in: ${branchExists.join(', ')}`);\r\n return null;\r\n }\r\n }\r\n\r\n console.log(chalk.cyan(`Creating group worktree: ${groupName}/${branchName}`));\r\n console.log(chalk.gray(`Directory: ${groupWorktreePath}`));\r\n console.log('');\r\n\r\n fs.mkdirSync(groupWorktreePath, { recursive: true });\r\n\r\n const createdWorktrees: Array<{ repoPath: string; worktreePath: string }> = [];\r\n\r\n for (const alias of repoAliases) {\r\n const repoPath = config.repos[alias];\r\n const repoName = path.basename(repoPath);\r\n const subWorktreePath = path.join(groupWorktreePath, repoName);\r\n\r\n console.log(chalk.cyan(`[${alias}] (${repoName}):`));\r\n const success = createSingleWorktree(repoPath, subWorktreePath, branchName, config, baseBranch);\r\n\r\n if (success) {\r\n createdWorktrees.push({ repoPath, worktreePath: subWorktreePath });\r\n } else {\r\n // Rollback\r\n console.log('');\r\n console.log(chalk.yellow('Rolling back created worktrees due to failure...'));\r\n for (const wt of createdWorktrees) {\r\n removeSingleWorktree(wt.repoPath, wt.worktreePath, branchName, true);\r\n }\r\n try {\r\n if (fs.readdirSync(groupWorktreePath).length === 0) {\r\n fs.rmSync(groupWorktreePath, { recursive: true, force: true });\r\n }\r\n } catch { /* */ }\r\n console.error('Failed to create group worktree. Changes have been rolled back.');\r\n return null;\r\n }\r\n }\r\n\r\n // Copy group CLAUDE.md\r\n const configDir = getConfigDir();\r\n const claudeMdSrc = path.join(configDir, `${groupName}.claude.md`);\r\n const claudeMdDest = path.join(groupWorktreePath, 'CLAUDE.md');\r\n\r\n if (fs.existsSync(claudeMdSrc)) {\r\n fs.copyFileSync(claudeMdSrc, claudeMdDest);\r\n console.log('');\r\n console.log(chalk.green('Copied group CLAUDE.md to worktree root'));\r\n } else {\r\n console.log('');\r\n console.log(chalk.yellow(`Warning: Group CLAUDE.md not found at ${claudeMdSrc}`));\r\n console.log(chalk.yellow(`Run 'work config regengroup ${groupName}' to generate it.`));\r\n }\r\n\r\n const allPaths = createdWorktrees.map((wt) => wt.worktreePath);\r\n const { port } = await upsertSessionWithPort(\r\n groupName,\r\n true,\r\n branchName,\r\n allPaths,\r\n config,\r\n jiraKey,\r\n baseBranch,\r\n );\r\n\r\n console.log('');\r\n console.log(`Branch: ${branchName}`);\r\n if (port !== undefined) console.log(chalk.gray(`Dev-server port: ${port}`));\r\n\r\n return { launchDir: groupWorktreePath, paths: allPaths, isGroup: true, port };\r\n}\r\n\r\nasync function setupSingleWorktree(\r\n targetName: string,\r\n branchName: string,\r\n workTreeDirName: string,\r\n config: WorkConfig,\r\n baseBranch?: string,\r\n jiraKey?: string,\r\n): Promise<WorktreeSetupResult | null> {\r\n const repoPath = config.repos[targetName];\r\n const repoName = path.basename(repoPath);\r\n let workTreePath = path.join(config.worktreesRoot, repoName, workTreeDirName);\r\n\r\n if (!fs.existsSync(repoPath)) {\r\n console.error(`Repository path does not exist: ${repoPath}`);\r\n return null;\r\n }\r\n\r\n // Check for existing worktree at any path\r\n const worktrees = parseWorktreeList(repoPath);\r\n const existing = worktrees.find(\r\n (wt) =>\r\n wt.branch === branchName &&\r\n path.resolve(wt.path) !== path.resolve(repoPath),\r\n );\r\n\r\n if (existing) {\r\n if (baseBranch) {\r\n console.error(`Cannot use --base: worktree for '${branchName}' already exists at ${existing.path}`);\r\n return null;\r\n }\r\n console.log(`Worktree already exists at: ${existing.path}`);\r\n workTreePath = existing.path;\r\n } else {\r\n const success = createSingleWorktree(repoPath, workTreePath, branchName, config, baseBranch);\r\n if (!success) return null;\r\n }\r\n\r\n const { port } = await upsertSessionWithPort(\r\n targetName,\r\n false,\r\n branchName,\r\n [workTreePath],\r\n config,\r\n jiraKey,\r\n baseBranch,\r\n );\r\n\r\n console.log(`Branch: ${branchName}`);\r\n if (port !== undefined) console.log(chalk.gray(`Dev-server port: ${port}`));\r\n\r\n return { launchDir: workTreePath, paths: [workTreePath], isGroup: false, port };\r\n}\r\n\r\n/**\r\n * Remove all worktrees for a session and clean up.\r\n * Returns true if all worktrees were successfully removed.\r\n * When force=false, stops on uncommitted/unpushed changes.\r\n */\r\nexport function teardownWorktree(\r\n target: string,\r\n isGroup: boolean,\r\n branch: string,\r\n config: WorkConfig,\r\n force: boolean = true,\r\n): boolean {\r\n const workTreeDirName = branch.replace(/\\//g, '-');\r\n\r\n if (isGroup && config.groups[target]) {\r\n const aliases = config.groups[target];\r\n const groupWorktreePath = path.join(config.worktreesRoot, target, workTreeDirName);\r\n let allRemoved = true;\r\n\r\n for (const alias of aliases) {\r\n const repoPath = config.repos[alias];\r\n if (!repoPath) continue;\r\n const repoName = path.basename(repoPath);\r\n const subWorktreePath = path.join(groupWorktreePath, repoName);\r\n console.log(chalk.cyan(`[${alias}] (${repoName}):`));\r\n if (!removeSingleWorktree(repoPath, subWorktreePath, branch, force)) {\r\n allRemoved = false;\r\n }\r\n }\r\n\r\n // Clean up group CLAUDE.md and empty parent dir\r\n const claudeMd = path.join(groupWorktreePath, 'CLAUDE.md');\r\n try { if (fs.existsSync(claudeMd)) fs.unlinkSync(claudeMd); } catch { /* */ }\r\n try {\r\n if (fs.existsSync(groupWorktreePath) && fs.readdirSync(groupWorktreePath).length === 0) {\r\n fs.rmSync(groupWorktreePath, { recursive: true, force: true });\r\n console.log(chalk.green(`Cleaned up group directory: ${groupWorktreePath}`));\r\n }\r\n } catch { /* */ }\r\n\r\n return allRemoved;\r\n } else {\r\n const repoPath = config.repos[target];\r\n if (repoPath) {\r\n const worktrees = parseWorktreeList(repoPath);\r\n const wt = worktrees.find((w) => w.branch === branch);\r\n if (wt) {\r\n return removeSingleWorktree(repoPath, wt.path, branch, force);\r\n }\r\n }\r\n console.log(chalk.yellow(`No worktree found for branch '${branch}' in '${target}'.`));\r\n return false;\r\n }\r\n}\r\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { WorkConfig } from './config.js';\nimport { getConfigDir } from './config.js';\nimport { atomicWriteFile, ensureFile, withFileLock } from './fs-safe.js';\nimport { effectiveLastAccessedAt } from './claude-activity.js';\nimport { allocateFreePort } from './port-allocator.js';\n\nexport interface WorktreeSession {\n target: string;\n isGroup: boolean;\n branch: string;\n paths: string[];\n createdAt: string;\n lastAccessedAt: string;\n jiraKey?: string;\n /** Branch this worktree was forked from. Recorded when known at creation time. */\n baseBranch?: string;\n /** Stable dev-server port allocated to this worktree, exposed as $PORT. */\n port?: number;\n}\n\nexport function getHistoryPath(): string {\n return path.join(getConfigDir(), 'history.json');\n}\n\n/**\n * Back up a corrupt history file so the user can recover manually\n * instead of having it silently overwritten.\n */\nfunction backupCorruptFile(historyPath: string, reason: string): void {\n try {\n const stamp = new Date().toISOString().replace(/[:.]/g, '-');\n const backupPath = `${historyPath}.bad-${stamp}`;\n fs.copyFileSync(historyPath, backupPath);\n console.error(\n `[work] history.json was unreadable (${reason}). Corrupt copy saved to ${backupPath}`,\n );\n } catch {\n // best-effort\n }\n}\n\nexport function loadHistory(): WorktreeSession[] {\n const historyPath = getHistoryPath();\n if (!fs.existsSync(historyPath)) {\n return [];\n }\n\n let raw: string;\n try {\n raw = fs.readFileSync(historyPath, 'utf-8');\n } catch (err) {\n backupCorruptFile(historyPath, `read error: ${(err as Error).message}`);\n return [];\n }\n\n try {\n const parsed = JSON.parse(raw);\n if (!Array.isArray(parsed)) {\n backupCorruptFile(historyPath, 'not an array');\n return [];\n }\n return parsed.filter(\n (s) =>\n s &&\n typeof s.target === 'string' &&\n typeof s.branch === 'string' &&\n Array.isArray(s.paths),\n );\n } catch (err) {\n backupCorruptFile(historyPath, `parse error: ${(err as Error).message}`);\n return [];\n }\n}\n\nexport function saveHistory(sessions: WorktreeSession[]): void {\n atomicWriteFile(getHistoryPath(), JSON.stringify(sessions, null, 2));\n}\n\n/**\n * Serialize a read-modify-write sequence against other work processes.\n * Concurrent `work tree`/`remove` calls would otherwise clobber each\n * other's writes (this is the bug that wiped 60+ sessions).\n */\nasync function withHistoryLock<T>(fn: () => T | Promise<T>): Promise<T> {\n const historyPath = getHistoryPath();\n ensureFile(historyPath, '[]');\n return withFileLock(historyPath, fn);\n}\n\nfunction sessionKey(target: string, branch: string): string {\n return `${target}:${branch}`;\n}\n\nexport function findSession(\n sessions: WorktreeSession[],\n target: string,\n branch: string,\n): WorktreeSession | undefined {\n return sessions.find(\n (s) => s.target === target && s.branch === branch,\n );\n}\n\nexport async function upsertSession(\n target: string,\n isGroup: boolean,\n branch: string,\n paths: string[],\n jiraKey?: string,\n baseBranch?: string,\n port?: number,\n): Promise<void> {\n await withHistoryLock(() => {\n const sessions = loadHistory();\n const existing = findSession(sessions, target, branch);\n const now = new Date().toISOString();\n\n if (existing) {\n existing.paths = paths;\n existing.lastAccessedAt = now;\n if (jiraKey) existing.jiraKey = jiraKey;\n if (baseBranch && !existing.baseBranch) existing.baseBranch = baseBranch;\n if (port !== undefined) existing.port = port;\n } else {\n const session: WorktreeSession = {\n target,\n isGroup,\n branch,\n paths,\n createdAt: now,\n lastAccessedAt: now,\n };\n if (jiraKey) session.jiraKey = jiraKey;\n if (baseBranch) session.baseBranch = baseBranch;\n if (port !== undefined) session.port = port;\n sessions.push(session);\n }\n\n saveHistory(sessions);\n });\n}\n\n/**\n * Atomically allocate a stable dev-server port AND persist the session in a\n * single locked critical section. Allocating over `loadHistory()` and then\n * writing in a separate `upsertSession` call is a TOCTOU race: two concurrent\n * `work tree` runs would both read the same history snapshot and pick the same\n * port. Doing load + allocate + save under one `withHistoryLock` serializes\n * concurrent allocations so each gets a distinct port.\n *\n * Port allocation is best-effort: if it fails (range exhausted, etc.) the\n * session is still persisted, just without a port, and `port` comes back\n * undefined. An already-allocated port on an existing session is preserved\n * (idempotent re-runs keep their port).\n *\n * The allocation seed is `target:branch` (unique per worktree) so two repos\n * that share a branch name don't collide on the same deterministic base offset.\n */\nexport async function upsertSessionWithPort(\n target: string,\n isGroup: boolean,\n branch: string,\n paths: string[],\n config: Pick<WorkConfig, 'portRange'>,\n jiraKey?: string,\n baseBranch?: string,\n): Promise<{ port?: number }> {\n return withHistoryLock(async () => {\n const sessions = loadHistory();\n const existing = findSession(sessions, target, branch);\n const now = new Date().toISOString();\n\n // Keep an already-assigned port; otherwise allocate against the current\n // (locked) snapshot so concurrent callers can't pick the same one.\n let port = existing?.port;\n if (port === undefined) {\n const seedKey = sessionKey(target, branch);\n try {\n port = await allocateFreePort(seedKey, config, sessions);\n } catch {\n port = undefined;\n }\n }\n\n if (existing) {\n existing.paths = paths;\n existing.lastAccessedAt = now;\n if (jiraKey) existing.jiraKey = jiraKey;\n if (baseBranch && !existing.baseBranch) existing.baseBranch = baseBranch;\n if (port !== undefined) existing.port = port;\n } else {\n const session: WorktreeSession = {\n target,\n isGroup,\n branch,\n paths,\n createdAt: now,\n lastAccessedAt: now,\n };\n if (jiraKey) session.jiraKey = jiraKey;\n if (baseBranch) session.baseBranch = baseBranch;\n if (port !== undefined) session.port = port;\n sessions.push(session);\n }\n\n saveHistory(sessions);\n return { port };\n });\n}\n\nexport async function removeSession(target: string, branch: string): Promise<void> {\n await withHistoryLock(() => {\n const sessions = loadHistory();\n const filtered = sessions.filter(\n (s) => !(s.target === target && s.branch === branch),\n );\n\n if (filtered.length !== sessions.length) {\n saveHistory(filtered);\n }\n });\n}\n\nexport function getSessionsForTarget(\n sessions: WorktreeSession[],\n target: string,\n): WorktreeSession[] {\n return sessions.filter((s) => s.target === target);\n}\n\nexport function getRecentSessions(\n sessions: WorktreeSession[],\n count: number,\n): WorktreeSession[] {\n return [...sessions]\n .sort(\n (a, b) =>\n new Date(effectiveLastAccessedAt(b)).getTime() -\n new Date(effectiveLastAccessedAt(a)).getTime(),\n )\n .slice(0, count);\n}\n\nexport function pruneStaleEntries(sessions: WorktreeSession[]): {\n kept: WorktreeSession[];\n pruned: number;\n} {\n const kept: WorktreeSession[] = [];\n let pruned = 0;\n\n for (const session of sessions) {\n const anyPathExists = session.paths.some((p) => fs.existsSync(p));\n if (anyPathExists) {\n kept.push(session);\n } else {\n pruned++;\n }\n }\n\n return { kept, pruned };\n}\n\n/** Locked variant of prune for `status --prune` callers. */\nexport async function prunePersistedStaleEntries(): Promise<{ pruned: number }> {\n return withHistoryLock(() => {\n const sessions = loadHistory();\n const { kept, pruned } = pruneStaleEntries(sessions);\n if (pruned > 0) saveHistory(kept);\n return { pruned };\n });\n}\n\n/**\n * Merge hydrated sessions into history without clobbering existing entries.\n * For each incoming session: if a matching target+branch exists, refresh its\n * paths (if different) but keep original timestamps. Otherwise insert.\n */\nexport async function mergeHydratedSessions(\n incoming: WorktreeSession[],\n): Promise<{ added: number; updated: number }> {\n return withHistoryLock(() => {\n const sessions = loadHistory();\n let added = 0;\n let updated = 0;\n\n for (const inc of incoming) {\n const existing = findSession(sessions, inc.target, inc.branch);\n if (existing) {\n const sortedA = [...existing.paths].sort();\n const sortedB = [...inc.paths].sort();\n const same =\n sortedA.length === sortedB.length &&\n sortedA.every((p, i) => p === sortedB[i]);\n if (!same) {\n existing.paths = inc.paths;\n updated++;\n }\n } else {\n sessions.push(inc);\n added++;\n }\n }\n\n if (added > 0 || updated > 0) {\n saveHistory(sessions);\n }\n return { added, updated };\n });\n}\n","import fs from 'node:fs';\nimport lockfile from 'proper-lockfile';\n\n/**\n * Write a file atomically. Writes to a sibling tmp file and renames\n * over the target, so a crash mid-write can't leave a truncated file.\n */\nexport function atomicWriteFile(filePath: string, content: string): void {\n const tmpPath = `${filePath}.tmp-${process.pid}`;\n fs.writeFileSync(tmpPath, content, 'utf-8');\n fs.renameSync(tmpPath, filePath);\n}\n\n/**\n * Ensure a file exists (creating with the given initial content if missing).\n * Required before `withFileLock` can acquire a lock on it — proper-lockfile\n * resolves the target file's realpath before creating the sibling .lock dir.\n */\nexport function ensureFile(filePath: string, initialContent: string): void {\n if (!fs.existsSync(filePath)) {\n fs.writeFileSync(filePath, initialContent, 'utf-8');\n }\n}\n\n/**\n * Serialize a read-modify-write sequence across processes using the same\n * advisory-lock mechanism as npm/yarn/pnpm. Caller is responsible for\n * ensuring the target file exists first (see `ensureFile`).\n */\nexport async function withFileLock<T>(\n filePath: string,\n fn: () => T | Promise<T>,\n): Promise<T> {\n const release = await lockfile.lock(filePath, {\n retries: { retries: 20, minTimeout: 25, maxTimeout: 500, factor: 2 },\n stale: 10_000,\n });\n try {\n return await fn();\n } finally {\n await release();\n }\n}\n\n/**\n * Synchronous sibling of {@link withFileLock}. Serializes a read-modify-write\n * across processes using the same advisory-lock mechanism, but without an\n * `await` so it can be called from synchronous code paths (e.g. the\n * file-backed comment store, whose API is sync and consumed by sync Hono\n * route handlers). Caller is responsible for ensuring the target file exists\n * first (see `ensureFile`).\n */\nexport function withFileLockSync<T>(filePath: string, fn: () => T): T {\n // proper-lockfile's sync API forbids its own retry config (it requires an\n // async flow), so we hand-roll a bounded retry: try to acquire, and on a\n // contended lock (ELOCKED) sleep synchronously and try again.\n const maxAttempts = 30;\n let release: (() => void) | undefined;\n for (let attempt = 0; ; attempt++) {\n try {\n release = lockfile.lockSync(filePath, { stale: 10_000 });\n break;\n } catch (err) {\n const code = (err as { code?: string }).code;\n if (code !== 'ELOCKED' || attempt >= maxAttempts) throw err;\n sleepSync(Math.min(25 * 2 ** Math.min(attempt, 4), 500));\n }\n }\n try {\n return fn();\n } finally {\n release();\n }\n}\n\n/** Block the current thread for `ms` milliseconds without spinning the CPU.\n * Used only by `withFileLockSync`'s contention backoff. */\nfunction sleepSync(ms: number): void {\n const shared = new Int32Array(new SharedArrayBuffer(4));\n Atomics.wait(shared, 0, 0, ms);\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport type { WorktreeSession } from './history.js';\n\n/**\n * Claude Code writes each session as JSONL under\n * `~/.claude/projects/<encoded-cwd>/`, where the encoded path replaces every\n * non-alphanumeric character with `-`. These files get rewritten on every\n * message, so their mtimes reflect actual conversation activity — which is\n * what the user cares about, not when `work tree` was last invoked.\n */\nfunction encodeProjectDir(p: string): string {\n return path.resolve(p).replace(/[^A-Za-z0-9]/g, '-');\n}\n\nfunction latestJsonlMtimeMs(projectDir: string): number {\n let entries: string[];\n try {\n entries = fs.readdirSync(projectDir);\n } catch {\n return 0;\n }\n let latest = 0;\n for (const name of entries) {\n if (!name.endsWith('.jsonl')) continue;\n try {\n const stat = fs.statSync(path.join(projectDir, name));\n if (stat.mtimeMs > latest) latest = stat.mtimeMs;\n } catch {\n /* ignore unreadable entries */\n }\n }\n return latest;\n}\n\nexport function getClaudeActivityMs(launchPath: string): number {\n const dir = path.join(\n os.homedir(),\n '.claude',\n 'projects',\n encodeProjectDir(launchPath),\n );\n return latestJsonlMtimeMs(dir);\n}\n\nfunction getLaunchPaths(session: WorktreeSession): string[] {\n if (!session.isGroup) return [...session.paths];\n // Groups launch Claude in the parent (group root), not a repo subfolder.\n const parents = new Set<string>();\n for (const p of session.paths) parents.add(path.dirname(p));\n return [...parents];\n}\n\n/**\n * Returns the most recent of the session's persisted `lastAccessedAt` and\n * the mtime of Claude's session logs for this worktree's launch path(s).\n */\nexport function effectiveLastAccessedAt(session: WorktreeSession): string {\n let bestMs = new Date(session.lastAccessedAt).getTime();\n if (!Number.isFinite(bestMs)) bestMs = 0;\n for (const p of getLaunchPaths(session)) {\n const ms = getClaudeActivityMs(p);\n if (ms > bestMs) bestMs = ms;\n }\n return new Date(bestMs).toISOString();\n}\n\nexport type ActivityState = 'active' | 'open' | 'stale';\n\n/** Active: Claude wrote a turn in the last 30 s — currently thinking. */\nconst ACTIVE_MS = 30_000;\n/** Open: Claude touched the transcript within 5 min — terminal still attached,\n * just idle. After this we consider the session stale. */\nconst OPEN_MS = 5 * 60_000;\n\nexport interface SessionActivity {\n /** ms since epoch of the most recent Claude write across this session's\n * launch path(s), or null if no transcript exists. */\n lastActivity: number | null;\n state: ActivityState;\n}\n\n/**\n * Derive an activity state for a session by looking at Claude's transcript\n * mtime. Works whether the user launched Claude via `work tree`, `work dash`,\n * a manual terminal, or `work web`'s own PTY — they all write to the same\n * `~/.claude/projects/<encoded-cwd>/` directory.\n *\n * If Claude's on-disk layout ever changes, the function falls back to\n * \"stale\" gracefully and the dashboard's other status signals (e.g. our own\n * PTY pool) keep working.\n */\nexport function readSessionActivity(session: WorktreeSession): SessionActivity {\n let latest = 0;\n for (const p of getLaunchPaths(session)) {\n const ms = getClaudeActivityMs(p);\n if (ms > latest) latest = ms;\n }\n if (latest === 0) return { lastActivity: null, state: 'stale' };\n const age = Date.now() - latest;\n const state: ActivityState =\n age <= ACTIVE_MS ? 'active' : age <= OPEN_MS ? 'open' : 'stale';\n return { lastActivity: latest, state };\n}\n\n/** Watch root: `~/.claude/projects/`. The web server subscribes to mtime\n * changes here and re-broadcasts `sessions-changed` so the sidebar\n * badges refresh without polling. */\nexport function claudeProjectsRoot(): string {\n return path.join(os.homedir(), '.claude', 'projects');\n}\n","import fs from 'node:fs';\nimport net from 'node:net';\nimport type { WorkConfig } from './config.js';\nimport type { WorktreeSession } from './history.js';\n\n/** Default dev-server port range when none is configured. */\nexport const DEFAULT_PORT_RANGE = { start: 3000, end: 3099 };\n\n/**\n * Thrown when every port in the configured range is occupied. Distinct class so\n * callers can tell \"range exhausted\" apart from any other allocation failure.\n */\nexport class PortRangeExhaustedError extends Error {\n readonly start: number;\n readonly end: number;\n constructor(start: number, end: number) {\n super(\n `No free dev-server port available in range ${start}-${end} ` +\n `(all ${end - start + 1} in use). Widen \"portRange\" in ~/.work/config.json.`,\n );\n this.name = 'PortRangeExhaustedError';\n this.start = start;\n this.end = end;\n }\n}\n\n/**\n * Deterministic, stable string hash (FNV-1a 32-bit). Same input always yields\n * the same non-negative integer, so a worktree's seed key maps to the same base\n * offset across processes and machines.\n */\nfunction hashString(input: string): number {\n let hash = 0x811c9dc5;\n for (let i = 0; i < input.length; i++) {\n hash ^= input.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193);\n }\n return hash >>> 0;\n}\n\nfunction normalizeRange(config: Pick<WorkConfig, 'portRange'>): {\n start: number;\n end: number;\n rangeSize: number;\n} {\n const range = config.portRange ?? DEFAULT_PORT_RANGE;\n const start = Math.min(range.start, range.end);\n const end = Math.max(range.start, range.end);\n const rangeSize = end - start + 1;\n if (rangeSize <= 0) {\n throw new Error(`Invalid port range: ${range.start}-${range.end}`);\n }\n return { start, end, rangeSize };\n}\n\nfunction portsHeldByActiveSessions(sessions: WorktreeSession[]): Set<number> {\n const inUse = new Set<number>();\n for (const session of sessions) {\n if (session.port === undefined) continue;\n const active = session.paths.some((p) => fs.existsSync(p));\n if (active) inUse.add(session.port);\n }\n return inUse;\n}\n\n/**\n * Allocate a stable dev-server port for a worktree.\n *\n * Pure and testable: the caller passes the current `sessions` array (e.g.\n * `loadHistory()`) rather than this function reading state itself.\n *\n * - The range comes from `config.portRange` (falling back to 3000–3099).\n * - A deterministic base offset is derived from `hash(seedKey) % rangeSize`,\n * so the same worktree prefers the same port. The seed key MUST be unique per\n * worktree (e.g. `target:branch` or the full worktree path) — using only a\n * branch name would let two repos sharing a branch collide deterministically.\n * - Ports already held by *active* sessions (at least one path still exists)\n * are considered in use and skipped.\n * - Walks forward from the base offset (wrapping around the range) to the first\n * free port. Throws `PortRangeExhaustedError` if every port is occupied.\n *\n * NOTE: this checks only our own history, not host liveness. Use\n * `allocateFreePort` when you also want to skip ports occupied by unrelated\n * processes on the machine.\n */\nexport function allocatePort(\n seedKey: string,\n config: Pick<WorkConfig, 'portRange'>,\n sessions: WorktreeSession[],\n): number {\n const { start, end, rangeSize } = normalizeRange(config);\n const inUse = portsHeldByActiveSessions(sessions);\n const baseOffset = hashString(seedKey) % rangeSize;\n\n for (let i = 0; i < rangeSize; i++) {\n const candidate = start + ((baseOffset + i) % rangeSize);\n if (!inUse.has(candidate)) {\n return candidate;\n }\n }\n\n throw new PortRangeExhaustedError(start, end);\n}\n\n/**\n * Probe whether a TCP port is free to bind on localhost. Resolves false on\n * EADDRINUSE (something is already listening), true otherwise. Best-effort: any\n * non-EADDRINUSE error resolves true so a quirky host can't block allocation.\n */\nexport function isPortFree(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const server = net.createServer();\n server.once('error', (err: NodeJS.ErrnoException) => {\n resolve(err.code !== 'EADDRINUSE');\n });\n server.once('listening', () => {\n server.close(() => resolve(true));\n });\n server.listen(port, '127.0.0.1');\n });\n}\n\n/**\n * Like `allocatePort`, but additionally probes host liveness: candidates that\n * are already bound by some other process on localhost (EADDRINUSE) are skipped\n * even when our history doesn't know about them. This is what actually prevents\n * collisions with non-work processes.\n *\n * Walks the same deterministic order as `allocatePort` and returns the first\n * candidate that is both free in history and bindable on the host. Throws\n * `PortRangeExhaustedError` if every candidate is taken.\n */\nexport async function allocateFreePort(\n seedKey: string,\n config: Pick<WorkConfig, 'portRange'>,\n sessions: WorktreeSession[],\n probe: (port: number) => Promise<boolean> = isPortFree,\n): Promise<number> {\n const { start, end, rangeSize } = normalizeRange(config);\n const inUse = portsHeldByActiveSessions(sessions);\n const baseOffset = hashString(seedKey) % rangeSize;\n\n for (let i = 0; i < rangeSize; i++) {\n const candidate = start + ((baseOffset + i) % rangeSize);\n if (inUse.has(candidate)) continue;\n if (await probe(candidate)) {\n return candidate;\n }\n }\n\n throw new PortRangeExhaustedError(start, end);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { globSync } from 'glob';\nimport chalk from 'chalk';\n\n/** Directories to exclude when copying files. */\nconst EXCLUDED_DIRS = ['bin', 'obj', 'node_modules', '.git'];\n\n/**\n * Copy files matching configured patterns from a source repo into a worktree.\n * Handles .claude directory specially (direct path match).\n * Other patterns use recursive glob with exclusion filtering.\n */\nexport function copyConfigFiles(\n repoPath: string,\n worktreePath: string,\n patterns: string[],\n): void {\n for (const pattern of patterns) {\n // Normalize pattern separators to forward slashes for glob\n const normalized = pattern.replace(/\\\\/g, '/');\n\n // Handle .claude directory specially (direct path match)\n if (normalized.startsWith('.claude/')) {\n const relativePath = normalized;\n const sourcePath = path.join(repoPath, relativePath);\n\n if (fs.existsSync(sourcePath)) {\n const destPath = path.join(worktreePath, relativePath);\n const destDir = path.dirname(destPath);\n fs.mkdirSync(destDir, { recursive: true });\n fs.copyFileSync(sourcePath, destPath);\n console.log(` Copied: ${relativePath}`);\n }\n continue;\n }\n\n // Use glob for pattern matching (recursive)\n const matches = globSync(`**/${normalized}`, {\n cwd: repoPath,\n nodir: true,\n posix: true,\n dot: true,\n });\n\n for (const match of matches) {\n // Check exclusion directories\n const parts = match.split('/');\n if (parts.some((p) => EXCLUDED_DIRS.includes(p))) {\n continue;\n }\n\n const sourcePath = path.join(repoPath, match);\n const destPath = path.join(worktreePath, match);\n const destDir = path.dirname(destPath);\n\n fs.mkdirSync(destDir, { recursive: true });\n fs.copyFileSync(sourcePath, destPath);\n console.log(` Copied: ${match}`);\n }\n }\n}\n","import chalk from 'chalk';\r\nimport type { CommandModule } from 'yargs';\r\nimport { ensureConfig } from '../core/config.js';\r\nimport { resolveProjectTarget, getAllTargetNames } from '../core/resolve.js';\r\nimport { teardownWorktree } from '../core/worktree.js';\r\nimport { removeSession } from '../core/history.js';\r\n\r\nexport const removeCommand: CommandModule = {\r\n command: 'remove <target> <branch>',\r\n describe: 'Remove a worktree',\r\n builder: (yargs) =>\r\n yargs\r\n .showHelpOnFail(true)\r\n .positional('target', {\r\n describe: 'Project alias or group name',\r\n type: 'string',\r\n demandOption: true,\r\n })\r\n .positional('branch', {\r\n describe: 'Branch name (e.g., feature/login)',\r\n type: 'string',\r\n demandOption: true,\r\n })\r\n .option('force', {\r\n describe: 'Force remove even with uncommitted/unpushed changes',\r\n type: 'boolean',\r\n default: false,\r\n }),\r\n handler: async (argv) => {\r\n const targetName = argv.target as string;\r\n const branchName = argv.branch as string;\r\n const force = argv.force as boolean;\r\n\r\n const config = ensureConfig();\r\n\r\n const target = resolveProjectTarget(targetName, config);\r\n if (!target) {\r\n const allNames = getAllTargetNames(config);\r\n console.error(`Project or group not found: ${targetName}`);\r\n console.log(chalk.yellow(`Available: ${allNames.join(', ')}`));\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n console.log(chalk.cyan(`Removing worktree: ${targetName}/${branchName}`));\r\n console.log('');\r\n\r\n const allRemoved = teardownWorktree(targetName, target.isGroup, branchName, config, force);\r\n\r\n if (allRemoved === true) {\r\n await removeSession(targetName, branchName);\r\n } else {\r\n process.exitCode = 1;\r\n console.log('');\r\n console.log(\r\n chalk.yellow(\r\n 'Some worktrees could not be removed due to uncommitted/unpushed changes.',\r\n ),\r\n );\r\n console.log(\r\n chalk.yellow(\r\n `Use 'work remove ${targetName} ${branchName} --force' to force remove.`,\r\n ),\r\n );\r\n }\r\n },\r\n};\r\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport type { CommandModule } from 'yargs';\nimport { ensureConfig } from '../core/config.js';\nimport { parseWorktreeList, getCurrentBranch } from '../core/git.js';\nimport { resolveProjectTarget, getAllTargetNames } from '../core/resolve.js';\n\nexport const listCommand: CommandModule = {\n command: 'list [target]',\n describe: 'List worktrees',\n builder: (yargs) =>\n yargs.positional('target', {\n describe: 'Project alias or group name (omit to list all)',\n type: 'string',\n }),\n handler: (argv) => {\n const targetName = argv.target as string | undefined;\n\n const config = ensureConfig();\n const worktreesRoot = config.worktreesRoot;\n\n console.log('');\n console.log(chalk.cyan('Worktrees'));\n console.log(chalk.cyan('========='));\n console.log('');\n\n let showRepos: string[] = [];\n let showGroups: string[] = [];\n\n if (targetName) {\n const target = resolveProjectTarget(targetName, config);\n if (!target) {\n const allNames = getAllTargetNames(config);\n console.error(`Project or group not found: ${targetName}`);\n console.log(chalk.yellow(`Available: ${allNames.join(', ')}`));\n process.exitCode = 1;\n return;\n }\n if (target.isGroup) {\n showGroups = [targetName];\n } else {\n showRepos = [targetName];\n }\n } else {\n showRepos = Object.keys(config.repos);\n showGroups = Object.keys(config.groups);\n }\n\n let foundAny = false;\n\n // Show per-repo worktrees\n for (const proj of showRepos) {\n const repoPath = config.repos[proj];\n\n if (!fs.existsSync(repoPath)) {\n console.log(\n chalk.red(\n `${proj} -> Repository path not found: ${repoPath}`,\n ),\n );\n continue;\n }\n\n const worktreeList = parseWorktreeList(repoPath).filter(\n (wt) => wt.path !== repoPath,\n );\n\n if (worktreeList.length > 0) {\n foundAny = true;\n const plural = worktreeList.length !== 1 ? 's' : '';\n console.log(\n chalk.green(\n `${proj} (${worktreeList.length} worktree${plural}):`,\n ),\n );\n\n for (const wt of worktreeList) {\n const branchDisplay = wt.branch || `detached at ${wt.head}`;\n console.log(` ${branchDisplay}`);\n console.log(chalk.gray(` ${wt.path}`));\n }\n console.log('');\n }\n }\n\n // Show group worktrees\n for (const groupName of showGroups) {\n const groupDir = path.join(worktreesRoot, groupName);\n if (!fs.existsSync(groupDir)) continue;\n\n let branchDirs: string[];\n try {\n branchDirs = fs\n .readdirSync(groupDir, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name);\n } catch {\n continue;\n }\n\n if (branchDirs.length === 0) continue;\n\n foundAny = true;\n const plural = branchDirs.length !== 1 ? 's' : '';\n console.log(\n chalk.magenta(\n `${groupName} [group] (${branchDirs.length} worktree${plural}):`,\n ),\n );\n\n const repoAliases = config.groups[groupName] ?? [];\n\n for (const bdName of branchDirs) {\n const bdPath = path.join(groupDir, bdName);\n\n // Determine the actual branch name from the first sub-worktree\n let actualBranch: string | null = null;\n for (const alias of repoAliases) {\n const repoPath = config.repos[alias];\n if (!repoPath) continue;\n const rdPath = path.join(bdPath, path.basename(repoPath));\n if (fs.existsSync(rdPath)) {\n const branch = getCurrentBranch(rdPath);\n if (branch) {\n actualBranch = branch;\n break;\n }\n }\n }\n\n const displayBranch = actualBranch || bdName;\n console.log(` ${displayBranch}`);\n console.log(chalk.gray(` ${bdPath}`));\n if (repoAliases.length > 0) {\n console.log(\n chalk.gray(` Repos: ${repoAliases.join(', ')}`),\n );\n }\n }\n console.log('');\n }\n\n if (!foundAny) {\n if (targetName) {\n console.log(\n chalk.yellow(`No worktrees found for: ${targetName}`),\n );\n } else {\n console.log(\n chalk.yellow(\n 'No worktrees found for any project or group',\n ),\n );\n }\n console.log('');\n }\n },\n};\n","import fs from 'node:fs';\r\nimport chalk from 'chalk';\r\nimport type { CommandModule } from 'yargs';\r\nimport { ensureConfig } from '../core/config.js';\r\nimport {\r\n loadHistory,\r\n getSessionsForTarget,\r\n prunePersistedStaleEntries,\r\n type WorktreeSession,\r\n} from '../core/history.js';\r\nimport { effectiveLastAccessedAt } from '../core/claude-activity.js';\r\nimport {\r\n getStatus,\r\n getUnpushedCommits,\r\n isBranchMerged,\r\n getCurrentBranch,\r\n} from '../core/git.js';\r\nimport { timeAgo, shortDateTime } from '../utils/format.js';\r\n\r\nexport const statusCommand: CommandModule = {\r\n command: 'status [target] [branch]',\r\n describe: 'Show status of tracked worktrees',\r\n builder: (yargs) =>\r\n yargs\r\n .positional('target', {\r\n describe: 'Filter by project alias or group name',\r\n type: 'string',\r\n })\r\n .positional('branch', {\r\n describe: 'Filter by branch name',\r\n type: 'string',\r\n })\r\n .option('prune', {\r\n describe: 'Remove entries whose worktree paths no longer exist',\r\n type: 'boolean',\r\n default: false,\r\n }),\r\n handler: async (argv) => {\r\n const target = argv.target as string | undefined;\r\n const branch = argv.branch as string | undefined;\r\n const prune = argv.prune as boolean;\r\n\r\n ensureConfig();\r\n\r\n if (prune) {\r\n const { pruned } = await prunePersistedStaleEntries();\r\n if (pruned > 0) {\r\n console.log(chalk.green(`Pruned ${pruned} stale session(s).`));\r\n } else {\r\n console.log('No stale sessions to prune.');\r\n }\r\n }\r\n\r\n let sessions = loadHistory();\r\n\r\n if (target) {\r\n sessions = getSessionsForTarget(sessions, target);\r\n }\r\n if (branch) {\r\n sessions = sessions.filter((s) => s.branch === branch);\r\n }\r\n\r\n if (sessions.length === 0) {\r\n console.log(chalk.yellow('No tracked worktree sessions found.'));\r\n return;\r\n }\r\n\r\n console.log('');\r\n console.log(chalk.cyan('Worktree Status'));\r\n console.log(chalk.cyan('==============='));\r\n console.log('');\r\n\r\n for (const session of sessions) {\r\n printSessionStatus(session);\r\n }\r\n },\r\n};\r\n\r\nfunction printSessionStatus(session: WorktreeSession): void {\r\n const typeLabel = session.isGroup\r\n ? chalk.magenta('[group]')\r\n : chalk.blue('[repo]');\r\n console.log(\r\n `${typeLabel} ${chalk.green(session.target)} ${chalk.white(session.branch)}`,\r\n );\r\n\r\n // Check if paths exist\r\n const existingPaths = session.paths.filter((p) => fs.existsSync(p));\r\n if (existingPaths.length === 0) {\r\n console.log(chalk.red(' Path(s) no longer exist on disk'));\r\n console.log(\r\n chalk.gray(\r\n ` Created: ${shortDateTime(session.createdAt)} Last used: ${timeAgo(effectiveLastAccessedAt(session))}`,\r\n ),\r\n );\r\n console.log('');\r\n return;\r\n }\r\n\r\n // For each existing path, gather git info\r\n for (const wtPath of existingPaths) {\r\n const branch = getCurrentBranch(wtPath);\r\n if (!branch) continue;\r\n\r\n const { merged } = isBranchMerged(branch, wtPath);\r\n const changes = getStatus(wtPath);\r\n const unpushed = getUnpushedCommits(wtPath);\r\n\r\n const changeCount = changes\r\n ? changes.split('\\n').filter((l) => l.trim()).length\r\n : 0;\r\n const unpushedCount = unpushed\r\n ? unpushed.split('\\n').filter((l) => l.trim()).length\r\n : 0;\r\n\r\n const mergedTag = merged\r\n ? chalk.green(' [merged]')\r\n : '';\r\n\r\n const parts: string[] = [];\r\n if (changeCount > 0) {\r\n parts.push(chalk.yellow(`${changeCount} uncommitted`));\r\n }\r\n if (unpushedCount > 0) {\r\n parts.push(chalk.yellow(`${unpushedCount} unpushed`));\r\n }\r\n\r\n const statusLine = parts.length > 0 ? parts.join(', ') : chalk.green('clean');\r\n\r\n if (existingPaths.length > 1) {\r\n console.log(chalk.gray(` ${wtPath}`));\r\n console.log(` ${statusLine}${mergedTag}`);\r\n } else {\r\n console.log(` ${statusLine}${mergedTag}`);\r\n console.log(chalk.gray(` ${wtPath}`));\r\n }\r\n }\r\n\r\n console.log(\r\n chalk.gray(\r\n ` Created: ${shortDateTime(session.createdAt)} Last used: ${timeAgo(effectiveLastAccessedAt(session))}`,\r\n ),\r\n );\r\n console.log('');\r\n}\r\n","const MINUTE = 60_000;\nconst HOUR = 60 * MINUTE;\nconst DAY = 24 * HOUR;\n\nexport function timeAgo(isoDate: string): string {\n const diff = Date.now() - new Date(isoDate).getTime();\n\n if (diff < MINUTE) return 'just now';\n if (diff < HOUR) {\n const mins = Math.floor(diff / MINUTE);\n return `${mins}m ago`;\n }\n if (diff < DAY) {\n const hours = Math.floor(diff / HOUR);\n return `${hours}h ago`;\n }\n const days = Math.floor(diff / DAY);\n return `${days}d ago`;\n}\n\nexport function shortDateTime(isoDate: string): string {\n const d = new Date(isoDate);\n const month = String(d.getMonth() + 1).padStart(2, '0');\n const day = String(d.getDate()).padStart(2, '0');\n const hours = String(d.getHours()).padStart(2, '0');\n const minutes = String(d.getMinutes()).padStart(2, '0');\n return `${month}-${day} ${hours}:${minutes}`;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport type { CommandModule } from 'yargs';\nimport { select } from '@inquirer/prompts';\nimport { ensureConfig } from '../core/config.js';\nimport { loadHistory, getRecentSessions, upsertSession } from '../core/history.js';\nimport { effectiveLastAccessedAt } from '../core/claude-activity.js';\nimport { getAiTool } from '../core/ai-launcher.js';\nimport { launchAi } from '../utils/platform.js';\nimport { timeAgo } from '../utils/format.js';\n\nexport const recentCommand: CommandModule = {\n command: 'recent [count]',\n describe: 'List recent worktree sessions',\n builder: (yargs) =>\n yargs\n .positional('count', {\n describe: 'Number of recent sessions to show',\n type: 'number',\n default: 10,\n })\n .option('resume', {\n describe: 'Interactively pick a session and launch the configured AI tool',\n type: 'boolean',\n default: false,\n })\n .option('unsafe', {\n describe: 'Launch the AI tool with its skip-permissions flag (used with --resume)',\n type: 'boolean',\n default: false,\n }),\n handler: async (argv) => {\n const count = argv.count as number;\n const resume = argv.resume as boolean;\n const unsafe = argv.unsafe as boolean;\n\n const config = ensureConfig();\n\n const sessions = loadHistory();\n const recent = getRecentSessions(sessions, count);\n\n if (recent.length === 0) {\n console.log(chalk.yellow('No recent worktree sessions found.'));\n return;\n }\n\n if (resume) {\n // Filter to sessions that still exist on disk\n const valid = recent.filter((s) =>\n s.paths.some((p) => fs.existsSync(p)),\n );\n\n if (valid.length === 0) {\n console.log(\n chalk.yellow('No resumable sessions (all paths removed from disk).'),\n );\n return;\n }\n\n const choices = valid.map((s) => {\n const typeTag = s.isGroup ? '[group]' : '[repo]';\n return {\n name: `${typeTag} ${s.target} ${s.branch} (${timeAgo(effectiveLastAccessedAt(s))})`,\n value: s,\n };\n });\n\n const choice = await select({\n message: 'Select a session to resume:',\n choices,\n pageSize: 15,\n });\n\n // Find first existing path\n const firstExisting = choice.paths.find((p) => fs.existsSync(p));\n if (!firstExisting) {\n console.error('Session path no longer exists.');\n process.exitCode = 1;\n return;\n }\n\n // For groups, launch in the parent directory (group root), not a single repo subfolder\n const launchPath = choice.isGroup ? path.dirname(firstExisting) : firstExisting;\n\n await upsertSession(choice.target, choice.isGroup, choice.branch, choice.paths);\n\n const tool = getAiTool(config);\n console.log(chalk.cyan(`Resuming in: ${launchPath}`));\n console.log(`Starting ${tool.cmd}...`);\n launchAi(launchPath, tool, { unsafe, resume: true }, choice.port);\n return;\n }\n\n // Just list recent sessions\n console.log('');\n console.log(chalk.cyan('Recent Sessions'));\n console.log(chalk.cyan('==============='));\n console.log('');\n\n for (const session of recent) {\n const typeLabel = session.isGroup\n ? chalk.magenta('[group]')\n : chalk.blue('[repo]');\n const exists = session.paths.some((p) => fs.existsSync(p));\n const existsTag = exists ? '' : chalk.red(' [removed]');\n\n console.log(\n `${typeLabel} ${chalk.green(session.target)} ${chalk.white(session.branch)}${existsTag}`,\n );\n console.log(\n chalk.gray(` Last used: ${timeAgo(effectiveLastAccessedAt(session))}`),\n );\n }\n\n console.log('');\n console.log(\n chalk.gray('Use work resume to interactively resume a session.'),\n );\n },\n};\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport type { CommandModule } from 'yargs';\nimport { select } from '@inquirer/prompts';\nimport { ensureConfig } from '../core/config.js';\nimport { loadHistory, getRecentSessions, upsertSession } from '../core/history.js';\nimport { effectiveLastAccessedAt } from '../core/claude-activity.js';\nimport { getAiTool } from '../core/ai-launcher.js';\nimport { launchAi } from '../utils/platform.js';\nimport { timeAgo } from '../utils/format.js';\n\nexport const resumeCommand: CommandModule = {\n command: 'resume',\n describe: 'Resume a recent worktree session',\n builder: (yargs) =>\n yargs.option('unsafe', {\n describe: 'Launch the AI tool with its skip-permissions flag',\n type: 'boolean',\n default: false,\n }),\n handler: async (argv) => {\n const unsafe = argv.unsafe as boolean;\n\n const config = ensureConfig();\n\n const sessions = loadHistory();\n const recent = getRecentSessions(sessions, sessions.length);\n\n // Filter to sessions that still exist on disk\n const valid = recent.filter((s) =>\n s.paths.some((p) => fs.existsSync(p)),\n );\n\n if (valid.length === 0) {\n console.log(\n chalk.yellow('No resumable sessions found.'),\n );\n return;\n }\n\n const choices = valid.map((s) => {\n const typeTag = s.isGroup ? '[group]' : '[repo]';\n return {\n name: `${typeTag} ${s.target} ${s.branch} (${timeAgo(effectiveLastAccessedAt(s))})`,\n value: s,\n };\n });\n\n const choice = await select({\n message: 'Select a session to resume:',\n choices,\n pageSize: 15,\n });\n\n // Find first existing path\n const firstExisting = choice.paths.find((p) => fs.existsSync(p));\n if (!firstExisting) {\n console.error('Session path no longer exists.');\n process.exitCode = 1;\n return;\n }\n\n // For groups, launch in the parent directory (group root), not a single repo subfolder\n const launchPath = choice.isGroup ? path.dirname(firstExisting) : firstExisting;\n\n await upsertSession(choice.target, choice.isGroup, choice.branch, choice.paths);\n\n const tool = getAiTool(config);\n console.log(chalk.cyan(`Resuming in: ${launchPath}`));\n console.log(`Starting ${tool.cmd}...`);\n launchAi(launchPath, tool, { unsafe, resume: true }, choice.port);\n },\n};\n","import chalk from 'chalk';\nimport { checkbox } from '@inquirer/prompts';\nimport type { CommandModule } from 'yargs';\nimport { ensureConfig } from '../core/config.js';\nimport {\n type PrunableEntry,\n collectPrunable,\n removeSingleEntry,\n removeGroupEntry,\n} from '../core/prunable-scan.js';\n\nexport const pruneCommand: CommandModule = {\n command: 'prune',\n describe: 'Remove worktrees for merged branches',\n builder: (yargs) =>\n yargs.option('force', {\n describe: 'Skip interactive picker and remove all merged worktrees',\n type: 'boolean',\n default: false,\n }),\n handler: async (argv) => {\n const force = argv.force as boolean;\n\n const config = ensureConfig();\n console.log(chalk.gray('Scanning worktrees for merged branches...\\n'));\n // Interactive prune is human-confirmed (the user sees the list and picks),\n // so squash-merged matches are safe to surface here. `work sync` stays gated\n // to true-merge confidence by default (opt in via --include-squash).\n const prunable = collectPrunable(config, { includeSquash: true });\n\n if (prunable.length === 0) {\n console.log(chalk.green('No merged worktrees found. Nothing to prune.'));\n return;\n }\n\n console.log(\n chalk.cyan(`Found ${prunable.length} merged worktree(s):\\n`),\n );\n\n let selected: PrunableEntry[];\n\n if (force) {\n selected = prunable;\n for (const entry of selected) {\n const suffix = entry.type === 'group' ? ' [group]' : '';\n console.log(` ${entry.target}: ${entry.branch}${suffix}`);\n }\n console.log('');\n } else {\n const choices = prunable.map((entry) => {\n const suffix = entry.type === 'group' ? ' [group]' : '';\n return {\n name: `${entry.target}: ${entry.branch}${suffix}`,\n value: entry,\n };\n });\n\n selected = await checkbox({\n message: 'Select merged worktrees to remove',\n choices,\n pageSize: choices.length,\n });\n\n if (selected.length === 0) {\n console.log(chalk.yellow('Nothing selected.'));\n return;\n }\n\n console.log('');\n }\n\n for (const entry of selected) {\n if (entry.type === 'single') {\n await removeSingleEntry(entry);\n } else {\n await removeGroupEntry(entry, config);\n }\n }\n\n console.log('');\n console.log(chalk.green(`Pruned ${selected.length} worktree(s).`));\n },\n};\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport { type WorkConfig } from './config.js';\nimport {\n parseWorktreeList,\n isBranchMerged,\n getCurrentBranch,\n getStatus,\n fetchRemote,\n type MergeConfidence,\n} from './git.js';\nimport { removeSingleWorktree } from './worktree.js';\nimport { removeSession } from './history.js';\n\nexport interface PrunableEntry {\n type: 'single' | 'group';\n /** repo alias (single) or group name (group) */\n target: string;\n branch: string;\n /** For single: [{ alias, repoPath, worktreePath }]. For group: one per sub-repo. */\n repos: Array<{\n alias: string;\n repoPath: string;\n worktreePath: string;\n }>;\n /**\n * True if any (sub-)repo in this entry has uncommitted changes. Used by\n * callers to decide whether removal needs the explicit --force escape hatch.\n */\n hasChanges: boolean;\n /**\n * Merge confidence for the whole entry. 'merged' is a high-confidence true\n * merge; 'squash-merged' means at least one (sub-)repo only matched via the\n * lower-confidence squash heuristic. Unattended callers should require an\n * explicit opt-in before pruning 'squash-merged' entries.\n */\n confidence: MergeConfidence;\n}\n\ninterface ScanEntry {\n target: string;\n branch: string;\n merged: boolean;\n into: string | null;\n hasChanges: boolean;\n /**\n * Merge confidence for this row. 'squash-merged' rows only matched the\n * lower-confidence squash heuristic and are gated out of the prunable list\n * by default — the table labels them distinctly so it never claims a row is\n * a plain 'merged' when prune/sync will actually skip it.\n */\n confidence: MergeConfidence | null;\n /** Sub-label for group repos, e.g. \"[straumur]\" */\n subLabel?: string;\n}\n\nexport interface CollectOptions {\n /**\n * When true, fetch each configured repo before scanning so merge checks use\n * up-to-date remote refs. Defaults to true (matches `work prune`). Callers\n * that already fetched (e.g. `work sync`, which fetches in parallel) pass\n * false to avoid redundant serial fetches.\n */\n fetch?: boolean;\n /** When false, suppress the per-target scan-result table. Defaults to true. */\n print?: boolean;\n /**\n * When true, include entries whose only positive signal is the\n * lower-confidence squash-merge heuristic. Defaults to false so unattended\n * callers (e.g. `work sync`) require true-merge confidence and only opt into\n * squash matches explicitly.\n */\n includeSquash?: boolean;\n /**\n * Aliases to skip entirely (e.g. repos whose fetch failed, so their remote\n * refs may be stale and merge checks unreliable). Worktrees in these repos\n * are never reported as prunable.\n */\n skipAliases?: Set<string>;\n}\n\nexport function collectPrunable(\n config: WorkConfig,\n options: CollectOptions = {},\n): PrunableEntry[] {\n const { fetch = true, print = true, includeSquash = false } = options;\n const skipAliases = options.skipAliases ?? new Set<string>();\n const prunable: PrunableEntry[] = [];\n const scanResults: ScanEntry[] = [];\n\n // Fetch all repos upfront so merge checks use up-to-date remote refs\n if (fetch) {\n for (const [alias, repoPath] of Object.entries(config.repos)) {\n if (!fs.existsSync(repoPath)) continue;\n console.log(chalk.gray(` Fetching ${alias}...`));\n fetchRemote(repoPath);\n }\n console.log('');\n }\n\n // Track branches already covered by a group so we don't double-list them\n const groupCoveredKeys = new Set<string>();\n\n // --- Groups ---\n for (const [groupName, aliases] of Object.entries(config.groups)) {\n const groupDir = path.join(config.worktreesRoot, groupName);\n if (!fs.existsSync(groupDir)) continue;\n\n let branchDirs: string[];\n try {\n branchDirs = fs\n .readdirSync(groupDir, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name);\n } catch {\n continue;\n }\n\n for (const branchDir of branchDirs) {\n const branchDirPath = path.join(groupDir, branchDir);\n let branch: string | undefined;\n let allMerged = true;\n let groupHasChanges = false;\n // Whole-group confidence is the weakest across sub-repos: if any sub-repo\n // only matched via the squash heuristic, the group is squash-confidence.\n let groupConfidence: MergeConfidence = 'merged';\n let skipped = false;\n const repos: PrunableEntry['repos'] = [];\n\n for (const alias of aliases) {\n const repoPath = config.repos[alias];\n if (!repoPath) continue;\n // Skip groups touching a repo we couldn't fetch — stale refs make the\n // merge check unreliable, so we must not prune on them.\n if (skipAliases.has(alias)) {\n skipped = true;\n continue;\n }\n const repoName = path.basename(repoPath);\n const subWorktreePath = path.join(branchDirPath, repoName);\n\n if (!fs.existsSync(subWorktreePath)) {\n continue;\n }\n\n const currentBranch = getCurrentBranch(subWorktreePath);\n if (!currentBranch) {\n allMerged = false;\n continue;\n }\n\n if (!branch) branch = currentBranch;\n\n const { merged, into, confidence } = isBranchMerged(currentBranch, repoPath);\n const changes = getStatus(subWorktreePath);\n if (changes) groupHasChanges = true;\n\n scanResults.push({\n target: groupName,\n branch,\n merged,\n into,\n hasChanges: !!changes,\n confidence,\n subLabel: alias,\n });\n\n if (!merged) {\n allMerged = false;\n } else if (confidence === 'squash-merged') {\n groupConfidence = 'squash-merged';\n }\n repos.push({ alias, repoPath, worktreePath: subWorktreePath });\n }\n\n const squashGated = groupConfidence === 'squash-merged' && !includeSquash;\n if (allMerged && !skipped && !squashGated && repos.length > 0 && branch) {\n prunable.push({\n type: 'group',\n target: groupName,\n branch,\n repos,\n hasChanges: groupHasChanges,\n confidence: groupConfidence,\n });\n\n for (const r of repos) {\n groupCoveredKeys.add(`${r.alias}:${branch}`);\n }\n }\n }\n }\n\n // --- Single repos ---\n for (const [alias, repoPath] of Object.entries(config.repos)) {\n if (!fs.existsSync(repoPath)) continue;\n // Skip repos we couldn't fetch — stale refs make merge checks unreliable.\n if (skipAliases.has(alias)) continue;\n\n const worktrees = parseWorktreeList(repoPath);\n const normalizedRepoPath = path.resolve(repoPath);\n\n for (const wt of worktrees) {\n if (path.resolve(wt.path) === normalizedRepoPath) continue;\n if (!wt.branch) continue;\n if (groupCoveredKeys.has(`${alias}:${wt.branch}`)) continue;\n\n const { merged, into, confidence } = isBranchMerged(wt.branch, repoPath);\n const changes = getStatus(wt.path);\n\n scanResults.push({\n target: alias,\n branch: wt.branch,\n merged,\n into,\n hasChanges: !!changes,\n confidence,\n });\n\n const squashGated = confidence === 'squash-merged' && !includeSquash;\n if (merged && confidence && !squashGated) {\n prunable.push({\n type: 'single',\n target: alias,\n branch: wt.branch,\n repos: [{ alias, repoPath, worktreePath: wt.path }],\n hasChanges: !!changes,\n confidence,\n });\n }\n }\n }\n\n // Print scan results grouped by target, merged first\n if (print) {\n printScanResults(scanResults);\n }\n\n return prunable;\n}\n\nexport function printScanResults(results: ScanEntry[]): void {\n // Group by target\n const byTarget = new Map<string, ScanEntry[]>();\n for (const r of results) {\n const entries = byTarget.get(r.target) ?? [];\n entries.push(r);\n byTarget.set(r.target, entries);\n }\n\n for (const [target, entries] of byTarget) {\n // Sort: not merged first, then merged\n entries.sort((a, b) => (a.merged === b.merged ? 0 : a.merged ? 1 : -1));\n\n console.log(chalk.cyan(`${target}:`));\n for (const e of entries) {\n const parts: string[] = [];\n if (!e.merged) {\n parts.push(chalk.red('not merged'));\n } else if (e.confidence === 'squash-merged') {\n // Distinct label: squash matches are skipped by default (sync needs\n // --include-squash), so don't show them as a plain 'merged'.\n parts.push(chalk.yellow(`squash-merged into ${e.into} (skipped unless --include-squash)`));\n } else {\n parts.push(chalk.green(`merged into ${e.into}`));\n }\n if (e.hasChanges) parts.push(chalk.yellow('uncommitted changes'));\n\n const label = e.subLabel\n ? `${e.branch} [${e.subLabel}]`\n : e.branch;\n console.log(` ${chalk.gray(`${label}:`)} ${parts.join(', ')}`);\n }\n console.log('');\n }\n}\n\n/**\n * Remove a single-repo prunable entry. Returns true only if the worktree was\n * actually removed (so callers can tally non-throwing failures). When `force`\n * is false, removeSingleWorktree refuses worktrees with uncommitted changes or\n * unpushed commits and returns false.\n */\nexport async function removeSingleEntry(\n entry: PrunableEntry,\n force = true,\n): Promise<boolean> {\n const { repoPath, worktreePath } = entry.repos[0];\n console.log(chalk.cyan(`Removing ${entry.target}: ${entry.branch}`));\n\n const removed = removeSingleWorktree(repoPath, worktreePath, entry.branch, force);\n if (removed) {\n await removeSession(entry.target, entry.branch);\n }\n return removed;\n}\n\n/**\n * Remove a group prunable entry (one worktree per sub-repo). Returns true only\n * if every sub-repo worktree was removed. When `force` is false, dirty/unpushed\n * sub-repos are refused and the entry is reported as a failure.\n */\nexport async function removeGroupEntry(\n entry: PrunableEntry,\n config: WorkConfig,\n force = true,\n): Promise<boolean> {\n const workTreeDirName = entry.branch.replace(/\\//g, '-');\n const groupWorktreePath = path.join(\n config.worktreesRoot,\n entry.target,\n workTreeDirName,\n );\n\n console.log(\n chalk.cyan(`Removing group ${entry.target}: ${entry.branch}`),\n );\n\n let allRemoved = true;\n\n for (const { alias, repoPath, worktreePath } of entry.repos) {\n const repoName = path.basename(repoPath);\n console.log(chalk.cyan(` [${alias}] (${repoName}):`));\n const removed = removeSingleWorktree(repoPath, worktreePath, entry.branch, force);\n if (!removed) {\n allRemoved = false;\n }\n }\n\n if (allRemoved) {\n await removeSession(entry.target, entry.branch);\n }\n\n // Clean up CLAUDE.md in group worktree\n const claudeMdInWorktree = path.join(groupWorktreePath, 'CLAUDE.md');\n if (fs.existsSync(claudeMdInWorktree)) {\n fs.unlinkSync(claudeMdInWorktree);\n }\n\n // Remove parent dir only if empty\n try {\n if (fs.existsSync(groupWorktreePath)) {\n const contents = fs.readdirSync(groupWorktreePath);\n if (contents.length === 0) {\n fs.rmSync(groupWorktreePath, { recursive: true, force: true });\n console.log(\n chalk.green(\n ` Cleaned up group directory: ${groupWorktreePath}`,\n ),\n );\n }\n }\n } catch {\n // ignore\n }\n\n return allRemoved;\n}\n","import fs from 'node:fs';\nimport chalk from 'chalk';\nimport type { CommandModule } from 'yargs';\nimport { ensureConfig } from '../core/config.js';\nimport { fetchRemoteAsync, getUnpushedCommits } from '../core/git.js';\nimport {\n collectPrunable,\n removeSingleEntry,\n removeGroupEntry,\n} from '../core/prunable-scan.js';\n\nexport const syncCommand: CommandModule = {\n command: 'sync',\n describe: 'Fetch all repos in parallel and prune merged worktrees (non-interactive)',\n builder: (yargs) =>\n yargs\n .option('dry-run', {\n describe: 'Show what would be pruned without removing anything',\n type: 'boolean',\n default: false,\n })\n .option('force', {\n describe:\n 'Force-remove worktrees with uncommitted changes or unpushed commits ' +\n '(default: false — such worktrees are skipped unless --force is given)',\n type: 'boolean',\n default: false,\n })\n .option('include-squash', {\n describe:\n 'Also prune branches detected only via the squash-merge heuristic ' +\n '(default: false — unattended sync requires true-merge confidence)',\n type: 'boolean',\n default: false,\n }),\n handler: async (argv) => {\n const dryRun = argv['dryRun'] as boolean;\n const force = argv['force'] as boolean;\n const includeSquash = argv['includeSquash'] as boolean;\n\n const config = ensureConfig();\n\n // Fetch all configured repos in parallel so merge checks use fresh refs.\n const repoEntries = Object.entries(config.repos).filter(([, repoPath]) =>\n fs.existsSync(repoPath),\n );\n\n // Repos whose fetch failed: their remote refs may be stale, so merge checks\n // are unreliable. Skip pruning any worktree backed by these repos.\n const skipAliases = new Set<string>();\n\n if (repoEntries.length > 0) {\n console.log(chalk.gray(`Fetching ${repoEntries.length} repo(s)...`));\n await Promise.all(\n repoEntries.map(async ([alias, repoPath]) => {\n try {\n await fetchRemoteAsync(repoPath);\n } catch (err) {\n skipAliases.add(alias);\n console.log(\n chalk.yellow(\n ` Warning: fetch failed for ${alias} — skipping prune for this ` +\n `repo (refs may be stale): ${\n err instanceof Error ? err.message : String(err)\n }`,\n ),\n );\n }\n }),\n );\n console.log('');\n }\n\n console.log(chalk.gray('Scanning worktrees for merged branches...\\n'));\n // We already fetched in parallel above; skip the serial fetch in collect.\n // print:false avoids double-printing the scan table — sync prints its own\n // summary below.\n const prunable = collectPrunable(config, {\n fetch: false,\n print: false,\n includeSquash,\n skipAliases,\n });\n\n if (prunable.length === 0) {\n console.log(chalk.green('No merged worktrees found. Everything is in sync.'));\n return;\n }\n\n console.log(chalk.cyan(`Found ${prunable.length} merged worktree(s):\\n`));\n for (const entry of prunable) {\n const suffix = entry.type === 'group' ? ' [group]' : '';\n console.log(` ${entry.target}: ${entry.branch}${suffix}`);\n }\n console.log('');\n\n if (dryRun) {\n console.log(\n chalk.yellow(`Dry run: would prune ${prunable.length} worktree(s). Nothing removed.`),\n );\n return;\n }\n\n let removed = 0;\n let failed = 0;\n let skippedDirty = 0;\n let skippedUnpushed = 0;\n for (const entry of prunable) {\n // Safe by default: never force-remove a worktree with uncommitted\n // changes or unpushed commits unless --force was passed explicitly.\n if (entry.hasChanges && !force) {\n skippedDirty++;\n console.log(\n chalk.yellow(\n ` Skipping ${entry.target}: ${entry.branch} — uncommitted changes ` +\n `(pass --force to remove anyway).`,\n ),\n );\n continue;\n }\n\n // A clean worktree may still hold commits not pushed to its upstream.\n // removeSingleWorktree(force=false) refuses these, which would otherwise\n // surface as a \"Failed to remove\" + exitCode=1. That's not a failure —\n // it's the same safe-by-default skip as uncommitted changes. Detect it\n // here and report it as a benign skip. (entry.hasChanges only covers the\n // working tree, not unpushed history.)\n if (!force) {\n const unpushedRepos = entry.repos.filter(\n (r) => getUnpushedCommits(r.worktreePath) !== '',\n );\n if (unpushedRepos.length > 0) {\n skippedUnpushed++;\n console.log(\n chalk.yellow(\n ` Skipping ${entry.target}: ${entry.branch} — unpushed commits ` +\n `(pass --force to remove anyway).`,\n ),\n );\n continue;\n }\n }\n\n try {\n const ok =\n entry.type === 'single'\n ? await removeSingleEntry(entry, force)\n : await removeGroupEntry(entry, config, force);\n if (ok) {\n removed++;\n } else {\n // Non-throwing failure (e.g. removeSingleWorktree refused or git\n // failed). Count it so process.exitCode is reliable for CI.\n failed++;\n console.log(\n chalk.red(\n ` Failed to remove ${entry.target}: ${entry.branch}`,\n ),\n );\n }\n } catch (err) {\n failed++;\n console.log(\n chalk.red(\n ` Failed to remove ${entry.target}: ${entry.branch} — ${\n err instanceof Error ? err.message : String(err)\n }`,\n ),\n );\n }\n }\n\n console.log('');\n console.log(chalk.green(`Pruned ${removed} worktree(s).`));\n if (skippedDirty > 0) {\n console.log(\n chalk.yellow(\n `Skipped ${skippedDirty} worktree(s) with local changes (use --force).`,\n ),\n );\n }\n if (skippedUnpushed > 0) {\n console.log(\n chalk.yellow(\n `Skipped ${skippedUnpushed} worktree(s) with unpushed commits (use --force).`,\n ),\n );\n }\n if (failed > 0) {\n console.log(chalk.red(`Failed to remove ${failed} worktree(s).`));\n process.exitCode = 1;\n }\n },\n};\n","import React from 'react';\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport { render } from 'ink';\r\nimport { App, runDashboardCleanup } from './App.js';\r\nimport { getConfigDir } from '../core/config.js';\r\n\r\n/** Synchronous crash log — survives hard exits where the async stream doesn't flush. */\r\nfunction crashLog(label: string, err: unknown): void {\r\n try {\r\n const msg = err instanceof Error ? err.stack || err.message : String(err);\r\n const line = `${new Date().toISOString()} [CRASH] ${label}: ${msg}\\n`;\r\n fs.appendFileSync(path.join(getConfigDir(), 'debug.log'), line);\r\n } catch { /* last resort — nothing we can do */ }\r\n}\r\n\r\nexport async function startDashboard(unsafe: boolean): Promise<void> {\r\n if (!process.stdin.isTTY) {\r\n console.error('work dash requires an interactive terminal');\r\n process.exit(1);\r\n }\r\n\r\n // Enter alternate screen buffer and hide cursor\r\n process.stdout.write('\\x1B[?1049h\\x1B[?25l');\r\n process.stdin.setRawMode(true);\r\n process.stdin.resume();\r\n\r\n const restore = () => {\r\n process.stdout.write('\\x1B]0;\\x07\\x1B[?25h\\x1B[?1049l');\r\n };\r\n\r\n try {\r\n const { waitUntilExit, unmount } = render(\r\n <App unsafe={unsafe} onExit={() => {\r\n unmount();\r\n restore();\r\n process.exit(0);\r\n }} />,\r\n {\r\n exitOnCtrlC: false,\r\n patchConsole: false,\r\n },\r\n );\r\n\r\n process.on('exit', restore);\r\n process.on('SIGINT', () => { runDashboardCleanup(); restore(); process.exit(0); });\r\n process.on('SIGTERM', () => { runDashboardCleanup(); restore(); process.exit(0); });\r\n process.on('uncaughtException', (err) => {\r\n // node-pty throws async errors for already-exited PTYs — non-fatal\r\n if (err instanceof Error && err.message?.includes('pty that has already exited')) return;\r\n crashLog('uncaughtException', err);\r\n runDashboardCleanup();\r\n unmount();\r\n restore();\r\n console.error('work dash error:', err);\r\n process.exit(1);\r\n });\r\n\r\n process.on('unhandledRejection', (reason) => {\r\n crashLog('unhandledRejection', reason);\r\n });\r\n\r\n await waitUntilExit();\r\n } catch (err) {\r\n crashLog('catch', err);\r\n restore();\r\n console.error('work dash error:', err);\r\n process.exit(1);\r\n }\r\n}\r\n","import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react';\nimport { Box } from 'ink';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { execFile } from 'node:child_process';\nimport spawn from 'cross-spawn';\nimport {\n loadHistory,\n getRecentSessions,\n upsertSession,\n getHistoryPath,\n type WorktreeSession,\n} from '../core/history.js';\nimport { loadConfig } from '../core/config.js';\nimport { rebaseOntoMain, countConflicts, isBranchMerged, fetchRemoteAsync } from '../core/git.js';\nimport { fetchAllPullRequests, isGhAvailable, type BranchPrMap } from '../core/pr.js';\nimport { fetchMyJiraIssues, isAcliAvailable, type JiraIssue } from '../core/jira.js';\nimport { getTasks, addTask, completeTask, uncompleteTask, removeTask, editTask, getTasksPath_, type Task } from '../core/tasks.js';\nimport { getAiTool, buildAiLaunchArgs } from '../core/ai-launcher.js';\nimport { openUrl } from '../utils/platform.js';\nimport { PtySession, type SessionStatus } from '../tui/session.js';\nimport { debug } from '../core/logger.js';\nimport { HookServer, type HookEvent } from '../core/hook-server.js';\nimport { notifyDesktop, notifyKindForEvent } from '../core/notifier.js';\nimport { runStatusHooks } from '../core/status-hooks.js';\nimport { renderBufferLines } from './renderer-lines.js';\nimport {\n Sidebar, PrPane, JiraPane, TaskPane,\n buildSessionRows, buildProjectRows, buildPrRows, buildJiraRows, buildTaskRows,\n countSelectable, cursorToRow, visualRowToCursor, sessionKey,\n type SidebarRow,\n} from './Sidebar.js';\nimport { TerminalPane } from './TerminalPane.js';\nimport { StatusBar } from './StatusBar.js';\n\nconst DETACH_KEY = '\\x1D'; // Ctrl+]\nconst TAB_KEY = '\\t';\nconst RENDER_INTERVAL_MS = 50;\n\n// SGR mouse: \\x1b[<button;col;rowM or \\x1b[<button;col;rowm\n// Button 64 = scroll up, 65 = scroll down\nconst MOUSE_SGR_RE = /\\x1b\\[<(\\d+);(\\d+);(\\d+)[Mm]/;\n\nenum Focus {\n SESSIONS,\n TASKS,\n PRS,\n JIRA,\n TERMINAL,\n}\n\n/** Top pane can show sessions (default) or projects (when creating new worktree). */\nenum TopPaneMode {\n SESSIONS,\n PROJECTS,\n BRANCH_INPUT,\n}\n\n\n/** Check if Claude has an existing conversation for a directory. */\nfunction hasClaudeConversation(dir: string): boolean {\n try {\n const resolved = path.resolve(dir);\n const projectDir = resolved.replace(/[/\\\\:]/g, '-');\n const claudeProjectPath = path.join(os.homedir(), '.claude', 'projects', projectDir);\n if (!fs.existsSync(claudeProjectPath)) return false;\n const files = fs.readdirSync(claudeProjectPath);\n return files.some((f) => f.endsWith('.jsonl'));\n } catch {\n return false;\n }\n}\n\n/**\n * Stable notification-dedup key for a PTY: its resolved launch directory.\n * findPtyByCwd matches hook cwds by prefix against this, so deriving the key\n * here collapses all sub-repo / group child cwds onto the one PtySession.\n */\nfunction ptyDedupKey(pty: PtySession): string {\n return path.resolve(pty.cwd).toLowerCase();\n}\n\nfunction loadSessions(): WorktreeSession[] {\n const all = loadHistory();\n return getRecentSessions(all, 50).filter((s) =>\n s.paths.some((p) => fs.existsSync(p)),\n );\n}\n\nfunction loadProjects(): Array<{ name: string; isGroup: boolean }> {\n const config = loadConfig();\n if (!config) return [];\n const projects: Array<{ name: string; isGroup: boolean }> = [];\n for (const name of Object.keys(config.groups)) {\n projects.push({ name, isGroup: true });\n }\n for (const name of Object.keys(config.repos)) {\n projects.push({ name, isGroup: false });\n }\n return projects;\n}\n\n/** Find the best project target for a repo alias: prefer group if it belongs to one. */\nfunction repoToProject(repoAlias: string, config: { repos: Record<string, string>; groups: Record<string, string[]> }): string {\n for (const [groupName, aliases] of Object.entries(config.groups)) {\n if (aliases.includes(repoAlias)) return groupName;\n }\n return repoAlias;\n}\n\nfunction mechanicalSlug(summary: string): string {\n return summary\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-|-$/g, '')\n .slice(0, 50);\n}\n\nfunction generateSlug(summary: string): Promise<string> {\n const fallback = mechanicalSlug(summary);\n return new Promise((resolve) => {\n const child = execFile(\n 'claude',\n ['-p', '--model', 'haiku'],\n { encoding: 'utf-8', timeout: 10000 },\n (err, stdout) => {\n const result = stdout?.trim()\n .toLowerCase()\n .replace(/[^a-z0-9-]/g, '')\n .replace(/^-|-$/g, '');\n resolve(result || fallback);\n },\n );\n child.stdin?.write(\n `Generate a short git branch slug (max 40 chars, lowercase, hyphens only) for: ${summary}\\nOutput ONLY the slug, nothing else.`,\n );\n child.stdin?.end();\n });\n}\n\ninterface AppProps {\n unsafe: boolean;\n onExit: () => void;\n}\n\n/**\n * Module-level cleanup registry. App registers its PTY/HookServer cleanup\n * here so that index.tsx's signal handlers (SIGINT/SIGTERM/uncaughtException)\n * can run it before calling process.exit(), which otherwise leaves orphaned\n * `claude` processes and stale hook entries in ~/.claude/settings.json.\n */\nlet dashboardCleanup: (() => void) | null = null;\nexport function runDashboardCleanup(): void {\n dashboardCleanup?.();\n}\n\nexport function App({ unsafe, onExit }: AppProps) {\n const [focus, setFocus] = useState(Focus.SESSIONS);\n const [sessions, setSessions] = useState<WorktreeSession[]>(loadSessions);\n const [projects] = useState(loadProjects);\n const [config] = useState(() => loadConfig());\n const [sessionCursor, setSessionCursor] = useState(0);\n const [prCursor, setPrCursor] = useState(0);\n const [jiraCursor, setJiraCursor] = useState(0);\n const [taskCursor, setTaskCursor] = useState(0);\n const [tasks, setTasks] = useState<Task[]>(getTasks);\n const [taskInput, setTaskInput] = useState<string | null>(null);\n const [taskEditId, setTaskEditId] = useState<number | null>(null);\n const taskEditIdRef = useRef(taskEditId);\n const [jiraIssues, setJiraIssues] = useState<JiraIssue[]>([]);\n const [acliAvailable, setAcliAvailable] = useState(false);\n const jiraFetching = useRef(false);\n const [conflictCounts, setConflictCounts] = useState<Map<string, number>>(new Map());\n const [mergedSet, setMergedSet] = useState<Set<string>>(new Set());\n const [prMap, setPrMap] = useState<BranchPrMap>(new Map());\n const [ghAvailable, setGhAvailable] = useState(false);\n const [syncing, setSyncing] = useState(false);\n const prFetching = useRef(false);\n const [activeKey, setActiveKey] = useState<string | null>(null);\n const [message, setMessage] = useState('');\n const [termLines, setTermLines] = useState<string[]>([]);\n const termScrollBack = useRef(0);\n const [statusVersion, setStatusVersion] = useState(0);\n const [topPaneMode, setTopPaneMode] = useState(TopPaneMode.SESSIONS);\n const [branchInput, setBranchInput] = useState<{ projectName: string; value: string; isGroup: boolean } | null>(null);\n const [pendingBranch, setPendingBranch] = useState<string | null>(null);\n const [pendingJiraIssue, setPendingJiraIssue] = useState<JiraIssue | null>(null);\n const [pendingTask, setPendingTask] = useState<Task | null>(null);\n const [savedSessionCursor, setSavedSessionCursor] = useState(0);\n\n const localBranches = useMemo(() => new Set(sessions.map((s) => s.branch)), [sessions]);\n const projectRows = useMemo(() => buildProjectRows(projects), [projects]);\n const prRows = useMemo(() => buildPrRows(prMap), [prMap]);\n const jiraRows = useMemo(() => buildJiraRows(jiraIssues), [jiraIssues]);\n const taskRows = useMemo(() => buildTaskRows(tasks), [tasks]);\n\n const ptySessions = useRef(new Map<string, PtySession>());\n const sessionRows = useMemo(() => {\n const sMap = new Map<string, SessionStatus>();\n for (const [key, pty] of ptySessions.current) {\n if (pty.exited) continue;\n sMap.set(key, pty.idle ? 'idle' : 'running');\n }\n return buildSessionRows(sessions, sMap);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [sessions, statusVersion]);\n const topRows = topPaneMode === TopPaneMode.SESSIONS ? sessionRows : projectRows;\n const renderPending = useRef(false);\n const focusRef = useRef(focus);\n const activeKeyRef = useRef(activeKey);\n const sessionCursorRef = useRef(sessionCursor);\n const prCursorRef = useRef(prCursor);\n const jiraCursorRef = useRef(jiraCursor);\n const jiraRowsRef = useRef(jiraRows);\n const taskCursorRef = useRef(taskCursor);\n const taskRowsRef = useRef(taskRows);\n const taskInputRef = useRef(taskInput);\n const sessionsRef = useRef(sessions);\n const topPaneModeRef = useRef(topPaneMode);\n const branchInputRef = useRef(branchInput);\n const topRowsRef = useRef(topRows);\n const prRowsRef = useRef(prRows);\n\n // Keep refs in sync\n focusRef.current = focus;\n activeKeyRef.current = activeKey;\n sessionCursorRef.current = sessionCursor;\n prCursorRef.current = prCursor;\n jiraCursorRef.current = jiraCursor;\n jiraRowsRef.current = jiraRows;\n taskCursorRef.current = taskCursor;\n taskRowsRef.current = taskRows;\n taskInputRef.current = taskInput;\n taskEditIdRef.current = taskEditId;\n sessionsRef.current = sessions;\n topPaneModeRef.current = topPaneMode;\n branchInputRef.current = branchInput;\n const pendingBranchRef = useRef(pendingBranch);\n pendingBranchRef.current = pendingBranch;\n const pendingJiraIssueRef = useRef(pendingJiraIssue);\n pendingJiraIssueRef.current = pendingJiraIssue;\n const pendingTaskRef = useRef(pendingTask);\n pendingTaskRef.current = pendingTask;\n topRowsRef.current = topRows;\n prRowsRef.current = prRows;\n const acliAvailableRef = useRef(acliAvailable);\n acliAvailableRef.current = acliAvailable;\n const hookServerRef = useRef<HookServer | null>(null);\n // Sessions already alerted for their current idle period (notification dedupe).\n const notifiedSessionsRef = useRef<Set<string>>(new Set());\n\n // Layout — reactive to terminal resize\n const [dims, setDims] = useState(() => ({\n cols: process.stdout.columns || 80,\n rows: process.stdout.rows || 24,\n }));\n const cols = dims.cols;\n const rows = dims.rows;\n const sidebarWidth = Math.min(60, Math.floor(cols * 0.525));\n const termWidth = cols - sidebarWidth;\n const termInner = termWidth - 2;\n const contentHeight = rows - 1; // status bar\n // Split left column into 3 panes\n const jiraPaneHeight = acliAvailable ? Math.max(5, Math.floor(contentHeight * 0.20)) : 0;\n const taskPaneHeight = Math.max(5, Math.floor(contentHeight * 0.20));\n const prPaneHeight = Math.max(5, Math.floor((contentHeight - jiraPaneHeight - taskPaneHeight) * 0.45));\n const sessionPaneHeight = contentHeight - prPaneHeight - taskPaneHeight - jiraPaneHeight;\n\n // Update terminal title — icon + count per state, hide if 0\n useEffect(() => {\n let idleCount = 0;\n let runningCount = 0;\n for (const pty of ptySessions.current.values()) {\n if (pty.exited) continue;\n if (pty.idle) idleCount++;\n else runningCount++;\n }\n const parts: string[] = [];\n if (idleCount > 0) parts.push(`🟡 ${idleCount}`);\n if (runningCount > 0) parts.push(`🟢 ${runningCount}`);\n const title = parts.length > 0 ? parts.join(' ') : 'work dash';\n process.stdout.write(`\\x1B]0;${title}\\x07`);\n }, [statusVersion, activeKey]);\n\n const computeGeneration = useRef(0);\n const computeConflictsAndMerged = useCallback((sessionList: WorktreeSession[]) => {\n const generation = ++computeGeneration.current;\n const tick = () => new Promise<void>((r) => setTimeout(r, 0));\n\n (async () => {\n const counts = new Map<string, number>();\n const merged = new Set<string>();\n\n for (const s of sessionList) {\n if (computeGeneration.current !== generation) return; // cancelled\n await tick(); // yield to keep UI responsive (git calls are sync)\n\n const existing = s.paths.find((p) => fs.existsSync(p));\n if (!existing || !s.branch) continue;\n const key = sessionKey(s);\n try {\n const c = countConflicts(s.branch, existing);\n if (c > 0) counts.set(key, c);\n } catch { /* ignore */ }\n try {\n const { merged: isMerged } = isBranchMerged(s.branch, existing);\n if (isMerged) merged.add(key);\n } catch { /* ignore */ }\n }\n\n if (computeGeneration.current === generation) {\n setConflictCounts(counts);\n setMergedSet(merged);\n }\n })();\n }, []);\n\n const refreshPrs = useCallback(() => {\n if (!ghAvailable || !config || prFetching.current) return;\n prFetching.current = true;\n setMessage('Loading PRs...');\n fetchAllPullRequests(config.repos)\n .then((prs) => {\n setPrMap(prs);\n setMessage('');\n })\n .catch(() => {})\n .finally(() => { prFetching.current = false; });\n }, [ghAvailable, config]);\n\n const refreshJira = useCallback(() => {\n if (!acliAvailable || jiraFetching.current) return;\n jiraFetching.current = true;\n fetchMyJiraIssues()\n .then((issues) => setJiraIssues(issues))\n .catch(() => {})\n .finally(() => { jiraFetching.current = false; });\n }, [acliAvailable]);\n\n const refreshTasks = useCallback(() => {\n setTasks(getTasks());\n }, []);\n\n const refreshSessions = useCallback(() => {\n const updated = loadSessions();\n setSessions(updated);\n computeConflictsAndMerged(updated);\n }, [computeConflictsAndMerged]);\n\n const buildStatusMap = useCallback(() => {\n const map = new Map<string, 'stopped' | 'running' | 'idle'>();\n for (const [key, pty] of ptySessions.current) {\n if (pty.exited) continue;\n map.set(key, pty.idle ? 'idle' : 'running');\n }\n return map;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [statusVersion]);\n\n const scheduleTerminalRender = useCallback((pty: PtySession) => {\n if (renderPending.current) return;\n renderPending.current = true;\n setTimeout(() => {\n renderPending.current = false;\n try {\n // If scrolled back, don't auto-update (user is reading scrollback)\n if (termScrollBack.current > 0) return;\n const lines = renderBufferLines(pty.terminal.buffer.active, termInner, contentHeight - 2);\n setTermLines(lines);\n } catch { /* buffer not ready */ }\n }, RENDER_INTERVAL_MS);\n }, [termInner, contentHeight]);\n\n /** Re-render terminal at current scroll offset. */\n const renderTermAtScroll = useCallback(() => {\n if (!activeKeyRef.current) return;\n const pty = ptySessions.current.get(activeKeyRef.current);\n if (!pty || pty.exited) return;\n try {\n setTermLines(renderBufferLines(pty.terminal.buffer.active, termInner, contentHeight - 2, termScrollBack.current));\n } catch { /* */ }\n }, [termInner, contentHeight]);\n\n const findPtyByCwd = useCallback((cwd: string): PtySession | undefined => {\n const normalized = path.resolve(cwd).toLowerCase();\n for (const pty of ptySessions.current.values()) {\n if (pty.exited) continue;\n // Match if hook cwd is inside the PTY's launch directory\n if (normalized.startsWith(path.resolve(pty.cwd).toLowerCase())) return pty;\n }\n return undefined;\n }, []);\n\n // Enable SGR mouse tracking for scroll wheel support\n useEffect(() => {\n process.stdout.write('\\x1b[?1000h\\x1b[?1006h');\n return () => { process.stdout.write('\\x1b[?1000l\\x1b[?1006l'); };\n }, []);\n\n // Initial sync: fetch remotes, check gh, then refresh everything\n useEffect(() => {\n let cancelled = false;\n setSyncing(true);\n\n (async () => {\n const [, ghOk, acliOk] = await Promise.all([\n config\n ? Promise.all(\n Object.values(config.repos).map((repoPath) =>\n fetchRemoteAsync(repoPath).catch(() => {}),\n ),\n )\n : Promise.resolve(),\n isGhAvailable(),\n isAcliAvailable(),\n ]);\n\n if (cancelled) return;\n setSyncing(false);\n refreshSessions();\n setGhAvailable(ghOk);\n setAcliAvailable(acliOk);\n })();\n\n return () => { cancelled = true; };\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n // Watch history file for changes (e.g. when work tree creates a new session)\n useEffect(() => {\n const historyPath = getHistoryPath();\n try {\n const watcher = fs.watch(historyPath, () => refreshSessions());\n return () => watcher.close();\n } catch { /* file may not exist yet */ }\n }, [refreshSessions]);\n\n // Watch tasks file for external changes\n useEffect(() => {\n const tasksPath = getTasksPath_();\n try {\n const watcher = fs.watch(tasksPath, () => refreshTasks());\n return () => watcher.close();\n } catch { /* file may not exist yet */ }\n }, [refreshTasks]);\n\n // Fetch PRs once gh is confirmed available\n useEffect(() => {\n if (ghAvailable) refreshPrs();\n }, [ghAvailable, refreshPrs]);\n\n // Fetch Jira issues once acli is confirmed available\n useEffect(() => {\n if (acliAvailable) refreshJira();\n }, [acliAvailable, refreshJira]);\n\n // Hook server\n useEffect(() => {\n const hookServer = new HookServer({\n owner: 'dash',\n callback: (cwd: string, event: HookEvent) => {\n const pty = findPtyByCwd(cwd);\n if (!pty) return;\n if (event === 'stop' || event === 'notification') {\n pty.setIdle(true);\n } else if (event === 'prompt_submit') {\n pty.setIdle(false);\n }\n // Desktop notification (opt-in; no-op when disabled/unsupported).\n // De-duped per session per idle period via notifyKindForEvent, so the\n // first idle alerts and repeated Stop events don't spam — independent\n // of PtySession's initial idle state.\n //\n // The dedup key is derived from the resolved PtySession identity\n // (its launch dir), NOT the raw hook cwd. findPtyByCwd matches by\n // prefix, so multiple sub-repo / group cwds resolve to one PtySession;\n // keying off the raw cwd would let that single session notify multiple\n // times for distinct child paths.\n const dedupKey = ptyDedupKey(pty);\n const kind = notifyKindForEvent(\n event,\n dedupKey,\n notifiedSessionsRef.current,\n );\n if (kind) {\n notifyDesktop(path.basename(pty.cwd), kind, {\n enabled: config?.notifications === true,\n });\n // User-configurable status-change commands (opt-in via config).\n // Fire-and-forget; independent of the desktop-notification path.\n // Use the resolved PtySession launch dir (pty.cwd), not the raw hook\n // cwd, so a group/sub-repo cwd doesn't run the hook in a sub-repo\n // directory or expose a sub-repo basename as $WORK_SESSION — matching\n // the notifyDesktop call above.\n runStatusHooks(\n kind,\n pty.cwd,\n path.basename(pty.cwd),\n config?.statusHooks,\n );\n }\n },\n });\n hookServerRef.current = hookServer;\n hookServer.start().catch(() => {});\n return () => {\n if (hookServerRef.current === hookServer) hookServerRef.current = null;\n hookServer.stop().catch(() => {});\n };\n }, [findPtyByCwd]);\n\n // Reliable cleanup that also runs on signal/crash exit (not just React\n // unmount). Disposes every live PTY (kills spawned `claude` processes) and\n // restores the injected Claude hook settings synchronously via\n // hookServer.cleanupSync(). Idempotent — guarded against double-run.\n useEffect(() => {\n let done = false;\n const cleanup = () => {\n if (done) return;\n done = true;\n try {\n for (const pty of ptySessions.current.values()) {\n try { pty.dispose(); } catch { /* already gone */ }\n }\n ptySessions.current.clear();\n notifiedSessionsRef.current.clear();\n } catch { /* best effort */ }\n // cleanupSync() is synchronous fs work, safe in a process 'exit' handler.\n try { hookServerRef.current?.cleanupSync(); } catch { /* best effort */ }\n };\n dashboardCleanup = cleanup;\n process.once('exit', cleanup);\n return () => {\n process.removeListener('exit', cleanup);\n if (dashboardCleanup === cleanup) dashboardCleanup = null;\n };\n }, []);\n\n /** Switch the right pane to show a different session's terminal. */\n const switchDisplay = useCallback((s: WorktreeSession) => {\n const k = sessionKey(s);\n if (activeKeyRef.current === k) return;\n\n termScrollBack.current = 0;\n if (activeKeyRef.current) {\n ptySessions.current.get(activeKeyRef.current)?.setOutputHandler(undefined);\n }\n\n setActiveKey(k);\n\n const pty = ptySessions.current.get(k);\n if (pty && !pty.exited) {\n pty.resize(termInner, contentHeight - 2);\n pty.setOutputHandler(() => scheduleTerminalRender(pty));\n try {\n setTermLines(renderBufferLines(pty.terminal.buffer.active, termInner, contentHeight - 2));\n } catch { /* */ }\n } else {\n setTermLines([]);\n }\n }, [termInner, contentHeight, scheduleTerminalRender]);\n\n /** Move cursor within the top (session/project) pane. */\n const moveSessionCursor = useCallback((delta: number) => {\n const max = countSelectable(topRowsRef.current) - 1;\n if (max < 0) return;\n const next = Math.max(0, Math.min(max, sessionCursorRef.current + delta));\n setSessionCursor(next);\n\n // Auto-switch display if in sessions mode\n if (topPaneModeRef.current === TopPaneMode.SESSIONS) {\n const row = cursorToRow(topRowsRef.current, next);\n if (row?.type === 'session') {\n switchDisplay(row.session);\n }\n }\n }, [switchDisplay]);\n\n /** Move cursor within the PR pane. */\n const movePrCursor = useCallback((delta: number) => {\n const max = countSelectable(prRowsRef.current) - 1;\n if (max < 0) return;\n const next = Math.max(0, Math.min(max, prCursorRef.current + delta));\n setPrCursor(next);\n }, []);\n\n /** Move cursor within the Jira pane. */\n const moveJiraCursor = useCallback((delta: number) => {\n const max = countSelectable(jiraRowsRef.current) - 1;\n if (max < 0) return;\n const next = Math.max(0, Math.min(max, jiraCursorRef.current + delta));\n setJiraCursor(next);\n }, []);\n\n const moveTaskCursor = useCallback((delta: number) => {\n const max = countSelectable(taskRowsRef.current) - 1;\n if (max < 0) return;\n const next = Math.max(0, Math.min(max, taskCursorRef.current + delta));\n setTaskCursor(next);\n }, []);\n\n /** Connect a PTY to the display and focus on it. */\n const connectPty = useCallback((key: string, pty: PtySession) => {\n if (activeKeyRef.current && activeKeyRef.current !== key) {\n ptySessions.current.get(activeKeyRef.current)?.setOutputHandler(undefined);\n }\n setActiveKey(key);\n setFocus(Focus.TERMINAL);\n pty.resize(termInner, contentHeight - 2);\n pty.setOutputHandler(() => scheduleTerminalRender(pty));\n try {\n setTermLines(renderBufferLines(pty.terminal.buffer.active, termInner, contentHeight - 2));\n } catch { /* */ }\n }, [termInner, contentHeight, scheduleTerminalRender]);\n\n /** Register a PTY: wire up exit/status handlers and track in the sessions map. */\n const registerPty = useCallback((key: string, pty: PtySession, exitMessage: string, onExitExtra?: () => void) => {\n ptySessions.current.set(key, pty);\n pty.onExit = (code: number) => {\n debug('onExit PTY', { key, code });\n // Evict the notification-dedup entry so a future PTY for the same dir\n // re-arms (and the Set doesn't grow unbounded across the session).\n notifiedSessionsRef.current.delete(ptyDedupKey(pty));\n pty.dispose();\n ptySessions.current.delete(key);\n if (activeKeyRef.current === key) {\n setActiveKey(null);\n setFocus(Focus.SESSIONS);\n setTermLines([]);\n setMessage(`${exitMessage} (code ${code})`);\n }\n setStatusVersion((v) => v + 1);\n onExitExtra?.();\n };\n pty.onStatusChange = () => setStatusVersion((v) => v + 1);\n setStatusVersion((v) => v + 1);\n }, []);\n\n const startPtyForSession = useCallback((s: WorktreeSession, key: string) => {\n const existing = s.paths.find((p) => fs.existsSync(p));\n if (!existing) {\n setMessage('Session path no longer exists');\n refreshSessions();\n return;\n }\n\n const dir = s.isGroup ? path.dirname(existing) : existing;\n const resume = hasClaudeConversation(dir);\n const tool = getAiTool(config ?? {});\n const pty = new PtySession(dir, termInner, contentHeight - 2, undefined, { tool, unsafe, resume, port: s.port });\n void upsertSession(s.target, s.isGroup, s.branch, s.paths).catch((err) =>\n setMessage(`Failed to save session: ${(err as Error).message}`),\n );\n registerPty(key, pty, `Session exited: ${s.target} / ${s.branch}`);\n return pty;\n }, [unsafe, termInner, contentHeight, refreshSessions, registerPty, config]);\n\n const activateSession = useCallback((s: WorktreeSession) => {\n const key = sessionKey(s);\n let pty = ptySessions.current.get(key);\n\n if (!pty || pty.exited) {\n pty = startPtyForSession(s, key);\n if (!pty) return;\n }\n connectPty(key, pty);\n }, [startPtyForSession, connectPty]);\n\n const syncSessionCursorToActive = useCallback(() => {\n if (!activeKeyRef.current) return;\n let selectableIdx = 0;\n for (const row of topRowsRef.current) {\n if (row.type === 'header') continue;\n if (row.type === 'session' && sessionKey(row.session) === activeKeyRef.current) {\n setSessionCursor(selectableIdx);\n return;\n }\n selectableIdx++;\n }\n }, []);\n\n /** Launch Claude directly in the base repo (no worktree). */\n const handleLaunchBaseRepo = useCallback((projectName: string) => {\n setTopPaneMode(TopPaneMode.SESSIONS);\n setSessionCursor(savedSessionCursor);\n\n if (!config) return;\n const repoPath = config.repos[projectName];\n if (!repoPath || !fs.existsSync(repoPath)) {\n setMessage(`Repo path not found for ${projectName}`);\n return;\n }\n\n const branch = '(base)';\n const key = `${projectName}:${branch}`;\n void upsertSession(projectName, false, branch, [repoPath]).catch((err) =>\n setMessage(`Failed to save session: ${(err as Error).message}`),\n );\n refreshSessions();\n\n const tool = getAiTool(config);\n const pty = new PtySession(repoPath, termInner, contentHeight - 2, undefined, { tool, unsafe });\n registerPty(key, pty, `Session exited: ${projectName} (base)`);\n connectPty(key, pty);\n setMessage(`Launched: ${projectName} (base repo)`);\n }, [unsafe, termInner, contentHeight, connectPty, refreshSessions, savedSessionCursor, config]);\n\n /** Create a worktree (visible in terminal pane) then launch Claude in it. */\n const handleCreateWorktree = useCallback((projectName: string, branchName: string, jiraIssue?: JiraIssue | null) => {\n setTopPaneMode(TopPaneMode.SESSIONS);\n setSessionCursor(savedSessionCursor);\n\n if (!config) { setMessage('No config loaded'); return; }\n\n const key = `${projectName}:${branchName}`;\n\n // Build prompt file for Jira issues\n let promptFile: string | undefined;\n if (jiraIssue) {\n const prompt = [\n `Read Jira issue ${jiraIssue.key} (${jiraIssue.url}) and determine how to handle it.`,\n '',\n '1. Read the Jira issue details to understand requirements, acceptance criteria, and context.',\n '',\n '2. Classify the issue:',\n ' - **One-time data task**: The issue asks for a data lookup, data fix, report, or one-off operation (e.g., \"find all X\", \"update Y records\", \"generate a report for Z\").',\n ' - **One-time script/query**: The issue asks for a SQL query, a script, or a one-off test client invocation — not a permanent code change.',\n ' - **Code change**: The issue requires modifying the codebase (bug fix, feature, refactor).',\n '',\n '3. Based on the classification:',\n '',\n ' **If one-time data task or script/query:**',\n ' - Generate the SQL query, script, or test client code directly.',\n ' - If a test client or similar tool exists in the codebase, use it.',\n ' - Present the output ready to run. Explain what it does and any parameters to adjust.',\n ' - Ask if I want to run it, modify it, or need something different.',\n '',\n ' **If code change:**',\n ' - Analyze the codebase to understand which files/components need changes, existing patterns, dependencies, and impact areas.',\n ' - Present a structured implementation plan:',\n '',\n ' **Summary**: Brief overview of what the issue requires.',\n '',\n ' **Affected Areas**: List the files, components, or modules that will need changes.',\n '',\n ' **Implementation Approach**:',\n ' - For simple issues (bug fixes, small features): provide a single clear approach with step-by-step details.',\n ' - For complex issues (new features, architectural changes): present 2-3 alternative approaches, each with Description, Pros, Cons, and Effort (Low/Medium/High). Include a Recommendation with reasoning.',\n '',\n ' **Key Considerations**: Security, performance, testing requirements, migration needs, backwards compatibility.',\n '',\n ' **Next Steps**: Ordered list of implementation tasks, ready to be executed.',\n '',\n ' - Ask if I want to proceed with the recommended approach, choose a different one, get more details, or make adjustments.',\n ].join('\\n');\n promptFile = path.join(os.tmpdir(), `work-prompt-${Date.now()}.txt`);\n fs.writeFileSync(promptFile, prompt, 'utf-8');\n }\n\n // Phase 1: Run `work tree --setup-only` in a PTY to show setup progress\n const setupArgs = ['tree', projectName, branchName, '--setup-only'];\n if (jiraIssue?.key) setupArgs.push('--jira-key', jiraIssue.key);\n const setupPty = new PtySession(process.cwd(), termInner, contentHeight - 2,\n { cmd: 'work', args: setupArgs });\n ptySessions.current.set(key, setupPty);\n setStatusVersion((v) => v + 1);\n\n setupPty.onExit = (code: number) => {\n setupPty.dispose();\n ptySessions.current.delete(key);\n\n if (code !== 0) {\n setMessage(`Failed to create worktree: ${projectName}/${branchName}`);\n if (activeKeyRef.current === key) {\n setActiveKey(null);\n setFocus(Focus.SESSIONS);\n setTermLines([]);\n }\n setStatusVersion((v) => v + 1);\n return;\n }\n\n // Phase 2: Setup succeeded — refresh sessions and launch Claude in the worktree\n refreshSessions();\n const sessions = getRecentSessions(loadHistory(), 50);\n const session = sessions.find((s) => s.target === projectName && s.branch === branchName);\n if (!session) {\n setMessage(`Worktree created but session not found: ${projectName}/${branchName}`);\n setStatusVersion((v) => v + 1);\n return;\n }\n\n const existing = session.paths.find((p) => fs.existsSync(p));\n if (!existing) {\n setMessage(`Worktree path not found after setup`);\n setStatusVersion((v) => v + 1);\n return;\n }\n\n const launchDir = session.isGroup ? path.dirname(existing) : existing;\n const tool = getAiTool(config);\n const aiPty = new PtySession(launchDir, termInner, contentHeight - 2, undefined,\n { tool, unsafe, promptFile, port: session.port });\n registerPty(key, aiPty, `Session exited: ${projectName} / ${branchName}`);\n connectPty(key, aiPty);\n setMessage(`Launched: ${projectName}/${branchName}`);\n };\n\n setMessage(`Creating: ${projectName}/${branchName}...`);\n connectPty(key, setupPty);\n }, [unsafe, termInner, contentHeight, refreshSessions, connectPty, registerPty, savedSessionCursor, config]);\n\n // Raw stdin input handling\n useEffect(() => {\n const handler = (data: Buffer) => {\n const key = data.toString('utf8');\n\n // Mouse wheel: scroll the pane under the cursor\n const mouseMatch = MOUSE_SGR_RE.exec(key);\n if (mouseMatch) {\n const button = parseInt(mouseMatch[1], 10);\n const col = parseInt(mouseMatch[2], 10);\n const row = parseInt(mouseMatch[3], 10);\n if (button === 64 || button === 65) {\n const delta = button === 64 ? -1 : 1;\n // Determine which pane the mouse is over based on column and row\n if (col <= sidebarWidth) {\n // Left column — determine which pane by row\n if (row <= sessionPaneHeight) {\n moveSessionCursor(delta);\n } else if (row <= sessionPaneHeight + prPaneHeight) {\n movePrCursor(delta);\n } else if (jiraPaneHeight > 0 && row <= sessionPaneHeight + prPaneHeight + jiraPaneHeight) {\n moveJiraCursor(delta);\n } else {\n moveTaskCursor(delta);\n }\n } else {\n // Right column (terminal) — scroll through scrollback buffer\n const pty = activeKeyRef.current ? ptySessions.current.get(activeKeyRef.current) : undefined;\n if (pty && !pty.exited) {\n const maxScroll = pty.terminal.buffer.active.baseY;\n const newScroll = Math.max(0, Math.min(maxScroll, termScrollBack.current - delta * 3));\n if (newScroll !== termScrollBack.current) {\n termScrollBack.current = newScroll;\n renderTermAtScroll();\n }\n }\n }\n return;\n }\n // Left click (button 0): focus the pane under cursor and select the clicked row\n if (button === 0) {\n if (col <= sidebarWidth) {\n // row is 1-indexed, each pane has a 1-row top border, so content starts at paneStart + 2\n if (row <= sessionPaneHeight) {\n setFocus(Focus.SESSIONS);\n const visualIdx = row - 2; // skip top border\n if (visualIdx >= 0) setSessionCursor(visualRowToCursor(topRowsRef.current, visualIdx));\n } else if (row <= sessionPaneHeight + prPaneHeight) {\n setFocus(Focus.PRS);\n const visualIdx = row - sessionPaneHeight - 2;\n if (visualIdx >= 0) setPrCursor(visualRowToCursor(prRowsRef.current, visualIdx));\n } else if (jiraPaneHeight > 0 && row <= sessionPaneHeight + prPaneHeight + jiraPaneHeight) {\n setFocus(Focus.JIRA);\n const visualIdx = row - sessionPaneHeight - prPaneHeight - 2;\n if (visualIdx >= 0) setJiraCursor(visualRowToCursor(jiraRowsRef.current, visualIdx));\n } else {\n setFocus(Focus.TASKS);\n const taskStart = sessionPaneHeight + prPaneHeight + jiraPaneHeight;\n const visualIdx = row - taskStart - 2;\n if (visualIdx >= 0) setTaskCursor(visualRowToCursor(taskRowsRef.current, visualIdx));\n }\n } else {\n const activePty = activeKeyRef.current ? ptySessions.current.get(activeKeyRef.current) : undefined;\n if (activePty && !activePty.exited) setFocus(Focus.TERMINAL);\n }\n setMessage('');\n return;\n }\n // Ignore other mouse events\n return;\n }\n\n // Branch input mode (top pane)\n if (branchInputRef.current) {\n if (key === '\\x1B' || key === '\\x03') {\n setBranchInput(null);\n setTopPaneMode(TopPaneMode.PROJECTS);\n setMessage('Esc to go back');\n return;\n }\n if (key === '\\r') {\n const { projectName, value, isGroup } = branchInputRef.current;\n setBranchInput(null);\n if (value.trim()) {\n handleCreateWorktree(projectName, value.trim());\n } else if (!isGroup) {\n handleLaunchBaseRepo(projectName);\n } else {\n setMessage('Branch name is required for groups');\n }\n return;\n }\n if (key === '\\x7F' || key === '\\b') {\n setBranchInput((prev) => prev ? { ...prev, value: prev.value.slice(0, -1) } : null);\n return;\n }\n if (key.charCodeAt(0) < 32) return;\n setBranchInput((prev) => prev ? { ...prev, value: prev.value + key } : null);\n return;\n }\n\n // Tab: cycle focus SESSIONS → PRS → JIRA → TERMINAL → SESSIONS\n if (key === TAB_KEY) {\n if (topPaneModeRef.current !== TopPaneMode.SESSIONS) return;\n const activePty = activeKeyRef.current ? ptySessions.current.get(activeKeyRef.current) : undefined;\n const hasTerminal = activePty && !activePty.exited;\n\n if (focusRef.current === Focus.SESSIONS) {\n setFocus(Focus.PRS);\n } else if (focusRef.current === Focus.PRS) {\n setFocus(acliAvailableRef.current ? Focus.JIRA : Focus.TASKS);\n } else if (focusRef.current === Focus.JIRA) {\n setFocus(Focus.TASKS);\n } else if (focusRef.current === Focus.TASKS) {\n if (hasTerminal) setFocus(Focus.TERMINAL);\n else { setFocus(Focus.SESSIONS); syncSessionCursorToActive(); }\n } else {\n setFocus(Focus.SESSIONS);\n syncSessionCursorToActive();\n }\n setMessage('');\n return;\n }\n\n // Terminal mode\n if (focusRef.current === Focus.TERMINAL) {\n if (key === DETACH_KEY) {\n setFocus(Focus.SESSIONS);\n syncSessionCursorToActive();\n setMessage('');\n return;\n }\n // Reset scroll to bottom when typing\n termScrollBack.current = 0;\n ptySessions.current.get(activeKeyRef.current!)?.write(key);\n return;\n }\n\n // --- Left pane navigation ---\n setMessage('');\n\n // Global sync (G = shift+g) from any left pane\n if (key === 'G') {\n if (syncing) return;\n setSyncing(true);\n setMessage('Syncing all...');\n (async () => {\n if (config) {\n await Promise.all(\n Object.values(config.repos).map((repoPath) =>\n fetchRemoteAsync(repoPath).catch(() => {}),\n ),\n );\n }\n refreshSessions();\n refreshPrs();\n refreshJira();\n refreshTasks();\n computeConflictsAndMerged(sessionsRef.current);\n setSyncing(false);\n setMessage('All synced');\n })();\n return;\n }\n\n // Project picker mode (top pane)\n if (topPaneModeRef.current === TopPaneMode.PROJECTS) {\n if (key === '\\x1B' || key === '\\x03' || key === 'q') {\n setTopPaneMode(TopPaneMode.SESSIONS);\n setSessionCursor(savedSessionCursor);\n setPendingBranch(null);\n setPendingJiraIssue(null);\n setPendingTask(null);\n setMessage('');\n return;\n }\n if (key === '\\x1B[A' || key === 'k') { moveSessionCursor(-1); return; }\n if (key === '\\x1B[B' || key === 'j') { moveSessionCursor(1); return; }\n if (key === '\\r') {\n const row = cursorToRow(topRowsRef.current, sessionCursorRef.current);\n if (row?.type === 'project') {\n const issue = pendingJiraIssueRef.current;\n const task = pendingTaskRef.current;\n if (issue) {\n // Jira flow: generate slug then create worktree in PTY\n setPendingJiraIssue(null);\n setMessage(`Generating branch name for ${issue.key}...`);\n const keyLower = issue.key.toLowerCase();\n generateSlug(issue.summary).then((slug) => {\n const branchName = `task/${keyLower}-${slug}`;\n handleCreateWorktree(row.name, branchName, issue);\n });\n } else if (task) {\n // Task flow: generate slug from task text\n setPendingTask(null);\n setMessage(`Generating branch name for task #${task.id}...`);\n generateSlug(task.text).then((slug) => {\n const branchName = `todo/${slug}`;\n handleCreateWorktree(row.name, branchName);\n });\n } else if (pendingBranchRef.current) {\n const branch = pendingBranchRef.current;\n setPendingBranch(null);\n handleCreateWorktree(row.name, branch);\n } else {\n setTopPaneMode(TopPaneMode.BRANCH_INPUT);\n setBranchInput({ projectName: row.name, value: '', isGroup: row.isGroup });\n setMessage(row.isGroup\n ? 'Type branch name, Enter to create, Esc to cancel'\n : 'Type branch name (empty for base repo), Enter to create, Esc to cancel');\n }\n }\n return;\n }\n return;\n }\n\n // Tasks pane focused\n if (focusRef.current === Focus.TASKS) {\n // Task input mode\n if (taskInputRef.current !== null) {\n if (key === '\\x1B' || key === '\\x03') {\n setTaskInput(null);\n setTaskEditId(null);\n setMessage('');\n return;\n }\n if (key === '\\r') {\n const text = taskInputRef.current.trim();\n const editId = taskEditIdRef.current;\n setTaskInput(null);\n setTaskEditId(null);\n if (text) {\n if (editId !== null) {\n void editTask(editId, text).then(() => refreshTasks());\n setMessage(`Updated task #${editId}`);\n } else {\n void addTask(text).then(() => refreshTasks());\n setMessage(`Added task: ${text}`);\n }\n }\n return;\n }\n if (key === '\\x7F' || key === '\\b') {\n setTaskInput((prev) => prev !== null ? prev.slice(0, -1) : null);\n return;\n }\n if (key.charCodeAt(0) < 32) return;\n setTaskInput((prev) => prev !== null ? prev + key : null);\n return;\n }\n\n if (key === '\\x1B[A' || key === 'k') { moveTaskCursor(-1); return; }\n if (key === '\\x1B[B' || key === 'j') { moveTaskCursor(1); return; }\n\n // Toggle done/undone\n if (key === '\\r' || key === 'x') {\n const row = cursorToRow(taskRowsRef.current, taskCursorRef.current);\n if (row?.type === 'task') {\n const p = row.task.done\n ? uncompleteTask(row.task.id)\n : completeTask(row.task.id);\n void p.then(() => refreshTasks());\n }\n return;\n }\n\n // Add new task\n if (key === 'a') {\n setTaskInput('');\n setMessage('Type task, Enter to add, Esc to cancel');\n return;\n }\n\n // Edit task\n if (key === 'e') {\n const row = cursorToRow(taskRowsRef.current, taskCursorRef.current);\n if (row?.type === 'task') {\n setTaskEditId(row.task.id);\n setTaskInput(row.task.text);\n setMessage('Edit task, Enter to save, Esc to cancel');\n }\n return;\n }\n\n // Create worktree for task\n if (key === 'w') {\n const row = cursorToRow(taskRowsRef.current, taskCursorRef.current);\n if (row?.type === 'task') {\n setPendingTask(row.task);\n setSavedSessionCursor(sessionCursorRef.current);\n setFocus(Focus.SESSIONS);\n setTopPaneMode(TopPaneMode.PROJECTS);\n setSessionCursor(0);\n setMessage(`Select project for task #${row.task.id}`);\n }\n return;\n }\n\n // Remove task\n if (key === 'd') {\n const row = cursorToRow(taskRowsRef.current, taskCursorRef.current);\n if (row?.type === 'task') {\n void removeTask(row.task.id).then(() => refreshTasks());\n setMessage(`Removed: ${row.task.text}`);\n }\n return;\n }\n\n if (key === 'g') {\n refreshTasks();\n setMessage('Tasks refreshed');\n return;\n }\n\n if (key === '\\x03' || key === 'q') {\n for (const pty of ptySessions.current.values()) pty.dispose();\n ptySessions.current.clear();\n onExit();\n return;\n }\n\n return;\n }\n\n // Jira pane focused\n if (focusRef.current === Focus.JIRA) {\n if (key === '\\x1B[A' || key === 'k') { moveJiraCursor(-1); return; }\n if (key === '\\x1B[B' || key === 'j') { moveJiraCursor(1); return; }\n\n if (key === '\\r') {\n const row = cursorToRow(jiraRowsRef.current, jiraCursorRef.current);\n if (row?.type === 'jira') {\n const issue = row.issue;\n\n // Check if any existing session is linked to this Jira issue\n const existing = sessionsRef.current.find((s) =>\n s.jiraKey === issue.key,\n );\n if (existing) {\n setFocus(Focus.SESSIONS);\n activateSession(existing);\n setMessage(`Resumed: ${existing.target}/${existing.branch}`);\n } else {\n // Show project picker immediately; slug generation happens after selection\n setPendingJiraIssue(issue);\n setSavedSessionCursor(sessionCursorRef.current);\n setFocus(Focus.SESSIONS);\n setTopPaneMode(TopPaneMode.PROJECTS);\n setSessionCursor(0);\n setMessage(`Select project for ${issue.key}`);\n }\n }\n return;\n }\n\n if (key === 'o') {\n const row = cursorToRow(jiraRowsRef.current, jiraCursorRef.current);\n if (row?.type === 'jira' && row.issue.url) {\n openUrl(row.issue.url);\n setMessage(`Opened: ${row.issue.key}`);\n }\n return;\n }\n\n if (key === '\\x03' || key === 'q') {\n for (const pty of ptySessions.current.values()) pty.dispose();\n ptySessions.current.clear();\n onExit();\n return;\n }\n\n if (key === 'g') {\n if (syncing) return;\n setSyncing(true);\n setMessage('Syncing Jira...');\n (async () => {\n await refreshJira();\n setSyncing(false);\n setMessage('Jira synced');\n })();\n return;\n }\n\n return;\n }\n\n // PR pane focused\n if (focusRef.current === Focus.PRS) {\n if (key === '\\x1B[A' || key === 'k') { movePrCursor(-1); return; }\n if (key === '\\x1B[B' || key === 'j') { movePrCursor(1); return; }\n\n if (key === '\\r') {\n const row = cursorToRow(prRowsRef.current, prCursorRef.current);\n if (row?.type === 'pr' && config) {\n const pr = row.pr;\n const projectName = repoToProject(pr.repoAlias, config);\n\n // Check if any session already has this branch (regardless of target name)\n const existing = sessionsRef.current.find(\n (s) => s.branch === pr.branch,\n );\n if (existing) {\n setFocus(Focus.SESSIONS);\n activateSession(existing);\n setMessage(`Resumed: ${existing.target}/${pr.branch}`);\n } else {\n handleCreateWorktree(projectName, pr.branch);\n }\n }\n return;\n }\n\n if (key === 'o') {\n const row = cursorToRow(prRowsRef.current, prCursorRef.current);\n if (row?.type === 'pr' && row.pr.url) {\n openUrl(row.pr.url);\n setMessage(`Opened: ${row.pr.title}`);\n }\n return;\n }\n\n if (key === '\\x03' || key === 'q') {\n for (const pty of ptySessions.current.values()) pty.dispose();\n ptySessions.current.clear();\n onExit();\n return;\n }\n\n if (key === 'g') {\n if (syncing) return;\n setSyncing(true);\n setMessage('Syncing PRs...');\n (async () => {\n await refreshPrs();\n setSyncing(false);\n setMessage('PRs synced');\n })();\n return;\n }\n\n return;\n }\n\n // Sessions pane focused\n if (key === '\\x03' || key === 'q') {\n for (const pty of ptySessions.current.values()) pty.dispose();\n ptySessions.current.clear();\n onExit();\n return;\n }\n\n if (key === '\\x1B[A' || key === 'k') { moveSessionCursor(-1); return; }\n if (key === '\\x1B[B' || key === 'j') { moveSessionCursor(1); return; }\n\n if (key === '\\r') {\n const row = cursorToRow(topRowsRef.current, sessionCursorRef.current);\n if (row?.type === 'session') {\n activateSession(row.session);\n }\n return;\n }\n\n if (key === 'd') {\n const row = cursorToRow(topRowsRef.current, sessionCursorRef.current);\n if (row?.type !== 'session') return;\n\n const s = row.session;\n const k = sessionKey(s);\n const pty = ptySessions.current.get(k);\n if (pty) {\n notifiedSessionsRef.current.delete(ptyDedupKey(pty));\n pty.dispose();\n ptySessions.current.delete(k);\n }\n if (activeKeyRef.current === k) {\n setActiveKey(null);\n setTermLines([]);\n }\n setMessage(`Removing: ${s.target} / ${s.branch}...`);\n\n // Spawn work remove in a PTY so output shows in the right pane\n const removeKey = `remove:${s.target}:${s.branch}`;\n const removePty = new PtySession(\n process.cwd(),\n termInner,\n contentHeight - 2,\n { cmd: 'work', args: ['remove', s.target, s.branch, '--force'] },\n );\n registerPty(removeKey, removePty, `Removed: ${s.target} / ${s.branch}`, () => {\n refreshSessions();\n setMessage(`Removed: ${s.target} / ${s.branch}`);\n });\n connectPty(removeKey, removePty);\n return;\n }\n\n if (key === 'n') {\n setSavedSessionCursor(sessionCursorRef.current);\n setTopPaneMode(TopPaneMode.PROJECTS);\n setSessionCursor(0);\n setMessage('Select project, Esc to cancel');\n return;\n }\n\n if (key === '.') {\n const row = cursorToRow(topRowsRef.current, sessionCursorRef.current);\n if (row?.type !== 'session') return;\n const s = row.session;\n const existing = s.paths.find((p) => fs.existsSync(p));\n if (!existing) { setMessage('Session path no longer exists'); return; }\n const dir = s.isGroup ? path.dirname(existing) : existing;\n const editor = config?.editor ?? 'code';\n spawn(editor, [dir], { detached: true, stdio: 'ignore' }).unref();\n setMessage(`Opened in ${editor}: ${s.branch}`);\n return;\n }\n\n if (key === 'u') {\n const row = cursorToRow(topRowsRef.current, sessionCursorRef.current);\n if (row?.type !== 'session') return;\n const s = row.session;\n const existing = s.paths.find((p) => fs.existsSync(p));\n if (!existing) { setMessage('Session path no longer exists'); return; }\n setMessage(`Rebasing ${s.branch}...`);\n const err = rebaseOntoMain(s.branch, existing);\n if (err) {\n setMessage(`Rebase failed: ${err}`);\n } else {\n setMessage(`Rebased ${s.branch} onto main`);\n computeConflictsAndMerged(sessionsRef.current);\n }\n return;\n }\n\n if (key === 'g') {\n if (syncing) return;\n setSyncing(true);\n setMessage('Syncing sessions...');\n (async () => {\n if (config) {\n await Promise.all(\n Object.values(config.repos).map((repoPath) =>\n fetchRemoteAsync(repoPath).catch(() => {}),\n ),\n );\n }\n refreshSessions();\n refreshPrs();\n computeConflictsAndMerged(sessionsRef.current);\n setSyncing(false);\n setMessage('Sessions synced');\n })();\n return;\n }\n\n if (key === 'r') {\n refreshSessions();\n refreshPrs();\n refreshJira();\n refreshTasks();\n if (!ghAvailable) setMessage('Refreshed');\n return;\n }\n };\n\n process.stdin.on('data', handler);\n return () => { process.stdin.removeListener('data', handler); };\n }, [onExit, activateSession, refreshSessions, refreshPrs, refreshJira, refreshTasks, moveSessionCursor, movePrCursor, moveJiraCursor, moveTaskCursor, syncSessionCursorToActive, handleCreateWorktree, savedSessionCursor, config, computeConflictsAndMerged, syncing, ghAvailable, prMap]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // Resize handling\n useEffect(() => {\n const handler = () => {\n const newCols = process.stdout.columns || 80;\n const newRows = process.stdout.rows || 24;\n setDims({ cols: newCols, rows: newRows });\n if (activeKeyRef.current) {\n const pty = ptySessions.current.get(activeKeyRef.current);\n if (pty && !pty.exited) {\n const newTermInner = newCols - Math.min(60, Math.floor(newCols * 0.525)) - 2;\n const newContentHeight = newRows - 3;\n pty.resize(newTermInner, newContentHeight);\n }\n }\n };\n process.stdout.on('resize', handler);\n return () => { process.stdout.removeListener('resize', handler); };\n }, []);\n\n // Periodically clear scrollback for non-active PTYs to free memory\n useEffect(() => {\n const interval = setInterval(() => {\n for (const [key, pty] of ptySessions.current) {\n if (pty.exited) continue;\n if (key !== activeKeyRef.current) {\n pty.resetTerminal();\n }\n }\n }, 5 * 60 * 1000);\n return () => clearInterval(interval);\n }, []);\n\n const statusMap = buildStatusMap();\n\n const placeholder = !activeKey\n ? 'Select a session and press Enter'\n : 'Press Enter to start session';\n\n return (\n <Box flexDirection=\"column\" width={cols} height={rows}>\n <Box flexDirection=\"row\" height={contentHeight}>\n <Box flexDirection=\"column\" width={sidebarWidth}>\n <Sidebar\n sidebarRows={topRows}\n cursor={sessionCursor}\n focused={focus === Focus.SESSIONS}\n statusMap={statusMap}\n conflictCounts={conflictCounts}\n mergedSet={mergedSet}\n prMap={prMap}\n activeKey={activeKey}\n width={sidebarWidth}\n height={sessionPaneHeight}\n branchInput={branchInput}\n />\n <PrPane\n prRows={prRows}\n cursor={prCursor}\n focused={focus === Focus.PRS}\n localBranches={localBranches}\n width={sidebarWidth}\n height={prPaneHeight}\n />\n {jiraPaneHeight > 0 && (\n <JiraPane\n jiraRows={jiraRows}\n cursor={jiraCursor}\n focused={focus === Focus.JIRA}\n width={sidebarWidth}\n height={jiraPaneHeight}\n />\n )}\n <TaskPane\n taskRows={taskRows}\n cursor={taskCursor}\n focused={focus === Focus.TASKS}\n width={sidebarWidth}\n height={taskPaneHeight}\n taskInput={taskInput}\n />\n </Box>\n <TerminalPane\n lines={termLines}\n width={termWidth}\n height={contentHeight}\n focused={focus === Focus.TERMINAL}\n placeholder={placeholder}\n title=\"Terminal\"\n />\n </Box>\n <StatusBar message={message} pane={focus === Focus.TERMINAL ? 'terminal' : focus === Focus.PRS ? 'prs' : focus === Focus.JIRA ? 'jira' : focus === Focus.TASKS ? 'tasks' : 'sessions'} syncing={syncing} />\n </Box>\n );\n}\n","import { execFile } from 'node:child_process';\n\nexport interface PullRequestInfo {\n number: number;\n title: string;\n branch: string;\n url: string;\n isDraft: boolean;\n checksStatus: 'SUCCESS' | 'FAILURE' | 'PENDING' | 'NONE';\n reviewDecision: 'APPROVED' | 'CHANGES_REQUESTED' | 'REVIEW_REQUIRED' | 'NONE';\n /** Current user's latest review state on this PR. */\n myReview: 'APPROVED' | 'CHANGES_REQUESTED' | 'COMMENTED' | 'NONE';\n /** Whether the current user is the PR author. */\n isMine: boolean;\n /** Repo alias this PR belongs to (for distinguishing group PRs). */\n repoAlias: string;\n}\n\n/**\n * Map from branch name to array of PRs (one per repo that has a PR for that branch).\n * Single-repo sessions will have at most 1 entry; groups can have multiple.\n */\nexport type BranchPrMap = Map<string, PullRequestInfo[]>;\n\nfunction execAsync(cmd: string, args: string[], cwd: string, timeout: number): Promise<string> {\n return new Promise((resolve, reject) => {\n execFile(cmd, args, { cwd, encoding: 'utf-8', timeout }, (err, stdout) => {\n if (err) reject(err);\n else resolve(stdout ?? '');\n });\n });\n}\n\nfunction parsePrJson(stdout: string, repoAlias: string, currentUser: string): PullRequestInfo[] {\n const prs: any[] = JSON.parse(stdout);\n const results: PullRequestInfo[] = [];\n\n for (const pr of prs) {\n let checksStatus: PullRequestInfo['checksStatus'] = 'NONE';\n const checks: any[] = pr.statusCheckRollup ?? [];\n if (checks.length > 0) {\n const hasFailure = checks.some((c: any) =>\n c.conclusion === 'FAILURE' || c.conclusion === 'TIMED_OUT' || c.conclusion === 'CANCELLED',\n );\n const hasPending = checks.some((c: any) =>\n c.status === 'IN_PROGRESS' || c.status === 'QUEUED' || c.status === 'PENDING',\n );\n if (hasFailure) checksStatus = 'FAILURE';\n else if (hasPending) checksStatus = 'PENDING';\n else checksStatus = 'SUCCESS';\n }\n\n // Merge conflict overrides to failure\n if (pr.mergeable === 'CONFLICTING') checksStatus = 'FAILURE';\n\n let reviewDecision: PullRequestInfo['reviewDecision'] = 'NONE';\n if (pr.reviewDecision === 'APPROVED') reviewDecision = 'APPROVED';\n else if (pr.reviewDecision === 'CHANGES_REQUESTED') reviewDecision = 'CHANGES_REQUESTED';\n else if (pr.reviewDecision === 'REVIEW_REQUIRED') reviewDecision = 'REVIEW_REQUIRED';\n\n // Check current user's latest review state\n let myReview: PullRequestInfo['myReview'] = 'NONE';\n if (currentUser) {\n const reviews: any[] = pr.reviews ?? [];\n for (let i = reviews.length - 1; i >= 0; i--) {\n if (reviews[i].author?.login?.toLowerCase() === currentUser.toLowerCase()) {\n const state = reviews[i].state;\n if (state === 'APPROVED') myReview = 'APPROVED';\n else if (state === 'CHANGES_REQUESTED') myReview = 'CHANGES_REQUESTED';\n else if (state === 'COMMENTED') myReview = 'COMMENTED';\n break;\n }\n }\n }\n\n results.push({\n number: pr.number,\n title: pr.title,\n branch: pr.headRefName,\n url: pr.url,\n isDraft: pr.isDraft ?? false,\n checksStatus,\n reviewDecision,\n myReview,\n isMine: currentUser ? pr.author?.login?.toLowerCase() === currentUser.toLowerCase() : false,\n repoAlias,\n });\n }\n\n return results;\n}\n\n/**\n * Fetch open PRs for a repo using `gh` CLI (async, non-blocking).\n */\nasync function fetchPullRequests(repoPath: string, repoAlias: string, currentUser: string): Promise<PullRequestInfo[]> {\n try {\n const stdout = await execAsync(\n 'gh',\n [\n 'pr', 'list',\n '--state', 'open',\n '--json', 'number,title,headRefName,url,isDraft,statusCheckRollup,reviewDecision,reviews,mergeable,author',\n '--limit', '100',\n ],\n repoPath,\n 15000,\n );\n if (!stdout) return [];\n return parsePrJson(stdout, repoAlias, currentUser);\n } catch {\n return [];\n }\n}\n\n/**\n * Fetch PRs for all configured repos (async, non-blocking).\n * Runs all repo fetches in parallel.\n * Returns a map from branch name → array of PRs across repos.\n */\nasync function getCurrentUser(): Promise<string> {\n try {\n const stdout = await execAsync('gh', ['api', 'user', '--jq', '.login'], process.cwd(), 5000);\n return stdout.trim();\n } catch {\n return '';\n }\n}\n\nexport async function fetchAllPullRequests(repos: Record<string, string>): Promise<BranchPrMap> {\n const currentUser = await getCurrentUser();\n const entries = Object.entries(repos);\n const results = await Promise.all(\n entries.map(([alias, repoPath]) => fetchPullRequests(repoPath, alias, currentUser)),\n );\n\n const map: BranchPrMap = new Map();\n for (const prList of results) {\n for (const pr of prList) {\n const existing = map.get(pr.branch);\n if (existing) {\n existing.push(pr);\n } else {\n map.set(pr.branch, [pr]);\n }\n }\n }\n\n return map;\n}\n\n/** Check if `gh` CLI is available and authenticated (async). */\nexport async function isGhAvailable(): Promise<boolean> {\n try {\n await execAsync('gh', ['auth', 'status'], process.cwd(), 5000);\n return true;\n } catch {\n return false;\n }\n}\n","import { execFile } from 'node:child_process';\r\n\r\nexport interface JiraIssue {\r\n key: string;\r\n summary: string;\r\n status: string;\r\n issuetype: string;\r\n priority: string;\r\n url: string;\r\n}\r\n\r\nfunction execAsync(cmd: string, args: string[], timeout: number): Promise<string> {\r\n return new Promise((resolve, reject) => {\r\n execFile(cmd, args, { encoding: 'utf-8', timeout }, (err, stdout) => {\r\n if (err) reject(err);\r\n else resolve(stdout ?? '');\r\n });\r\n });\r\n}\r\n\r\n/** Check if `acli` is available and authenticated. Kept for the TUI's\r\n * pre-flight; the web pane uses fetchMyJiraIssues directly which short-\r\n * circuits on auth failure too. */\r\nexport async function isAcliAvailable(): Promise<boolean> {\r\n try {\r\n await execAsync('acli', ['jira', 'auth', 'status'], 5000);\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/** Run `acli jira auth status` once and parse it for both availability\r\n * AND the site URL. Replaces the previous two-call pattern where\r\n * `isAcliAvailable` and `getJiraSiteUrl` each shelled out independently. */\r\nasync function probeAcli(): Promise<{ available: boolean; siteUrl: string }> {\r\n try {\r\n const stdout = await execAsync('acli', ['jira', 'auth', 'status'], 5000);\r\n const match = stdout.match(/Site:\\s+(\\S+)/);\r\n return {\r\n available: true,\r\n siteUrl: match ? `https://${match[1]}` : '',\r\n };\r\n } catch {\r\n return { available: false, siteUrl: '' };\r\n }\r\n}\r\n\r\nfunction parseIssuesJson(stdout: string, siteUrl: string): JiraIssue[] {\r\n const parsed = JSON.parse(stdout);\r\n const issues: any[] = parsed.issues ?? parsed ?? [];\r\n\r\n return issues.map((issue: any) => {\r\n const fields = issue.fields ?? {};\r\n return {\r\n key: issue.key ?? '',\r\n summary: fields.summary ?? '',\r\n status: fields.status?.name ?? '',\r\n issuetype: fields.issuetype?.name ?? '',\r\n priority: fields.priority?.name ?? '',\r\n url: siteUrl ? `${siteUrl}/browse/${issue.key}` : '',\r\n };\r\n });\r\n}\r\n\r\nasync function searchMyIssues(siteUrl: string): Promise<JiraIssue[]> {\r\n try {\r\n const stdout = await execAsync(\r\n 'acli',\r\n [\r\n 'jira', 'workitem', 'search',\r\n '--jql', 'assignee = currentUser() AND resolution = Unresolved AND status NOT IN (Archived, Done) ORDER BY updated DESC',\r\n '--json',\r\n '--limit', '50',\r\n ],\r\n 15000,\r\n );\r\n if (!stdout) return [];\r\n return parseIssuesJson(stdout, siteUrl);\r\n } catch {\r\n return [];\r\n }\r\n}\r\n\r\n/**\r\n * Fetch Jira issues assigned to the current user.\r\n *\r\n * Backwards-compatible signature for the TUI — returns an empty array\r\n * when acli is unavailable or errors. Web callers that need to\r\n * distinguish unavailable from empty should use `fetchJiraPane()`.\r\n */\r\nexport async function fetchMyJiraIssues(): Promise<JiraIssue[]> {\r\n const probe = await probeAcli();\r\n if (!probe.available) return [];\r\n return searchMyIssues(probe.siteUrl);\r\n}\r\n\r\n/**\r\n * Combined availability check + issue fetch in one acli probe. Used by\r\n * the dashboard's Jira pane so a refresh only spawns `acli jira auth\r\n * status` once instead of twice. The pane needs `available` separately\r\n * from `issues` so it can render the \"acli not configured\" hint.\r\n */\r\nexport async function fetchJiraPane(): Promise<{\r\n available: boolean;\r\n issues: JiraIssue[];\r\n}> {\r\n const probe = await probeAcli();\r\n if (!probe.available) return { available: false, issues: [] };\r\n const issues = await searchMyIssues(probe.siteUrl);\r\n return { available: true, issues };\r\n}\r\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { getConfigDir } from './config.js';\nimport { atomicWriteFile, ensureFile, withFileLock } from './fs-safe.js';\n\nexport interface Task {\n id: number;\n text: string;\n done: boolean;\n createdAt: string;\n doneAt?: string;\n /** Optional link to a worktree session (target:branch). */\n link?: string;\n}\n\ninterface TaskStore {\n nextId: number;\n tasks: Task[];\n}\n\nconst EMPTY_STORE: TaskStore = { nextId: 1, tasks: [] };\n\nfunction getTasksPath(): string {\n return path.join(getConfigDir(), 'tasks.json');\n}\n\nfunction loadStore(): TaskStore {\n const p = getTasksPath();\n if (!fs.existsSync(p)) return { ...EMPTY_STORE };\n try {\n const parsed = JSON.parse(fs.readFileSync(p, 'utf-8'));\n if (!parsed || typeof parsed !== 'object' || !Array.isArray(parsed.tasks)) {\n return { ...EMPTY_STORE };\n }\n return parsed;\n } catch {\n return { ...EMPTY_STORE };\n }\n}\n\nfunction saveStore(store: TaskStore): void {\n atomicWriteFile(getTasksPath(), JSON.stringify(store, null, 2));\n}\n\n/**\n * Serialize read-modify-write against other work processes so concurrent\n * `work todo` / TUI edits can't clobber each other.\n */\nasync function withTasksLock<T>(fn: () => T): Promise<T> {\n const tasksPath = getTasksPath();\n ensureFile(tasksPath, JSON.stringify(EMPTY_STORE, null, 2));\n return withFileLock(tasksPath, fn);\n}\n\nexport function getTasks(): Task[] {\n return loadStore().tasks;\n}\n\nexport async function addTask(text: string, link?: string): Promise<Task> {\n return withTasksLock(() => {\n const store = loadStore();\n const task: Task = {\n id: store.nextId++,\n text,\n done: false,\n createdAt: new Date().toISOString(),\n link,\n };\n store.tasks.push(task);\n saveStore(store);\n return task;\n });\n}\n\nexport async function completeTask(id: number): Promise<Task | null> {\n return withTasksLock(() => {\n const store = loadStore();\n const task = store.tasks.find((t) => t.id === id);\n if (!task) return null;\n task.done = true;\n task.doneAt = new Date().toISOString();\n saveStore(store);\n return task;\n });\n}\n\nexport async function uncompleteTask(id: number): Promise<Task | null> {\n return withTasksLock(() => {\n const store = loadStore();\n const task = store.tasks.find((t) => t.id === id);\n if (!task) return null;\n task.done = false;\n task.doneAt = undefined;\n saveStore(store);\n return task;\n });\n}\n\nexport async function removeTask(id: number): Promise<Task | null> {\n return withTasksLock(() => {\n const store = loadStore();\n const idx = store.tasks.findIndex((t) => t.id === id);\n if (idx === -1) return null;\n const [removed] = store.tasks.splice(idx, 1);\n saveStore(store);\n return removed;\n });\n}\n\nexport async function editTask(id: number, text: string): Promise<Task | null> {\n return withTasksLock(() => {\n const store = loadStore();\n const task = store.tasks.find((t) => t.id === id);\n if (!task) return null;\n task.text = text;\n saveStore(store);\n return task;\n });\n}\n\nexport function getTasksPath_(): string {\n return getTasksPath();\n}\n","import pty, { type IPty } from 'node-pty';\nimport xtermHeadless from '@xterm/headless';\nimport { debug } from '../core/logger.js';\nimport { buildAiLaunchArgs, type AiToolSpec } from '../core/ai-launcher.js';\n\nconst { Terminal } = xtermHeadless;\n\nexport type SessionStatus = 'stopped' | 'running' | 'idle';\n\nexport interface PtyAiOptions {\n /** Resolved AI tool spec (from `getAiTool(config)`). */\n tool: AiToolSpec;\n unsafe?: boolean;\n resume?: boolean;\n promptFile?: string;\n /** Dev-server port exposed to the launched process as $PORT. */\n port?: number;\n}\n\nexport class PtySession {\n readonly pty: IPty;\n terminal: InstanceType<typeof Terminal>;\n readonly cwd: string;\n private outputHandler?: (data: string) => void;\n private _exited = false;\n private _idle = true;\n private _outputBuffer = '';\n private _loggedOutput = false;\n onExit?: (code: number) => void;\n onStatusChange?: () => void;\n\n constructor(\n cwd: string,\n cols: number,\n rows: number,\n command?: { cmd: string; args: string[] },\n aiOptions?: PtyAiOptions,\n ) {\n this.cwd = cwd;\n this.terminal = new Terminal({\n cols,\n rows,\n scrollback: 200,\n allowProposedApi: true,\n });\n\n const isWindows = process.platform === 'win32';\n\n let spawnCmd: string;\n let spawnArgs: string[];\n\n if (command) {\n // Custom command (e.g. work tree)\n spawnCmd = isWindows ? 'cmd.exe' : command.cmd;\n spawnArgs = isWindows ? ['/c', command.cmd, ...command.args] : command.args;\n } else if (aiOptions) {\n // Launch configured AI tool (default: claude)\n const { cmd, args } = buildAiLaunchArgs(aiOptions.tool, {\n unsafe: aiOptions.unsafe,\n resume: aiOptions.resume,\n promptFile: aiOptions.promptFile,\n });\n spawnCmd = isWindows ? 'cmd.exe' : cmd;\n spawnArgs = isWindows ? ['/c', cmd, ...args] : args;\n } else {\n throw new Error('PtySession requires either a custom command or aiOptions');\n }\n\n const env: Record<string, string> = Object.fromEntries(\n Object.entries(process.env).filter((e): e is [string, string] => e[1] != null),\n );\n if (aiOptions?.port !== undefined) {\n env.PORT = String(aiOptions.port);\n }\n\n debug('PtySession spawn', { spawnCmd, spawnArgs, cwd, cols, rows });\n this.pty = pty.spawn(spawnCmd, spawnArgs, {\n name: 'xterm-256color',\n cwd,\n cols,\n rows,\n env,\n });\n debug('PtySession spawned pid=', this.pty.pid);\n\n this.pty.onData((data) => {\n this.terminal.write(data);\n this.outputHandler?.(data);\n // Log first 500 chars of PTY output for debugging early exits\n if (!this._loggedOutput) {\n this._outputBuffer = (this._outputBuffer || '') + data;\n if (this._outputBuffer.length > 500) {\n debug('PtySession first output', { cwd, output: this._outputBuffer.slice(0, 500) });\n this._loggedOutput = true;\n this._outputBuffer = '';\n }\n }\n });\n\n this.pty.onExit(({ exitCode }) => {\n if (!this._loggedOutput && this._outputBuffer) {\n debug('PtySession output before exit', { cwd, output: this._outputBuffer.slice(0, 500) });\n }\n this._outputBuffer = '';\n debug('PtySession exited', { cwd, exitCode });\n this._exited = true;\n this.onExit?.(exitCode);\n });\n }\n\n get exited() {\n return this._exited;\n }\n\n get idle() {\n return this._idle;\n }\n\n /** Called by the dashboard when a hook event indicates idle state change. */\n setIdle(idle: boolean) {\n if (this._exited || this._idle === idle) return;\n this._idle = idle;\n this.onStatusChange?.();\n }\n\n write(data: string) {\n if (!this._exited) {\n try { this.pty.write(data); } catch { /* PTY already exited */ }\n }\n }\n\n resize(cols: number, rows: number) {\n if (!this._exited) {\n try {\n this.pty.resize(cols, rows);\n } catch {\n // PTY already exited natively before our flag was set — ignore\n }\n this.terminal.resize(cols, rows);\n }\n }\n\n /** Clear scrollback buffer to free memory. */\n clearScrollback() {\n if (!this._exited) {\n this.terminal.clear();\n }\n }\n\n /**\n * Dispose the xterm Terminal and create a fresh one to fully release\n * internal parser/buffer memory. The visible viewport content is\n * captured first and replayed into the new terminal so nothing looks\n * different when the user switches back.\n */\n resetTerminal() {\n if (this._exited) return;\n\n const { cols, rows } = this.terminal;\n const buf = this.terminal.buffer.active;\n\n // Capture visible viewport lines as plain text\n const viewportLines: string[] = [];\n for (let y = 0; y < rows; y++) {\n const line = buf.getLine(buf.baseY + y);\n viewportLines.push(line ? line.translateToString(true) : '');\n }\n\n this.terminal.dispose();\n\n this.terminal = new Terminal({\n cols,\n rows,\n scrollback: 200,\n allowProposedApi: true,\n });\n\n // Replay viewport content (skip trailing empty lines)\n let lastNonEmpty = viewportLines.length - 1;\n while (lastNonEmpty >= 0 && viewportLines[lastNonEmpty].trim() === '') lastNonEmpty--;\n for (let i = 0; i <= lastNonEmpty; i++) {\n this.terminal.write(viewportLines[i] + (i < lastNonEmpty ? '\\r\\n' : ''));\n }\n }\n\n setOutputHandler(handler?: (data: string) => void) {\n this.outputHandler = handler;\n }\n\n dispose() {\n this.setOutputHandler(undefined);\n if (!this._exited) {\n try { this.pty.kill(); } catch { /* PTY already exited */ }\n }\n this.terminal.dispose();\n }\n}\n","/**\n * Local HTTP receiver for Claude Code hook events (Stop, Notification,\n * UserPromptSubmit). Multiple `work` subcommands can each run their own\n * HookServer concurrently — each tags its entries in ~/.claude/settings.json\n * via the shared `settings-editor` so the file write is atomic and stale\n * entries from a crashed previous run get pruned automatically.\n *\n * Originally lived under src/tui/hooks.ts and was hard-wired to `work dash`.\n * Now it's a shared primitive: `work dash` keeps using it for idle tracking\n * and `work web` mounts its own instance.\n */\n\nimport http from 'node:http';\nimport path from 'node:path';\nimport {\n editSettings,\n editSettingsSync,\n isLegacyEntry,\n isOwnerEntry,\n isStaleEntry,\n tag,\n type HookEntry,\n} from './settings-editor.js';\n\nexport type HookEvent = 'stop' | 'notification' | 'prompt_submit';\n\nexport interface HookPayload {\n session_id: string;\n cwd: string;\n hook_event_name: string;\n notification_type?: string;\n}\n\nexport type HookCallback = (cwd: string, event: HookEvent) => void;\n\nexport interface HookServerOptions {\n /** Identifies the subscriber so multiple `work` processes can coexist. */\n owner: string;\n callback: HookCallback;\n}\n\nexport class HookServer {\n private readonly server: http.Server;\n private readonly owner: string;\n private readonly callback: HookCallback;\n private port = 0;\n\n constructor(opts: HookServerOptions) {\n this.owner = opts.owner;\n this.callback = opts.callback;\n\n this.server = http.createServer((req, res) => {\n if (req.method !== 'POST') {\n res.writeHead(404);\n res.end();\n return;\n }\n let body = '';\n req.on('data', (chunk: string) => { body += chunk; });\n req.on('end', () => {\n res.writeHead(200);\n res.end();\n try {\n const payload: HookPayload = JSON.parse(body);\n const cwd = normalizePath(payload.cwd);\n if (payload.hook_event_name === 'Stop') {\n this.callback(cwd, 'stop');\n } else if (payload.hook_event_name === 'Notification') {\n this.callback(cwd, 'notification');\n } else if (payload.hook_event_name === 'UserPromptSubmit') {\n this.callback(cwd, 'prompt_submit');\n }\n } catch { /* malformed — ignore */ }\n });\n });\n }\n\n async start(): Promise<number> {\n const port = await new Promise<number>((resolve, reject) => {\n this.server.listen(0, '127.0.0.1', () => {\n const addr = this.server.address();\n if (!addr || typeof addr === 'string') {\n reject(new Error('Failed to get server address'));\n return;\n }\n resolve(addr.port);\n });\n this.server.on('error', reject);\n });\n this.port = port;\n await injectHttpHooks(this.owner, port);\n return port;\n }\n\n async stop(): Promise<void> {\n await removeHttpHooks(this.owner);\n return new Promise((resolve) => this.server.close(() => resolve()));\n }\n\n /** Synchronous best-effort cleanup for use in signal handlers where we\n * can't await. Falls back to a sync write of the settings file. */\n cleanupSync(): void {\n removeHttpHooksSync(this.owner);\n try { this.server.close(); } catch { /* */ }\n }\n}\n\nconst EVENTS = ['Stop', 'Notification', 'UserPromptSubmit'] as const;\n\nfunction pathForEvent(event: string): string {\n if (event === 'Stop') return '/stop';\n if (event === 'Notification') return '/notification';\n return '/prompt_submit';\n}\n\nfunction buildEntry(owner: string, baseUrl: string, event: string): HookEntry {\n return tag(\n {\n hooks: [\n {\n type: 'http',\n url: `${baseUrl}${pathForEvent(event)}`,\n timeout: 5,\n },\n ],\n ...(event === 'Notification' ? { matcher: 'idle_prompt' } : {}),\n },\n owner,\n );\n}\n\nfunction mutateInject(owner: string, port: number) {\n const baseUrl = `http://127.0.0.1:${port}`;\n return (s: { hooks?: Record<string, HookEntry[] | undefined> }) => {\n if (!s.hooks) s.hooks = {};\n for (const event of EVENTS) {\n const list = (s.hooks[event] ?? []) as HookEntry[];\n const cleaned = list.filter(\n (h) => !isLegacyEntry(h) && !isStaleEntry(h) && !isOwnerEntry(h, owner),\n );\n cleaned.push(buildEntry(owner, baseUrl, event));\n s.hooks[event] = cleaned;\n }\n };\n}\n\nfunction mutateRemove(owner: string) {\n return (s: { hooks?: Record<string, HookEntry[] | undefined> }) => {\n if (!s.hooks) return;\n for (const key of Object.keys(s.hooks)) {\n const list = s.hooks[key];\n if (!Array.isArray(list)) continue;\n s.hooks[key] = list.filter(\n (h) => !isLegacyEntry(h) && !isOwnerEntry(h, owner) && !isStaleEntry(h),\n );\n if (s.hooks[key]!.length === 0) delete s.hooks[key];\n }\n };\n}\n\nfunction injectHttpHooks(owner: string, port: number): Promise<void> {\n return editSettings(mutateInject(owner, port));\n}\n\nfunction removeHttpHooks(owner: string): Promise<void> {\n return editSettings(mutateRemove(owner));\n}\n\nfunction removeHttpHooksSync(owner: string): void {\n editSettingsSync(mutateRemove(owner));\n}\n\nfunction normalizePath(p: string): string {\n return path.resolve(p).toLowerCase();\n}\n","/**\n * Atomic read/edit/write helpers for `~/.claude/settings.json` — the user's\n * global Claude Code settings.\n *\n * Both `HookServer` (http-type hooks) and `installCommandHook` (command-type\n * hooks) mutate this file at startup/shutdown. They previously each had\n * their own copy of `readSettings` / `writeSettings`, and the write was a\n * plain `fs.writeFileSync`. If two `work` processes started concurrently —\n * or one was killed mid-write — the user's global hooks would be silently\n * truncated. That breaks hooks for every project, not just `work`.\n *\n * Everything here writes through `editSettings`, which does a single\n * tmp-file + rename atomic write under a process-level mutex (best-effort —\n * no cross-process locking, but the rename is OS-atomic and the in-process\n * mutex is enough to serialize the dash + web case).\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\n\nconst SETTINGS_PATH = path.join(os.homedir(), '.claude', 'settings.json');\nconst OWNER_TAG = '_workHookOwner';\nconst PID_TAG = '_workHookPid';\nconst LEGACY_TAGS = ['_workDash', '_work2Dash'];\n\nexport interface SettingsFile {\n hooks?: Record<string, HookEntry[] | undefined>;\n [k: string]: unknown;\n}\nexport interface HookEntry {\n hooks?: { type: string; url?: string; command?: string; timeout?: number }[];\n matcher?: string;\n [k: string]: unknown;\n}\n\nexport const HOOK_TAGS = {\n OWNER_TAG,\n PID_TAG,\n LEGACY_TAGS,\n} as const;\n\nfunction readSettings(): SettingsFile {\n try {\n if (!fs.existsSync(SETTINGS_PATH)) return {};\n return JSON.parse(fs.readFileSync(SETTINGS_PATH, 'utf8')) as SettingsFile;\n } catch {\n return {};\n }\n}\n\n/** Atomic write: tmp-file + rename. Caller must hold the in-process queue. */\nfunction writeAtomic(s: SettingsFile): void {\n try {\n fs.mkdirSync(path.dirname(SETTINGS_PATH), { recursive: true });\n const tmp = `${SETTINGS_PATH}.tmp-${process.pid}-${Date.now()}`;\n fs.writeFileSync(tmp, JSON.stringify(s, null, 2), 'utf-8');\n fs.renameSync(tmp, SETTINGS_PATH);\n } catch { /* best-effort */ }\n}\n\n/**\n * Serialise in-process edits to the settings file. Two HookServer / hook\n * installer calls from the same `work` process can't race each other.\n * (Cross-process races still exist but are bounded by the OS-atomic rename\n * — the worst case is one process's edit clobbering another's, never a\n * truncated half-written file.)\n */\nlet editQueue: Promise<void> = Promise.resolve();\nexport function editSettings(\n mutate: (s: SettingsFile) => void,\n): Promise<void> {\n editQueue = editQueue.then(() => {\n const s = readSettings();\n if (!s.hooks) s.hooks = {};\n mutate(s);\n if (s.hooks && Object.keys(s.hooks).length === 0) delete s.hooks;\n writeAtomic(s);\n });\n return editQueue;\n}\n\n/** Synchronous mutation variant — used in shutdown handlers where there's\n * no time to await a promise. Still atomic on the rename, just doesn't\n * participate in the in-process queue. */\nexport function editSettingsSync(mutate: (s: SettingsFile) => void): void {\n const s = readSettings();\n if (!s.hooks) s.hooks = {};\n mutate(s);\n if (s.hooks && Object.keys(s.hooks).length === 0) delete s.hooks;\n writeAtomic(s);\n}\n\n/** Common predicate: is this entry tagged with our owner? */\nexport function isOwnerEntry(h: HookEntry, owner: string): boolean {\n return h[OWNER_TAG] === owner;\n}\n\n/** Common predicate: was this entry tagged by a `work` process that's no\n * longer running? Stale entries get pruned on every install. */\nexport function isStaleEntry(h: HookEntry): boolean {\n if (typeof h[OWNER_TAG] !== 'string') return false;\n const pid = h[PID_TAG];\n if (typeof pid !== 'number') return false;\n try {\n process.kill(pid, 0);\n return false;\n } catch {\n return true;\n }\n}\n\nexport function isLegacyEntry(h: HookEntry): boolean {\n return LEGACY_TAGS.some((t) => (h as Record<string, unknown>)[t] === true);\n}\n\n/** Tag an entry so future installs can find/remove it. */\nexport function tag(entry: HookEntry, owner: string): HookEntry {\n return {\n ...entry,\n [OWNER_TAG]: owner,\n [PID_TAG]: process.pid,\n };\n}\n","/**\n * Best-effort desktop notifications for session lifecycle events. Used by the\n * dashboard hook callback to alert the user when a background Claude session\n * goes idle (finished its turn) or needs input — so parallel sessions don't\n * have to be babysat in the TUI.\n *\n * Opt-in via `WorkConfig.notifications`. Cross-platform with a graceful no-op\n * when the platform is unsupported or the notifier binary is absent. Spawns\n * fire-and-forget via cross-spawn argv arrays (never a shell string) and never\n * throws back to the caller.\n */\n\nimport spawn from 'cross-spawn';\n\nexport type NotifyKind = 'idle' | 'needs_input';\n\nconst MESSAGES: Record<NotifyKind, string> = {\n idle: 'Idle — finished its turn',\n needs_input: 'Needs your input',\n};\n\n/** Drop control characters that could break notifier argument parsing. */\nfunction sanitize(s: string): string {\n // eslint-disable-next-line no-control-regex\n return s.replace(/[\\u0000-\\u001f\\u007f]/g, \" \").trim();\n}\n\n/** Escape for embedding inside an AppleScript double-quoted string literal. */\nfunction escapeAppleScript(s: string): string {\n return sanitize(s).replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n}\n\n/** Escape for a PowerShell single-quoted string literal (double the quote). */\nfunction escapePwshSingle(s: string): string {\n return sanitize(s).replace(/'/g, \"''\");\n}\n\n/**\n * Build the notifier command for a platform. Pure and side-effect-free so it\n * can be unit-tested without spawning. Returns null for unsupported platforms.\n */\nexport function buildNotifyCommand(\n sessionName: string,\n kind: NotifyKind,\n platform: NodeJS.Platform,\n): { cmd: string; args: string[] } | null {\n const title = `work: ${sessionName}`;\n const message = MESSAGES[kind];\n\n if (platform === 'darwin') {\n const t = escapeAppleScript(title);\n const m = escapeAppleScript(message);\n return {\n cmd: 'osascript',\n args: ['-e', `display notification \"${m}\" with title \"${t}\"`],\n };\n }\n\n if (platform === 'linux') {\n // notify-send takes title/message as separate argv args — injection-safe.\n return { cmd: 'notify-send', args: [sanitize(title), sanitize(message)] };\n }\n\n if (platform === 'win32') {\n const t = escapePwshSingle(title);\n const m = escapePwshSingle(message);\n // The Windows path is BEST-EFFORT. There is no reliable, dependency-free\n // way to raise a real toast from a short-lived headless PowerShell process:\n // - WinRT toasts require a registered AppUserModelID; without one they\n // are silently dropped on many systems.\n // - A System.Windows.Forms NotifyIcon balloon needs a live message pump,\n // and disposing the process immediately can cancel the balloon before\n // it renders — but keeping the process alive leaks a lingering pwsh.\n // We therefore prefer BurntToast (proper WinRT toasts) when the module is\n // installed, and otherwise fall back to a balloon shown without a\n // load-bearing Start-Sleep: we pump messages briefly (DoEvents) so the\n // balloon has a chance to appear, then dispose. This may occasionally\n // no-op, which is acceptable for an opt-in convenience notification.\n const script =\n \"$ErrorActionPreference='SilentlyContinue';\" +\n // Preferred path: BurntToast (real WinRT toast, no lingering process).\n 'if(Get-Module -ListAvailable -Name BurntToast){' +\n 'Import-Module BurntToast;' +\n `New-BurntToastNotification -Text '${t}','${m}';` +\n '}else{' +\n // Fallback: transient balloon. Pump messages so it can render without a\n // multi-second sleep, then dispose so the process exits promptly.\n 'Add-Type -AssemblyName System.Windows.Forms;' +\n 'Add-Type -AssemblyName System.Drawing;' +\n '$n=New-Object System.Windows.Forms.NotifyIcon;' +\n '$n.Icon=[System.Drawing.SystemIcons]::Information;' +\n '$n.Visible=$true;' +\n `$n.ShowBalloonTip(5000,'${t}','${m}',[System.Windows.Forms.ToolTipIcon]::Info);` +\n '[System.Windows.Forms.Application]::DoEvents();' +\n 'Start-Sleep -Milliseconds 500;' +\n '[System.Windows.Forms.Application]::DoEvents();' +\n '$n.Dispose();' +\n '}';\n return { cmd: 'powershell', args: ['-NoProfile', '-Command', script] };\n }\n\n return null;\n}\n\n/**\n * Decide whether a hook event should fire a notification, tracking which\n * sessions have already been alerted for the current idle period in `notified`\n * (mutated in place). Returns the kind to fire, or null to stay silent.\n *\n * Decoupled from `PtySession.idle` so it does not depend on that field's\n * initial value: a session's first `stop`/`notification` still alerts, while\n * repeated `stop` events within one idle period are de-duplicated. A\n * `prompt_submit` (the user replied / a new turn started) clears the session\n * so the next idle alerts again.\n */\nexport function notifyKindForEvent(\n event: 'stop' | 'notification' | 'prompt_submit',\n sessionKey: string,\n notified: Set<string>,\n): NotifyKind | null {\n if (event === 'prompt_submit') {\n notified.delete(sessionKey);\n return null;\n }\n if (notified.has(sessionKey)) return null;\n notified.add(sessionKey);\n return event === 'notification' ? 'needs_input' : 'idle';\n}\n\nexport interface NotifyOptions {\n /** Notifications only fire when this is explicitly true. */\n enabled?: boolean;\n /** Override the detected platform (testing). */\n platform?: NodeJS.Platform;\n /** Override the spawn function (testing). */\n spawnFn?: typeof spawn;\n}\n\n/**\n * Fire a desktop notification for a session event. No-op when disabled, on an\n * unsupported platform, or when the notifier binary is missing. Never throws.\n */\nexport function notifyDesktop(\n sessionName: string,\n kind: NotifyKind,\n opts: NotifyOptions = {},\n): void {\n if (opts.enabled !== true) return;\n\n const platform = opts.platform ?? process.platform;\n const command = buildNotifyCommand(sessionName, kind, platform);\n if (!command) return;\n\n const spawnFn = opts.spawnFn ?? spawn;\n try {\n const child = spawnFn(command.cmd, command.args, {\n detached: true,\n stdio: 'ignore',\n });\n // Swallow async spawn errors (e.g. ENOENT when the binary is absent);\n // without a handler these would surface as an uncaught exception.\n child.on?.('error', () => {});\n child.unref?.();\n } catch {\n /* notifier unavailable — silent no-op */\n }\n}\n","/**\n * Opt-in, user-configurable shell commands that run when a background session\n * changes status (idle / needs input). Generalizes the desktop-notification\n * path: instead of a fixed OS notification, the user can run any command from\n * their own config (e.g. play a sound, ping a webhook, focus a window).\n *\n * Each hook is spawned fire-and-forget with the session directory as cwd\n * (passed as the spawn `cwd` option — never interpolated into the command\n * string). Commands run with `shell: true` intentionally, since they come only\n * from the user's own `~/.work/config.json`. Mirrors notifier.ts's defensive\n * style: a bad command never throws back to the caller.\n */\n\nimport { spawn } from 'node:child_process';\nimport type { StatusHook } from './config.js';\n\n/**\n * Run every configured status hook whose `on` matches `kind`, fire-and-forget.\n * No-op when `hooks` is undefined/empty or nothing matches. Never throws.\n *\n * @param kind The status the session changed to.\n * @param cwd Session directory; used as the spawned command's cwd.\n * @param sessionName Friendly session name, exposed as $WORK_SESSION.\n * @param hooks The configured hooks (from WorkConfig.statusHooks).\n */\nexport function runStatusHooks(\n kind: 'idle' | 'needs_input',\n cwd: string,\n sessionName: string,\n hooks: StatusHook[] | undefined,\n): void {\n if (!hooks || hooks.length === 0) return;\n\n for (const hook of hooks) {\n try {\n // loadConfig only guarantees `hooks` is an array, not that each element\n // is a well-formed {on, command}. A hand-edited config such as\n // `statusHooks: [null]` or `[\"x\"]` must not throw past this guard, so\n // validate the element shape inside the try before reading its fields.\n if (!hook || typeof hook !== 'object') continue;\n const { on, command } = hook as Partial<StatusHook>;\n if (on !== kind) continue;\n if (typeof command !== 'string' || command.length === 0) continue;\n const child = spawn(command, {\n cwd,\n shell: true,\n detached: true,\n stdio: 'ignore',\n env: {\n ...process.env,\n WORK_SESSION: sessionName,\n WORK_STATUS: kind,\n },\n });\n // Swallow async spawn errors (e.g. ENOENT); without a handler these\n // would surface as an uncaught exception.\n child.on?.('error', () => {});\n child.unref?.();\n } catch {\n /* bad command — silent no-op, never throw back to the caller */\n }\n }\n}\n","/**\n * Renders an xterm-headless buffer into an array of ANSI-styled strings (one per row).\n * No cursor positioning — just SGR codes + text, suitable for Ink <Text> components.\n *\n * SGR utility functions are copied from src/tui/renderer.ts.\n */\n\ninterface CellAttrs {\n fgMode: number;\n fg: number;\n bgMode: number;\n bg: number;\n bold: number;\n dim: number;\n italic: number;\n underline: number;\n blink: number;\n inverse: number;\n invisible: number;\n strikethrough: number;\n overline: number;\n}\n\nfunction readAttrs(cell: any): CellAttrs {\n return {\n fgMode: cell.getFgColorMode(),\n fg: cell.getFgColor(),\n bgMode: cell.getBgColorMode(),\n bg: cell.getBgColor(),\n bold: cell.isBold(),\n dim: cell.isDim(),\n italic: cell.isItalic(),\n underline: cell.isUnderline(),\n blink: cell.isBlink(),\n inverse: cell.isInverse(),\n invisible: cell.isInvisible(),\n strikethrough: cell.isStrikethrough(),\n overline: cell.isOverline(),\n };\n}\n\nfunction attrsEqual(a: CellAttrs, b: CellAttrs): boolean {\n return (\n a.fgMode === b.fgMode &&\n a.fg === b.fg &&\n a.bgMode === b.bgMode &&\n a.bg === b.bg &&\n a.bold === b.bold &&\n a.dim === b.dim &&\n a.italic === b.italic &&\n a.underline === b.underline &&\n a.blink === b.blink &&\n a.inverse === b.inverse &&\n a.invisible === b.invisible &&\n a.strikethrough === b.strikethrough &&\n a.overline === b.overline\n );\n}\n\nfunction attrsToSgr(a: CellAttrs): string {\n if (\n a.fgMode === 0 &&\n a.bgMode === 0 &&\n !a.bold && !a.dim && !a.italic && !a.underline &&\n !a.blink && !a.inverse && !a.invisible && !a.strikethrough && !a.overline\n ) {\n return '\\x1B[0m';\n }\n\n const params: number[] = [0];\n if (a.bold) params.push(1);\n if (a.dim) params.push(2);\n if (a.italic) params.push(3);\n if (a.underline) params.push(4);\n if (a.blink) params.push(5);\n if (a.inverse) params.push(7);\n if (a.invisible) params.push(8);\n if (a.strikethrough) params.push(9);\n if (a.overline) params.push(53);\n\n pushColorSgr(params, a.fgMode, a.fg, false);\n pushColorSgr(params, a.bgMode, a.bg, true);\n\n return `\\x1B[${params.join(';')}m`;\n}\n\nconst CM_DEFAULT = 0;\nconst CM_P16 = 0x1000000;\nconst CM_P256 = 0x2000000;\nconst CM_RGB = 0x3000000;\n\nfunction pushColorSgr(params: number[], mode: number, color: number, isBg: boolean): void {\n if (mode === CM_DEFAULT) return;\n\n const base = isBg ? 40 : 30;\n\n if (mode === CM_P16 || mode === CM_P256) {\n if (color < 8) {\n params.push(base + color);\n } else if (color < 16) {\n params.push(base + 60 + (color - 8));\n } else {\n params.push(base + 8, 5, color);\n }\n } else if (mode === CM_RGB) {\n const r = (color >> 16) & 0xff;\n const g = (color >> 8) & 0xff;\n const b = color & 0xff;\n params.push(base + 8, 2, r, g, b);\n }\n}\n\n/**\n * Render the visible portion of an xterm buffer as an array of ANSI-styled lines.\n * @param scrollBack - number of lines scrolled back from the bottom (0 = live view)\n */\nexport function renderBufferLines(buffer: any, cols: number, rows: number, scrollBack: number = 0): string[] {\n const lines: string[] = [];\n const nullCell = buffer.getNullCell();\n\n for (let y = 0; y < rows; y++) {\n const lineIdx = buffer.baseY + y - scrollBack;\n const line = buffer.getLine(lineIdx);\n let prevAttrs: CellAttrs | null = null;\n\n if (!line) {\n lines.push(' '.repeat(cols));\n continue;\n }\n\n const parts: string[] = [];\n parts.push('\\x1B[0m');\n\n let col = 0;\n while (col < cols) {\n const cell = line.getCell(col, nullCell);\n if (!cell) {\n parts.push(' ');\n col++;\n continue;\n }\n\n const width = cell.getWidth();\n if (width === 0) {\n col++;\n continue;\n }\n\n const attrs = readAttrs(cell);\n if (!prevAttrs || !attrsEqual(prevAttrs, attrs)) {\n parts.push(attrsToSgr(attrs));\n prevAttrs = attrs;\n }\n\n const ch = cell.getChars();\n parts.push(ch || ' ');\n col += width;\n }\n\n parts.push('\\x1B[0m');\n lines.push(parts.join(''));\n }\n\n return lines;\n}\n","import React from 'react';\nimport { Box, Text } from 'ink';\nimport { timeAgo } from '../utils/format.js';\nimport type { WorktreeSession } from '../core/history.js';\nimport { effectiveLastAccessedAt } from '../core/claude-activity.js';\nimport type { SessionStatus } from '../tui/session.js';\nimport type { PullRequestInfo, BranchPrMap } from '../core/pr.js';\nimport type { JiraIssue } from '../core/jira.js';\nimport type { Task } from '../core/tasks.js';\n\nexport interface SidebarProps {\n sidebarRows: SidebarRow[];\n cursor: number;\n focused: boolean;\n statusMap: Map<string, SessionStatus>;\n conflictCounts: Map<string, number>;\n mergedSet: Set<string>;\n prMap: BranchPrMap;\n activeKey: string | null;\n width: number;\n height: number;\n branchInput?: { value: string } | null;\n}\n\nexport interface PrPaneProps {\n prRows: SidebarRow[];\n cursor: number;\n focused: boolean;\n localBranches: Set<string>;\n width: number;\n height: number;\n}\n\nexport interface JiraPaneProps {\n jiraRows: SidebarRow[];\n cursor: number;\n focused: boolean;\n width: number;\n height: number;\n}\n\nexport interface TaskPaneProps {\n taskRows: SidebarRow[];\n cursor: number;\n focused: boolean;\n width: number;\n height: number;\n taskInput?: string | null;\n}\n\nexport type SidebarRow =\n | { type: 'header'; label: string }\n | { type: 'session'; session: WorktreeSession }\n | { type: 'project'; name: string; isGroup: boolean }\n | { type: 'pr'; pr: PullRequestInfo }\n | { type: 'jira'; issue: JiraIssue }\n | { type: 'task'; task: Task };\n\nexport function buildSessionRows(sessions: WorktreeSession[], statusMap?: Map<string, SessionStatus>): SidebarRow[] {\n if (sessions.length === 0) return [];\n\n const rows: SidebarRow[] = [];\n\n // Split into active (running/idle PTY) and inactive sessions\n const active: WorktreeSession[] = [];\n const inactive: WorktreeSession[] = [];\n for (const s of sessions) {\n const key = `${s.target}:${s.branch}`;\n const status = statusMap?.get(key);\n if (status === 'running' || status === 'idle') {\n active.push(s);\n } else {\n inactive.push(s);\n }\n }\n\n // Active sessions first (flat list, no grouping needed)\n if (active.length > 0) {\n rows.push({ type: 'header', label: 'Active' });\n for (const session of active) {\n rows.push({ type: 'session', session });\n }\n }\n\n // Inactive sessions grouped by target\n if (inactive.length > 0) {\n const groups = new Map<string, WorktreeSession[]>();\n for (const s of inactive) {\n if (!groups.has(s.target)) groups.set(s.target, []);\n groups.get(s.target)!.push(s);\n }\n\n for (const [target, group] of groups) {\n const typeTag = group[0].isGroup ? 'group' : 'repo';\n rows.push({ type: 'header', label: `${target} (${typeTag})` });\n for (const session of group) {\n rows.push({ type: 'session', session });\n }\n }\n }\n\n return rows;\n}\n\nexport function buildProjectRows(projects: Array<{ name: string; isGroup: boolean }>): SidebarRow[] {\n if (projects.length === 0) return [];\n const rows: SidebarRow[] = [];\n rows.push({ type: 'header', label: 'Select project' });\n for (const p of projects) {\n rows.push({ type: 'project', name: p.name, isGroup: p.isGroup });\n }\n return rows;\n}\n\nexport function buildJiraRows(issues: JiraIssue[]): SidebarRow[] {\n if (issues.length === 0) return [{ type: 'header', label: 'No Jira issues' }];\n\n // Group by status\n const byStatus = new Map<string, JiraIssue[]>();\n for (const issue of issues) {\n const status = issue.status || 'Unknown';\n if (!byStatus.has(status)) byStatus.set(status, []);\n byStatus.get(status)!.push(issue);\n }\n\n // Sort statuses: To Do first, then In Progress, then alphabetical\n const statusOrder = (s: string) => {\n const lower = s.toLowerCase();\n if (lower === 'to do') return 0;\n if (lower.includes('progress')) return 1;\n if (lower.includes('review')) return 2;\n return 3;\n };\n const sortedStatuses = [...byStatus.keys()].sort((a, b) => statusOrder(a) - statusOrder(b) || a.localeCompare(b));\n\n const rows: SidebarRow[] = [];\n for (const status of sortedStatuses) {\n rows.push({ type: 'header', label: status });\n for (const issue of byStatus.get(status)!) {\n rows.push({ type: 'jira', issue });\n }\n }\n return rows;\n}\n\nexport function buildTaskRows(tasks: Task[]): SidebarRow[] {\n const open = tasks.filter((t) => !t.done);\n const done = tasks.filter((t) => t.done);\n if (open.length === 0 && done.length === 0) return [{ type: 'header', label: 'No tasks' }];\n\n const rows: SidebarRow[] = [];\n if (open.length > 0) {\n for (const task of open) {\n rows.push({ type: 'task', task });\n }\n }\n if (done.length > 0) {\n rows.push({ type: 'header', label: `Done (${done.length})` });\n for (const task of done) {\n rows.push({ type: 'task', task });\n }\n }\n return rows;\n}\n\nexport function buildPrRows(prMap: BranchPrMap): SidebarRow[] {\n // Collect all PRs, grouped by repoAlias\n const byRepo = new Map<string, PullRequestInfo[]>();\n for (const prs of prMap.values()) {\n for (const pr of prs) {\n if (!byRepo.has(pr.repoAlias)) byRepo.set(pr.repoAlias, []);\n byRepo.get(pr.repoAlias)!.push(pr);\n }\n }\n\n if (byRepo.size === 0) return [{ type: 'header', label: 'No open PRs' }];\n\n const rows: SidebarRow[] = [];\n // Deduplicate PRs by number+repo\n const seen = new Set<string>();\n for (const [repo, prs] of byRepo) {\n rows.push({ type: 'header', label: repo });\n for (const pr of prs) {\n const key = `${repo}:${pr.number}`;\n if (seen.has(key)) continue;\n seen.add(key);\n rows.push({ type: 'pr', pr });\n }\n }\n\n return rows;\n}\n\nexport function countSelectable(rows: SidebarRow[]): number {\n return rows.filter((r) => r.type !== 'header').length;\n}\n\n/** Convert a visual row index (0-based position in the pane) to a selectable cursor index. */\nexport function visualRowToCursor(rows: SidebarRow[], visualRow: number): number {\n let selectableIdx = -1;\n for (let i = 0; i <= visualRow && i < rows.length; i++) {\n if (rows[i].type !== 'header') selectableIdx++;\n }\n return Math.max(0, selectableIdx);\n}\n\nexport function cursorToRow(rows: SidebarRow[], cursor: number): SidebarRow | undefined {\n let idx = 0;\n for (const row of rows) {\n if (row.type === 'header') continue;\n if (idx === cursor) return row;\n idx++;\n }\n return undefined;\n}\n\nexport function sessionKey(s: WorktreeSession): string {\n return `${s.target}:${s.branch}`;\n}\n\nfunction truncate(str: string, max: number): string {\n if (max <= 1) return '';\n return str.length > max ? str.slice(0, max - 1) + '…' : str;\n}\n\n/** Wrap text in an OSC 8 terminal hyperlink. */\nfunction hyperlink(url: string, text: string): string {\n return `\\x1B]8;;${url}\\x07${text}\\x1B]8;;\\x07`;\n}\n\nfunction ownershipLen(pr: PullRequestInfo): number {\n if (pr.isMine) return 2;\n if (pr.myReview !== 'NONE') return 2;\n return 0;\n}\n\nfunction singlePrBadgeLength(pr: PullRequestInfo): number {\n return 1 + String(pr.number).length;\n}\n\nfunction prBadgesLength(prs: PullRequestInfo[]): number {\n if (prs.length === 0) return 0;\n return prs.reduce((sum, pr) => sum + singlePrBadgeLength(pr), 0) + (prs.length - 1);\n}\n\nfunction PrBadge({ pr }: { pr: PullRequestInfo }): React.ReactElement {\n return (\n <Text color=\"blue\">{hyperlink(pr.url, `#${pr.number}`)}</Text>\n );\n}\n\nfunction EmptyRow({ width }: { width: number }): React.ReactElement {\n return <Text>{' '.repeat(width)}</Text>;\n}\n\nfunction HeaderRow({ label, width }: { label: string; width: number }): React.ReactElement {\n const text = ` ${truncate(label, width - 3)} `;\n const lineLen = Math.max(0, width - text.length);\n return (\n <Box width={width}>\n <Text color=\"yellow\" bold>{text}</Text>\n <Text dimColor>{'─'.repeat(lineLen)}</Text>\n </Box>\n );\n}\n\nfunction SessionRow({\n session: s,\n selected,\n focused,\n status,\n conflicts,\n merged,\n prs,\n active,\n width,\n}: {\n session: WorktreeSession;\n selected: boolean;\n focused: boolean;\n status: SessionStatus;\n conflicts: number;\n merged: boolean;\n prs: PullRequestInfo[];\n active: boolean;\n width: number;\n}): React.ReactElement {\n const dotChar = status === 'idle' ? '◆' : status === 'running' ? '●' : '○';\n const dotColor = status === 'idle' ? 'yellow' : status === 'running' ? 'green' : 'gray';\n const agoStr = timeAgo(effectiveLastAccessedAt(s));\n\n const mergedLen = merged ? 8 : 0; // \" merged\"\n const conflictLen = conflicts > 0 ? 2 + String(conflicts).length : 0;\n const prLen = prs.length > 0 ? 1 + prBadgesLength(prs) : 0;\n const fixedOverhead = 5 + agoStr.length + mergedLen + conflictLen + prLen;\n const branchBudget = Math.max(4, width - fixedOverhead);\n\n const cursor = selected && focused ? '›' : active ? '▸' : ' ';\n const cursorColor = selected && focused ? 'cyan' : active ? 'green' : undefined;\n\n return (\n <Box width={width}>\n <Text> </Text>\n <Text color={cursorColor}>{cursor}</Text>\n <Text color={dotColor}>{dotChar}</Text>\n <Text> </Text>\n <Text color=\"green\">{truncate(s.branch, branchBudget)}</Text>\n {merged && <Text color=\"magenta\"> merged</Text>}\n {conflicts > 0 && <Text color=\"red\"> !{conflicts}</Text>}\n {prs.map((pr, i) => (\n <React.Fragment key={i}>\n <Text> </Text>\n <PrBadge pr={pr} />\n </React.Fragment>\n ))}\n <Box flexGrow={1} />\n <Text dimColor>{agoStr}</Text>\n </Box>\n );\n}\n\nfunction ProjectRow({\n name,\n isGroup,\n selected,\n focused,\n branchInput,\n width,\n}: {\n name: string;\n isGroup: boolean;\n selected: boolean;\n focused: boolean;\n branchInput?: { value: string } | null;\n width: number;\n}): React.ReactElement {\n const marker = selected && focused ? '›' : ' ';\n\n if (branchInput && selected) {\n return (\n <Box width={width}>\n <Text> </Text>\n <Text color=\"cyan\">{marker}</Text>\n <Text> </Text>\n <Text color=\"magenta\">{truncate(name, width - 15)}</Text>\n <Text> </Text>\n <Text color=\"cyan\">branch:</Text>\n <Text> {branchInput.value}</Text>\n <Text dimColor>█</Text>\n </Box>\n );\n }\n\n return (\n <Box width={width}>\n <Text> </Text>\n <Text color={selected && focused ? 'cyan' : undefined}>{marker}</Text>\n <Text> </Text>\n <Text color=\"magenta\">{truncate(name, width - 15)}</Text>\n <Text> </Text>\n <Text dimColor>{isGroup ? '(group)' : '(repo)'}</Text>\n </Box>\n );\n}\n\nfunction PrListRow({\n pr,\n selected,\n focused,\n local,\n width,\n}: {\n pr: PullRequestInfo;\n selected: boolean;\n focused: boolean;\n local: boolean;\n width: number;\n}): React.ReactElement {\n const marker = selected && focused ? '›' : ' ';\n const numStr = `#${pr.number}`;\n const checksChar = pr.checksStatus === 'SUCCESS' ? '✓' : pr.checksStatus === 'FAILURE' ? '✗' : pr.checksStatus === 'PENDING' ? '●' : '';\n const checksColor = pr.checksStatus === 'SUCCESS' ? 'green' : pr.checksStatus === 'FAILURE' ? 'red' : 'yellow';\n const checksLen = checksChar ? 2 : 0;\n const rvwLen = ownershipLen(pr);\n const localLen = local ? 6 : 0; // \" local\"\n const fixedOverhead = 4 + numStr.length + checksLen + rvwLen + localLen;\n const branchBudget = Math.max(4, width - fixedOverhead);\n const branchColor = pr.isDraft ? undefined : 'green';\n\n return (\n <Box width={width}>\n <Text> </Text>\n <Text color={selected && focused ? 'cyan' : undefined}>{marker}</Text>\n <Text> </Text>\n <Text color={branchColor} dimColor={pr.isDraft}>{truncate(pr.branch, branchBudget)}</Text>\n {local && <Text color=\"cyan\"> local</Text>}\n <Box flexGrow={1} />\n {pr.isMine && <Text color=\"magenta\">★ </Text>}\n {!pr.isMine && pr.myReview === 'APPROVED' && <Text color=\"green\">✔ </Text>}\n {!pr.isMine && (pr.myReview === 'CHANGES_REQUESTED' || pr.myReview === 'COMMENTED') && <Text color=\"red\">✎ </Text>}\n <Text color={pr.isDraft ? undefined : 'blue'} dimColor={pr.isDraft}>{hyperlink(pr.url, numStr)}</Text>\n {checksChar && <Text color={checksColor}> {checksChar}</Text>}\n </Box>\n );\n}\n\n/** Bordered pane that renders rows inside a box frame. */\nfunction BorderedPane({\n rows: sidebarRows,\n cursor,\n focused,\n borderColor,\n title,\n width,\n height,\n renderRow,\n}: {\n rows: SidebarRow[];\n cursor: number;\n focused: boolean;\n borderColor: string;\n title?: string;\n width: number;\n height: number;\n renderRow: (row: SidebarRow, idx: number, selected: boolean) => React.ReactNode;\n}): React.ReactElement {\n const innerWidth = width - 2;\n const contentHeight = height - 2;\n\n // Find the row index of the cursor-selected item so we can scroll to it\n let cursorRowIdx = 0;\n let selectableCount = 0;\n for (let i = 0; i < sidebarRows.length; i++) {\n if (sidebarRows[i].type !== 'header') {\n if (selectableCount === cursor) {\n cursorRowIdx = i;\n break;\n }\n selectableCount++;\n }\n }\n\n // Compute scroll offset to keep cursor visible (with 1-row margin)\n let scrollOffset = 0;\n if (sidebarRows.length > contentHeight) {\n if (cursorRowIdx >= contentHeight - 1) {\n scrollOffset = Math.min(\n cursorRowIdx - contentHeight + 2,\n sidebarRows.length - contentHeight,\n );\n }\n }\n\n const rendered: React.ReactNode[] = [];\n let selectableIdx = 0;\n // Count selectables before the visible window to get the right index\n for (let i = 0; i < scrollOffset; i++) {\n if (sidebarRows[i].type !== 'header') selectableIdx++;\n }\n for (let i = 0; i < contentHeight; i++) {\n const rowIdx = scrollOffset + i;\n if (rowIdx >= sidebarRows.length) {\n rendered.push(<EmptyRow key={i} width={innerWidth} />);\n continue;\n }\n const row = sidebarRows[rowIdx];\n if (row.type === 'header') {\n rendered.push(<HeaderRow key={i} label={row.label} width={innerWidth} />);\n } else {\n const sel = selectableIdx === cursor;\n selectableIdx++;\n rendered.push(renderRow(row, rowIdx, sel));\n }\n }\n\n const topBorder = title\n ? '┌─ ' + title + ' ' + '─'.repeat(Math.max(0, innerWidth - title.length - 3)) + '┐'\n : '┌' + '─'.repeat(innerWidth) + '┐';\n\n return (\n <Box flexDirection=\"column\" width={width}>\n <Text color={borderColor}>{topBorder}</Text>\n {rendered.map((row, i) => (\n <Box key={i}>\n <Text color={borderColor}>│</Text>\n {row}\n <Text color={borderColor}>│</Text>\n </Box>\n ))}\n <Text color={borderColor}>{'└' + '─'.repeat(innerWidth) + '┘'}</Text>\n </Box>\n );\n}\n\nexport function Sidebar({ sidebarRows, cursor, focused, statusMap, conflictCounts, mergedSet, prMap, activeKey, width, height, branchInput }: SidebarProps) {\n const borderColor = focused ? 'cyan' : 'gray';\n const innerWidth = width - 2;\n\n return (\n <BorderedPane\n rows={sidebarRows}\n cursor={cursor}\n focused={focused}\n borderColor={borderColor}\n title=\"Worktrees\"\n width={width}\n height={height}\n renderRow={(row, i, sel) => {\n if (row.type === 'session') {\n const s = row.session;\n const key = sessionKey(s);\n return (\n <SessionRow\n key={i}\n session={s}\n selected={sel}\n focused={focused}\n status={statusMap.get(key) ?? 'stopped'}\n conflicts={conflictCounts.get(key) ?? 0}\n merged={mergedSet.has(key)}\n prs={prMap.get(s.branch) ?? []}\n active={key === activeKey}\n width={innerWidth}\n />\n );\n }\n if (row.type === 'project') {\n return (\n <ProjectRow\n key={i}\n name={row.name}\n isGroup={row.isGroup}\n selected={sel}\n focused={focused}\n branchInput={branchInput}\n width={innerWidth}\n />\n );\n }\n return <EmptyRow key={i} width={innerWidth} />;\n }}\n />\n );\n}\n\nfunction statusColor(status: string): string | undefined {\n const lower = status.toLowerCase();\n if (lower.includes('progress') || lower.includes('review')) return 'cyan';\n if (lower.includes('done') || lower.includes('closed')) return 'green';\n return 'yellow';\n}\n\nfunction JiraListRow({\n issue,\n selected,\n focused,\n width,\n}: {\n issue: JiraIssue;\n selected: boolean;\n focused: boolean;\n width: number;\n}): React.ReactElement {\n const marker = selected && focused ? '›' : ' ';\n const keyLen = issue.key.length;\n const summaryBudget = Math.max(4, width - keyLen - 5);\n\n return (\n <Box width={width}>\n <Text> </Text>\n <Text color={selected && focused ? 'cyan' : undefined}>{marker}</Text>\n <Text> </Text>\n <Text color={statusColor(issue.status)}>{issue.url ? hyperlink(issue.url, issue.key) : issue.key}</Text>\n <Text> </Text>\n <Text>{truncate(issue.summary, summaryBudget)}</Text>\n </Box>\n );\n}\n\nexport function PrPane({ prRows, cursor, focused, localBranches, width, height }: PrPaneProps) {\n const borderColor = focused ? 'cyan' : 'gray';\n const innerWidth = width - 2;\n\n return (\n <BorderedPane\n rows={prRows}\n cursor={cursor}\n focused={focused}\n borderColor={borderColor}\n title=\"Pull Requests\"\n width={width}\n height={height}\n renderRow={(row, i, sel) => {\n if (row.type === 'pr') {\n return (\n <PrListRow\n key={i}\n pr={row.pr}\n selected={sel}\n focused={focused}\n local={localBranches.has(row.pr.branch)}\n width={innerWidth}\n />\n );\n }\n return <EmptyRow key={i} width={innerWidth} />;\n }}\n />\n );\n}\n\nfunction TaskListRow({\n task,\n selected,\n focused,\n width,\n}: {\n task: Task;\n selected: boolean;\n focused: boolean;\n width: number;\n}): React.ReactElement {\n const marker = selected && focused ? '›' : ' ';\n const check = task.done ? '✓' : '○';\n const checkColor = task.done ? 'green' : 'gray';\n const idStr = `#${task.id}`;\n const textBudget = Math.max(4, width - idStr.length - 7);\n\n return (\n <Box width={width}>\n <Text> </Text>\n <Text color={selected && focused ? 'cyan' : undefined}>{marker}</Text>\n <Text color={checkColor}>{check}</Text>\n <Text> </Text>\n <Text dimColor={task.done} strikethrough={task.done}>{truncate(task.text, textBudget)}</Text>\n <Box flexGrow={1} />\n <Text dimColor>{idStr}</Text>\n </Box>\n );\n}\n\nfunction TaskInputRow({ value, width }: { value: string; width: number }): React.ReactElement {\n const budget = Math.max(4, width - 8);\n return (\n <Box width={width}>\n <Text> </Text>\n <Text color=\"cyan\">+ </Text>\n <Text>{truncate(value, budget)}</Text>\n <Text dimColor>█</Text>\n </Box>\n );\n}\n\nexport function TaskPane({ taskRows, cursor, focused, width, height, taskInput }: TaskPaneProps) {\n const borderColor = focused ? 'cyan' : 'gray';\n const innerWidth = width - 2;\n const contentHeight = height - 2;\n\n // If input is active, render it manually at the top\n if (taskInput !== null && taskInput !== undefined) {\n const rendered: React.ReactNode[] = [];\n rendered.push(<TaskInputRow key=\"input\" value={taskInput} width={innerWidth} />);\n\n let selectableIdx = 0;\n for (let i = 0; i < contentHeight - 1 && i < taskRows.length; i++) {\n const row = taskRows[i];\n if (row.type === 'header') {\n rendered.push(<HeaderRow key={`r${i}`} label={row.label} width={innerWidth} />);\n } else if (row.type === 'task') {\n rendered.push(\n <TaskListRow key={`r${i}`} task={row.task} selected={false} focused={false} width={innerWidth} />,\n );\n selectableIdx++;\n }\n }\n while (rendered.length < contentHeight) {\n rendered.push(<EmptyRow key={`e${rendered.length}`} width={innerWidth} />);\n }\n\n const title = 'Tasks';\n const topBorder = '┌─ ' + title + ' ' + '─'.repeat(Math.max(0, innerWidth - title.length - 3)) + '┐';\n\n return (\n <Box flexDirection=\"column\" width={width}>\n <Text color={borderColor}>{topBorder}</Text>\n {rendered.map((row, i) => (\n <Box key={i}>\n <Text color={borderColor}>│</Text>\n {row}\n <Text color={borderColor}>│</Text>\n </Box>\n ))}\n <Text color={borderColor}>{'└' + '─'.repeat(innerWidth) + '┘'}</Text>\n </Box>\n );\n }\n\n return (\n <BorderedPane\n rows={taskRows}\n cursor={cursor}\n focused={focused}\n borderColor={borderColor}\n title=\"Tasks\"\n width={width}\n height={height}\n renderRow={(row, i, sel) => {\n if (row.type === 'task') {\n return (\n <TaskListRow\n key={i}\n task={row.task}\n selected={sel}\n focused={focused}\n width={innerWidth}\n />\n );\n }\n return <EmptyRow key={i} width={innerWidth} />;\n }}\n />\n );\n}\n\nexport function JiraPane({ jiraRows, cursor, focused, width, height }: JiraPaneProps) {\n const borderColor = focused ? 'cyan' : 'gray';\n const innerWidth = width - 2;\n\n return (\n <BorderedPane\n rows={jiraRows}\n cursor={cursor}\n focused={focused}\n borderColor={borderColor}\n title=\"Jira\"\n width={width}\n height={height}\n renderRow={(row, i, sel) => {\n if (row.type === 'jira') {\n return (\n <JiraListRow\n key={i}\n issue={row.issue}\n selected={sel}\n focused={focused}\n width={innerWidth}\n />\n );\n }\n return <EmptyRow key={i} width={innerWidth} />;\n }}\n />\n );\n}\n","import React from 'react';\nimport { Box, Text } from 'ink';\n\nexport interface TerminalPaneProps {\n lines: string[];\n width: number;\n height: number;\n focused: boolean;\n placeholder?: string;\n title?: string;\n}\n\nexport function TerminalPane({ lines, width, height, focused, placeholder, title }: TerminalPaneProps) {\n const borderColor = focused ? 'cyan' : 'gray';\n const innerWidth = width - 2;\n const contentHeight = height - 2;\n\n const content: React.ReactNode[] = [];\n\n if (lines.length === 0 && placeholder) {\n // Center placeholder message\n for (let i = 0; i < contentHeight; i++) {\n const mid = Math.floor(contentHeight / 2);\n if (i === mid) {\n const pad = Math.max(0, Math.floor((innerWidth - placeholder.length) / 2));\n content.push(\n <Text key={i} dimColor>\n {' '.repeat(pad) + placeholder}\n </Text>,\n );\n } else {\n content.push(<Text key={i}>{' '}</Text>);\n }\n }\n } else {\n for (let i = 0; i < contentHeight; i++) {\n if (i < lines.length) {\n content.push(<Text key={i}>{lines[i]}</Text>);\n } else {\n content.push(<Text key={i}>{' '}</Text>);\n }\n }\n }\n\n return (\n <Box flexDirection=\"column\" width={width}>\n <Text color={borderColor}>{title ? '┌─ ' + title + ' ' + '─'.repeat(Math.max(0, innerWidth - title.length - 3)) + '┐' : '┌' + '─'.repeat(innerWidth) + '┐'}</Text>\n {content.map((row, i) => (\n <Box key={i}>\n <Text color={borderColor}>{'│'}</Text>\n <Box width={innerWidth}>{row}</Box>\n <Text color={borderColor}>{'│'}</Text>\n </Box>\n ))}\n <Text color={borderColor}>{'└' + '─'.repeat(innerWidth) + '┘'}</Text>\n </Box>\n );\n}\n","import React from 'react';\nimport { Box, Text } from 'ink';\n\nexport interface StatusBarProps {\n message: string;\n pane: 'sessions' | 'tasks' | 'prs' | 'jira' | 'terminal';\n syncing?: boolean;\n}\n\nconst PANE_HINTS: Record<StatusBarProps['pane'], string> = {\n sessions: ' tab pane j/k nav enter start n new d remove . editor u rebase g sync G sync all q quit ',\n tasks: ' tab pane j/k nav enter toggle a add e edit w worktree d remove g sync G sync all q quit ',\n prs: ' tab pane j/k nav enter checkout g sync G sync all q quit ',\n jira: ' tab pane j/k nav enter worktree o open g sync G sync all q quit ',\n terminal: ' tab pane ctrl+] detach ',\n};\n\nexport function StatusBar({ message, pane, syncing }: StatusBarProps) {\n if (message) {\n return (\n <Box>\n <Text color=\"yellow\"> {message}</Text>\n {syncing && <Box flexGrow={1} />}\n {syncing && <Text color=\"cyan\"> syncing</Text>}\n </Box>\n );\n }\n\n return (\n <Box>\n <Text dimColor>{PANE_HINTS[pane]}</Text>\n {pane === 'sessions' && (\n <>\n <Text dimColor>{'['}</Text>\n <Text color=\"green\">●</Text>\n <Text dimColor>{' working '}</Text>\n <Text color=\"yellow\">◆</Text>\n <Text dimColor>{' needs input '}</Text>\n <Text dimColor>{'○ stopped]'}</Text>\n </>\n )}\n {syncing && <Box flexGrow={1} />}\n {syncing && <Text color=\"cyan\"> syncing</Text>}\n </Box>\n );\n}\n","import type { CommandModule } from 'yargs';\nimport { ensureConfig } from '../core/config.js';\nimport { startDashboard } from '../tui-ink/index.js';\n\nexport const dashCommand: CommandModule = {\n command: 'dash',\n describe: 'Interactive dashboard for all worktree sessions',\n builder: (yargs) =>\n yargs.option('unsafe', {\n describe: 'Launch the AI tool with its skip-permissions flag',\n type: 'boolean',\n default: false,\n }),\n handler: async (argv) => {\n ensureConfig();\n await startDashboard(argv.unsafe as boolean);\n },\n};\n","import chalk from 'chalk';\r\nimport type { CommandModule } from 'yargs';\r\nimport {\r\n setupCompletions,\r\n printCompletionResults,\r\n printManualInstructions,\r\n} from '../core/setup-completions.js';\r\n\r\nconst POWERSHELL_SCRIPT = `\r\nRegister-ArgumentCompleter -CommandName work -Native -ScriptBlock {\r\n param($wordToComplete, $commandAst, $cursorPosition)\r\n\r\n # When $wordToComplete is non-empty, the last CommandElement IS that word.\r\n # Exclude it so we don't double-pass it.\r\n $endIdx = $commandAst.CommandElements.Count\r\n if ($wordToComplete -ne '') { $endIdx -= 1 }\r\n\r\n $completedArgs = @()\r\n for ($i = 1; $i -lt $endIdx; $i++) {\r\n $el = $commandAst.CommandElements[$i]\r\n if ($el -is [System.Management.Automation.Language.CommandParameterAst]) { continue }\r\n $completedArgs += $el.ToString()\r\n }\r\n\r\n $results = & work --get-yargs-completions work @completedArgs $wordToComplete 2>$null\r\n $results | ForEach-Object {\r\n if ($_.Trim()) {\r\n [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)\r\n }\r\n }\r\n}\r\n`.trim();\r\n\r\nconst BASH_SCRIPT = `\r\n###-begin-work-completions-###\r\n_work_yargs_completions()\r\n{\r\n local cur_word args type_list\r\n cur_word=\"\\${COMP_WORDS[COMP_CWORD]}\"\r\n args=(\"\\${COMP_WORDS[@]}\")\r\n type_list=$(work --get-yargs-completions \"\\${args[@]}\")\r\n COMPREPLY=( $(compgen -W \"\\${type_list}\" -- \\${cur_word}) )\r\n if [ \\${#COMPREPLY[@]} -eq 0 ]; then\r\n COMPREPLY=()\r\n fi\r\n return 0\r\n}\r\ncomplete -o bashdefault -o default -F _work_yargs_completions work\r\n###-end-work-completions-###\r\n`.trim();\r\n\r\nconst ZSH_SCRIPT = `\r\n###-begin-work-completions-###\r\nautoload -Uz bashcompinit 2>/dev/null && bashcompinit 2>/dev/null\r\n_work_yargs_completions()\r\n{\r\n local cur_word args type_list\r\n cur_word=\"\\${COMP_WORDS[COMP_CWORD]}\"\r\n args=(\"\\${COMP_WORDS[@]}\")\r\n type_list=$(work --get-yargs-completions \"\\${args[@]}\")\r\n COMPREPLY=( $(compgen -W \"\\${type_list}\" -- \\${cur_word}) )\r\n if [ \\${#COMPREPLY[@]} -eq 0 ]; then\r\n COMPREPLY=()\r\n fi\r\n return 0\r\n}\r\ncomplete -o default -F _work_yargs_completions work\r\n###-end-work-completions-###\r\n`.trim();\r\n\r\nconst FISH_SCRIPT = `\r\n# work tab completions\r\nfunction __work_complete\r\n set -l cmd (commandline -opc)\r\n set -l cur (commandline -ct)\r\n work --get-yargs-completions $cmd $cur 2>/dev/null\r\nend\r\n\r\ncomplete -c work -f -a '(__work_complete)'\r\n`.trim();\r\n\r\nexport const completionCommand: CommandModule = {\r\n command: 'completion',\r\n describe: 'Generate shell completion script',\r\n builder: (yargs) =>\r\n yargs\r\n .option('shell', {\r\n describe: 'Shell type',\r\n choices: ['bash', 'zsh', 'fish', 'powershell', 'ps'] as const,\r\n type: 'string',\r\n })\r\n .option('install', {\r\n describe: 'Install completions into shell profile(s)',\r\n type: 'boolean',\r\n default: false,\r\n }),\r\n handler: (argv) => {\r\n if (argv.install) {\r\n const results = setupCompletions();\r\n if (results.length > 0) {\r\n printCompletionResults(results);\r\n console.log('');\r\n console.log(\r\n chalk.gray(' Restart your shell for completions to take effect.'),\r\n );\r\n } else {\r\n printManualInstructions();\r\n }\r\n return;\r\n }\r\n\r\n const shell = argv.shell as string | undefined;\r\n\r\n if (shell === 'powershell' || shell === 'ps') {\r\n console.log(POWERSHELL_SCRIPT);\r\n } else if (shell === 'zsh') {\r\n console.log(ZSH_SCRIPT);\r\n } else if (shell === 'fish') {\r\n console.log(FISH_SCRIPT);\r\n } else {\r\n console.log(BASH_SCRIPT);\r\n }\r\n },\r\n};\r\n","import chalk from 'chalk';\r\nimport type { CommandModule } from 'yargs';\r\nimport { getTasks, addTask, completeTask, uncompleteTask, removeTask, editTask } from '../core/tasks.js';\r\n\r\nexport const todoCommand: CommandModule = {\r\n command: 'todo [action]',\r\n describe: 'Manage tasks',\r\n builder: (yargs) =>\r\n yargs\r\n .positional('action', {\r\n describe: 'Action: add, done, undo, rm, edit (omit to list)',\r\n type: 'string',\r\n })\r\n .option('all', {\r\n describe: 'Show completed tasks too',\r\n type: 'boolean',\r\n default: false,\r\n alias: 'a',\r\n })\r\n .strict(false),\r\n handler: async (argv) => {\r\n const action = argv.action as string | undefined;\r\n const rest = (argv._ as string[]).slice(1);\r\n\r\n if (!action || action === 'list') {\r\n const showAll = argv.all as boolean;\r\n const tasks = getTasks();\r\n const filtered = showAll ? tasks : tasks.filter((t) => !t.done);\r\n if (filtered.length === 0) {\r\n console.log(chalk.gray(showAll ? 'No tasks.' : 'No open tasks. Use --all to show completed.'));\r\n return;\r\n }\r\n for (const t of filtered) {\r\n const check = t.done ? chalk.green('✓') : chalk.gray('○');\r\n const text = t.done ? chalk.strikethrough.gray(t.text) : t.text;\r\n const link = t.link ? chalk.blue(` [${t.link}]`) : '';\r\n console.log(` ${check} ${chalk.gray(`#${t.id}`)} ${text}${link}`);\r\n }\r\n return;\r\n }\r\n\r\n if (action === 'add') {\r\n const text = rest.join(' ');\r\n if (!text) {\r\n console.error('Usage: work todo add <text>');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n const task = await addTask(text);\r\n console.log(chalk.green(`Added #${task.id}: ${task.text}`));\r\n return;\r\n }\r\n\r\n if (action === 'done') {\r\n if (!/^\\d+$/.test(rest[0] ?? '')) {\r\n console.error('Invalid task id');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n const id = parseInt(rest[0], 10);\r\n if (isNaN(id)) {\r\n console.error('Usage: work todo done <id>');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n const task = await completeTask(id);\r\n if (!task) {\r\n console.error(`Task #${id} not found`);\r\n process.exitCode = 1;\r\n } else {\r\n console.log(chalk.green(`✓ #${id}: ${task.text}`));\r\n }\r\n return;\r\n }\r\n\r\n if (action === 'undo') {\r\n if (!/^\\d+$/.test(rest[0] ?? '')) {\r\n console.error('Invalid task id');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n const id = parseInt(rest[0], 10);\r\n if (isNaN(id)) {\r\n console.error('Usage: work todo undo <id>');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n const task = await uncompleteTask(id);\r\n if (!task) {\r\n console.error(`Task #${id} not found`);\r\n process.exitCode = 1;\r\n } else {\r\n console.log(chalk.yellow(`○ #${id}: ${task.text}`));\r\n }\r\n return;\r\n }\r\n\r\n if (action === 'rm') {\r\n if (!/^\\d+$/.test(rest[0] ?? '')) {\r\n console.error('Invalid task id');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n const id = parseInt(rest[0], 10);\r\n if (isNaN(id)) {\r\n console.error('Usage: work todo rm <id>');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n const task = await removeTask(id);\r\n if (!task) {\r\n console.error(`Task #${id} not found`);\r\n process.exitCode = 1;\r\n } else {\r\n console.log(chalk.red(`Removed #${id}: ${task.text}`));\r\n }\r\n return;\r\n }\r\n\r\n if (action === 'edit') {\r\n if (!/^\\d+$/.test(rest[0] ?? '')) {\r\n console.error('Invalid task id');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n const id = parseInt(rest[0], 10);\r\n const text = rest.slice(1).join(' ');\r\n if (isNaN(id) || !text) {\r\n console.error('Usage: work todo edit <id> <new text>');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n const task = await editTask(id, text);\r\n if (!task) {\r\n console.error(`Task #${id} not found`);\r\n process.exitCode = 1;\r\n } else {\r\n console.log(chalk.cyan(`Updated #${id}: ${task.text}`));\r\n }\r\n return;\r\n }\r\n\r\n console.error(`Unknown action: ${action}`);\r\n console.log(chalk.yellow('Actions: add, done, undo, rm, edit'));\r\n process.exitCode = 1;\r\n },\r\n};\r\n","import chalk from 'chalk';\nimport type { CommandModule } from 'yargs';\nimport { ensureConfig } from '../core/config.js';\nimport { hydrateHistoryFromDisk } from '../core/hydrate.js';\n\nexport const hydrateCommand: CommandModule = {\n command: 'hydrate',\n describe:\n 'Scan worktreesRoot for existing worktrees and add untracked ones to history',\n builder: (yargs) => yargs,\n handler: async () => {\n const config = ensureConfig();\n const { discovered, added, updated } = await hydrateHistoryFromDisk(config);\n\n console.log('');\n console.log(chalk.cyan(`Discovered ${discovered} worktree(s) on disk.`));\n if (added > 0) {\n console.log(chalk.green(`Added ${added} new session(s) to history.`));\n }\n if (updated > 0) {\n console.log(chalk.yellow(`Updated paths for ${updated} session(s).`));\n }\n if (added === 0 && updated === 0) {\n console.log(chalk.gray('History already in sync with disk.'));\n }\n },\n};\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { WorkConfig } from './config.js';\nimport { parseWorktreeList } from './git.js';\nimport {\n type WorktreeSession,\n mergeHydratedSessions,\n} from './history.js';\n\ninterface HydrateResult {\n discovered: number;\n added: number;\n updated: number;\n}\n\n/**\n * Scan configured repos for worktrees under worktreesRoot and seed history\n * with any that aren't already tracked. Used for recovery from a wiped\n * history.json and to adopt worktrees created outside work.\n */\nexport async function hydrateHistoryFromDisk(\n config: WorkConfig,\n): Promise<HydrateResult> {\n const worktreesRoot = path.resolve(config.worktreesRoot);\n const folderNameToAlias = new Map<string, string>();\n for (const [alias, repoPath] of Object.entries(config.repos)) {\n folderNameToAlias.set(path.basename(repoPath), alias);\n }\n\n // Key: `${target}:${branch}`\n const discovered = new Map<string, WorktreeSession>();\n\n for (const [alias, repoPath] of Object.entries(config.repos)) {\n if (!fs.existsSync(repoPath)) continue;\n\n for (const entry of parseWorktreeList(repoPath)) {\n if (!entry.branch) continue;\n if (path.resolve(entry.path) === path.resolve(repoPath)) continue;\n\n const rel = path.relative(worktreesRoot, entry.path);\n if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) continue;\n\n const parts = rel.split(path.sep).filter(Boolean);\n if (parts.length < 2) continue;\n\n const [first, second] = parts;\n const isGroup = Object.prototype.hasOwnProperty.call(config.groups, first);\n\n if (isGroup) {\n const groupName = first;\n const key = `${groupName}:${entry.branch}`;\n const existing = discovered.get(key);\n if (existing) {\n if (!existing.paths.includes(entry.path)) {\n existing.paths.push(entry.path);\n existing.paths.sort();\n }\n } else {\n const { createdAt, lastAccessedAt } = inferTimestamps(entry.path);\n discovered.set(key, {\n target: groupName,\n isGroup: true,\n branch: entry.branch,\n paths: [entry.path],\n createdAt,\n lastAccessedAt,\n });\n }\n // silence unused warning for `second` — branch-dir segment isn't needed\n void second;\n } else {\n // Single-repo worktree. `first` must be the repo's folder name.\n const aliasFromFolder = folderNameToAlias.get(first);\n if (aliasFromFolder !== alias) continue;\n\n const key = `${alias}:${entry.branch}`;\n if (discovered.has(key)) continue;\n const { createdAt, lastAccessedAt } = inferTimestamps(entry.path);\n discovered.set(key, {\n target: alias,\n isGroup: false,\n branch: entry.branch,\n paths: [entry.path],\n createdAt,\n lastAccessedAt,\n });\n }\n }\n }\n\n const sessions = [...discovered.values()];\n const { added, updated } = await mergeHydratedSessions(sessions);\n return { discovered: sessions.length, added, updated };\n}\n\nfunction inferTimestamps(p: string): {\n createdAt: string;\n lastAccessedAt: string;\n} {\n try {\n const stat = fs.statSync(p);\n const created = stat.birthtime.getTime() > 0 ? stat.birthtime : stat.mtime;\n return {\n createdAt: created.toISOString(),\n lastAccessedAt: stat.mtime.toISOString(),\n };\n } catch {\n const now = new Date().toISOString();\n return { createdAt: now, lastAccessedAt: now };\n }\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { spawn as childSpawn } from 'node:child_process';\nimport chalk from 'chalk';\nimport type { CommandModule } from 'yargs';\nimport { computeDiff } from '../core/diff-pipeline.js';\nimport { stableDiffPath, type RepoSpec } from '../core/repo-spec.js';\nimport {\n buildRepoSpecs,\n findAnyParentBranch,\n resolveBase,\n resolveScope,\n type DiffScope,\n type ResolvedBase,\n} from '../core/diff-scope.js';\nimport {\n formatSingleComment,\n startCommentServer,\n startReadOnlyDiffServer,\n} from '../core/comment-server.js';\nimport { diffReviewSnapshot } from '../core/review-poll.js';\nimport { renderStatic } from '../core/static-renderer.js';\nimport { openUrl } from '../utils/platform.js';\n\n/** Write an informational message to stderr. Keeps stdout clean so it can\n * be piped or captured by callers (notably `wd -c` review mode, where\n * stdout carries the comments markdown payload). */\nfunction info(message: string): void {\n process.stderr.write(message + '\\n');\n}\n\n// pid / process helpers\nfunction isPidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction readPid(pidPath: string): number | null {\n try {\n const raw = fs.readFileSync(pidPath, 'utf-8').trim();\n const n = Number(raw);\n return Number.isFinite(n) && n > 0 ? n : null;\n } catch {\n return null;\n }\n}\n\nfunction spawnDaemon(extraArgs: string[], logPath: string): number {\n const out = fs.openSync(logPath, 'a');\n const child = childSpawn(\n process.execPath,\n [process.argv[1], ...extraArgs, '--watch-daemon'],\n {\n detached: true,\n stdio: ['ignore', out, out],\n windowsHide: true,\n cwd: process.cwd(),\n },\n );\n child.unref();\n fs.closeSync(out);\n return child.pid ?? 0;\n}\n\n// paths\ninterface ScopePaths {\n /** Stable per-scope key; the daemon writes a URL file here. */\n base: string;\n pid: string;\n log: string;\n url: string;\n}\n\nfunction pathsForScope(repoSpecs: RepoSpec[]): ScopePaths {\n const base = stableDiffPath(repoSpecs.map((r) => r.root));\n return {\n base,\n pid: `${base}.pid`,\n log: `${base}.log`,\n url: `${base}.url`,\n };\n}\n\n// mode handlers\ninterface RenderContext {\n scope: DiffScope;\n base: string;\n baseSource: ResolvedBase['source'];\n repoSpecs: RepoSpec[];\n paths: ScopePaths;\n scopeLabel: string;\n}\n\nfunction runStop(paths: ScopePaths): void {\n const pid = readPid(paths.pid);\n if (pid && isPidAlive(pid)) {\n try {\n process.kill(pid);\n info(chalk.gray(`Stopped watcher (PID ${pid}).`));\n } catch (err) {\n console.error(chalk.red('Failed to stop watcher:'), (err as Error).message);\n }\n } else {\n info(chalk.gray('No watcher running for this scope.'));\n }\n try { fs.unlinkSync(paths.pid); } catch { /* */ }\n try { fs.unlinkSync(paths.url); } catch { /* */ }\n}\n\n/** Foreground daemon entrypoint. Starts the read-only review server and\n * blocks until killed. The launcher reads the printed URL from the log. */\nasync function runDaemon(ctx: RenderContext): Promise<void> {\n fs.writeFileSync(ctx.paths.pid, String(process.pid));\n const handle = await startReadOnlyDiffServer({\n repos: ctx.repoSpecs,\n scopeLabel: ctx.scopeLabel,\n sessionBaseBranch: ctx.scope.session?.baseBranch,\n });\n fs.writeFileSync(ctx.paths.url, handle.url);\n info(\n chalk.gray(\n `[live] watcher started, pid=${process.pid}, repos=${ctx.repoSpecs.map((r) => r.name).join(',')}, base=${ctx.base}, url=${handle.url}`,\n ),\n );\n const shutdown = () => {\n handle.stop();\n try { fs.unlinkSync(ctx.paths.pid); } catch { /* */ }\n try { fs.unlinkSync(ctx.paths.url); } catch { /* */ }\n process.exit(0);\n };\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n await new Promise(() => {});\n}\n\n/**\n * Try to register this scope with a running `work web` server. Returns\n * the browser URL to open (e.g. `http://127.0.0.1:54321/diff/abc123`)\n * when work web is up and accepting registrations, null otherwise.\n *\n * Reads `~/.work/web.url` for the discovery handshake. Failure paths\n * (file missing, server unreachable, register endpoint returns non-200)\n * all yield null cleanly — the caller falls back to spawning its own\n * standalone daemon.\n */\nasync function tryRegisterWithWorkWeb(\n ctx: RenderContext,\n routeKind: 'diff' | 'review',\n): Promise<string | null> {\n const webUrlFile = path.join(os.homedir(), '.work', 'web.url');\n let webUrl: string;\n try {\n webUrl = fs.readFileSync(webUrlFile, 'utf-8').trim();\n if (!webUrl) return null;\n } catch {\n return null;\n }\n try {\n const res = await fetch(`${webUrl}api/scopes`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n paths: ctx.repoSpecs.map((r) => r.root),\n label: ctx.scopeLabel,\n }),\n });\n if (!res.ok) return null;\n const body = (await res.json()) as { hash: string };\n const route = routeKind === 'diff' ? 'diff' : 'review';\n return `${webUrl}${route}/${body.hash}`;\n } catch {\n return null;\n }\n}\n\n/** Foreground launcher: opens a diff in the browser. Prefers a running\n * `work web` (registers a scope, opens /diff/<hash>) so a single server\n * process backs every `wd` invocation. Falls back to spawning a\n * standalone daemon when work web isn't running. */\nasync function runLauncher(ctx: RenderContext): Promise<void> {\n // Try work web first. When the dashboard is running, every `wd`\n // invocation lives inside that one server — no extra processes, no\n // extra ports, browser-bookmarkable URLs.\n const webRouteUrl = await tryRegisterWithWorkWeb(ctx, 'diff');\n if (webRouteUrl) {\n info(chalk.gray(`Opening in work web: ${webRouteUrl}`));\n openUrl(webRouteUrl);\n return;\n }\n\n // Fallback: standalone daemon. Same lifecycle we had before\n // (pid + url + log files keyed by scope hash).\n const existing = readPid(ctx.paths.pid);\n let url: string | null = null;\n if (existing && isPidAlive(existing)) {\n info(chalk.gray(`Watcher already running (PID ${existing}).`));\n try { url = fs.readFileSync(ctx.paths.url, 'utf-8').trim(); } catch { /* */ }\n } else {\n try { fs.unlinkSync(ctx.paths.pid); } catch { /* stale */ }\n try { fs.unlinkSync(ctx.paths.url); } catch { /* stale */ }\n const passthrough = process.argv\n .slice(2)\n .filter((a) => a !== '--stop' && a !== '--watch');\n const pid = spawnDaemon(passthrough, ctx.paths.log);\n info(chalk.gray(`Started watcher (PID ${pid}). Log: ${ctx.paths.log}`));\n info(chalk.gray('Stop with: wd --stop'));\n url = await waitForUrlFile(ctx.paths.url, 3000);\n }\n if (!url) {\n console.error(\n chalk.red('Watcher did not report a URL — check the log:'),\n ctx.paths.log,\n );\n return;\n }\n info(chalk.gray(`URL: ${url}`));\n openUrl(url);\n}\n\n/**\n * Static-file mode (the default for `wd`). Renders the React SPA shell\n * with the diff data inlined, writes it to ~/.work/diffs/<hash>.html,\n * opens the browser at file://…, and exits. No server, no daemon, no\n * port — the file works forever (modulo your filesystem). Same React\n * components and screens as the live server: file tree, scrollspy,\n * viewed checkboxes, syntax highlighting, intra-line diff.\n *\n * Trade-off: no live reload, no comments. Use `wd --server` (or `wd -c`\n * for review mode) when you need those.\n */\nfunction runStatic(ctx: RenderContext, initialBranch: boolean): void {\n // Always compute the uncommitted scope (cheap, the user expects it as\n // the default tab).\n const uncommitted = buildRepoSpecs(ctx.scope, 'HEAD');\n\n // Try to resolve a parent branch for each repo. If we find one, compute\n // the \"since branch\" scope too so the SPA can offer the toggle. If not,\n // the tab simply doesn't appear.\n //\n // Group worktrees: each sub-repo may have a different parent. We pick a\n // representative resolvedBase from the active repo (or the first one)\n // for the badge label; per-repo merge-base lookups still happen inside\n // buildRepoSpecs.\n const primaryRoot =\n ctx.scope.repos.find((r) => r.name === ctx.scope.activeRepoName)?.root ??\n ctx.scope.repos[0].root;\n // Use the lenient finder so the toggle stays available even when the\n // branch has no commits past its parent yet (the diff will just be\n // empty — better than no tab at all).\n const parent =\n ctx.scope.session?.baseBranch ?? findAnyParentBranch(primaryRoot);\n const branch =\n parent === null\n ? undefined\n : {\n specs: buildRepoSpecs(ctx.scope, parent),\n resolvedBase: parent,\n };\n\n // Don't write a file when both views are empty — `wd` is a viewer,\n // not a generator of empty pages.\n const uncommittedTotal = uncommitted.reduce(\n (s, r) => s + computeDiff({ root: r.root, diffArg: r.diffArg }).length,\n 0,\n );\n const branchTotal = branch\n ? branch.specs.reduce(\n (s, r) => s + computeDiff({ root: r.root, diffArg: r.diffArg }).length,\n 0,\n )\n : 0;\n if (uncommittedTotal === 0 && branchTotal === 0) {\n info(chalk.gray('No changes to show.'));\n return;\n }\n\n const html = renderStatic({\n scopeLabel: ctx.scopeLabel,\n uncommitted,\n branch,\n initialBase: initialBranch && branch ? 'branch' : 'uncommitted',\n });\n const filePath = `${ctx.paths.base}.html`;\n fs.writeFileSync(filePath, html, 'utf-8');\n info(chalk.gray(`Wrote ${filePath}`));\n openUrl(`file:///${filePath.replace(/\\\\/g, '/')}`);\n}\n\nfunction waitForUrlFile(filePath: string, timeoutMs: number): Promise<string | null> {\n return new Promise((resolve) => {\n const start = Date.now();\n const tick = () => {\n try {\n const v = fs.readFileSync(filePath, 'utf-8').trim();\n if (v) return resolve(v);\n } catch { /* not yet */ }\n if (Date.now() - start > timeoutMs) return resolve(null);\n setTimeout(tick, 75);\n };\n tick();\n });\n}\n\n/**\n * Try to route `wd -c` through a running `work web`. Registers the scope\n * as a reviewable view, opens the browser at /review/<hash>, then polls\n * the scope's comments and proxies them to stdout as the markers the\n * `wd-review` skill consumes (`--- review started ---`,\n * `--- comment ---`, etc.). Returns true if work web handled the review;\n * the caller falls back to a standalone server when this returns false.\n */\nasync function tryReviewViaWorkWeb(ctx: RenderContext): Promise<boolean> {\n const webUrlFile = path.join(os.homedir(), '.work', 'web.url');\n let webUrl: string;\n try {\n webUrl = fs.readFileSync(webUrlFile, 'utf-8').trim();\n if (!webUrl) return false;\n } catch {\n return false;\n }\n\n let hash: string;\n try {\n const res = await fetch(`${webUrl}api/scopes`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n paths: ctx.repoSpecs.map((r) => r.root),\n label: ctx.scopeLabel,\n }),\n });\n if (!res.ok) return false;\n hash = ((await res.json()) as { hash: string }).hash;\n } catch {\n return false;\n }\n\n const initialTotal = ctx.repoSpecs.reduce(\n (s, r) => s + computeDiff({ root: r.root, diffArg: r.diffArg }).length,\n 0,\n );\n const reviewUrl = `${webUrl}review/${hash}`;\n info(chalk.gray(`Opening review in work web: ${reviewUrl}`));\n info(chalk.gray('Comments stream below. Ctrl+C to detach.'));\n process.stdout.write(\n `--- review started ---\\nrepos: ${ctx.repoSpecs.map((r) => r.name).join(', ')}\\nfiles: ${initialTotal}\\nurl: ${reviewUrl}\\n\\n`,\n );\n openUrl(reviewUrl);\n\n // Poll the scope's comments. Cheap (one localhost JSON GET) and\n // simpler than reading SSE chunks in Node. The skill latency budget\n // is generous so 1s is fine.\n const seen = new Set<string>();\n let exiting = false;\n let interval: NodeJS.Timeout | null = null;\n // Reentrancy guard. `setInterval(poll, 1000)` fires every second\n // regardless of whether the previous tick has resolved. A slow `work\n // web` response (>1s) would otherwise let two `poll()`s mutate `seen`\n // concurrently. JS being single-threaded makes interleaving impossible\n // for synchronous blocks but the await points do let two flows\n // interleave — duplicate `--- review done ---` markers if `ended:true`\n // races with a slow prior poll.\n let polling = false;\n\n type SnapshotComment = import('../core/comment-types.js').Comment;\n\n async function poll(): Promise<void> {\n if (exiting || polling) return;\n polling = true;\n try {\n const res = await fetch(`${webUrl}api/scopes/${hash}/comments`);\n if (res.status === 404) {\n // Scope is gone (user explicitly removed it). Treat like\n // End Review for the marker stream.\n process.stdout.write(`--- review done ---\\ntotal: ${seen.size}\\n`);\n cleanup();\n process.exit(0);\n }\n if (!res.ok) return;\n const { comments, ended } = (await res.json()) as {\n comments: SnapshotComment[];\n ended?: boolean;\n };\n // Compute deltas before checking `ended` so any comments posted\n // in the same batch as End Review still flush through.\n const { newComments, deleted } = diffReviewSnapshot(comments, seen);\n for (const id of deleted) {\n process.stdout.write(`--- comment deleted ---\\nid: ${id}\\n\\n`);\n }\n for (const c of newComments) {\n process.stdout.write(formatSingleComment(c));\n }\n // End Review fired: emit the done marker and exit. The scope\n // (and the browser tab) stays viewable — only the CLI proxy\n // stops.\n if (ended) {\n process.stdout.write(\n `--- review done ---\\ntotal: ${seen.size}\\n`,\n );\n cleanup();\n process.exit(0);\n }\n } catch { /* transient — retry next tick */ }\n finally { polling = false; }\n }\n\n function cleanup(): void {\n exiting = true;\n if (interval) clearInterval(interval);\n // Don't deregister — the browser tab and URL should keep working\n // after the CLI exits. Scopes live in work web's memory until it\n // restarts or the user removes them from the dashboard.\n }\n\n const onSignal = () => {\n process.stdout.write(`--- review aborted (signal) ---\\n`);\n cleanup();\n process.exit(0);\n };\n process.on('SIGINT', onSignal);\n process.on('SIGTERM', onSignal);\n\n // Arm the interval BEFORE the first poll. If the first poll triggers\n // cleanup (e.g. scope already ended, 404), `cleanup()` clears this\n // interval correctly — otherwise the previous shape left a small window\n // where setInterval would be assigned AFTER cleanup ran, which mocked\n // tests reproduced (the real runtime survives because process.exit is\n // synchronous, but tests that stub process.exit would leak the timer).\n interval = setInterval(poll, 1000);\n await poll();\n // Block forever — cleanup happens via signal or 404 detection.\n await new Promise(() => {});\n return true;\n}\n\nasync function runReview(ctx: RenderContext): Promise<void> {\n // Quick check — early-exit if nothing to review. (Server will recompute\n // on each /api/diff request thereafter, which is what's served live.)\n const initialTotal = ctx.repoSpecs.reduce(\n (s, r) => s + computeDiff({ root: r.root, diffArg: r.diffArg }).length,\n 0,\n );\n if (initialTotal === 0) {\n info(chalk.gray('No changes to review.'));\n return;\n }\n\n // Prefer work web when it's running — same consolidation pattern as\n // `wd` (read-only). The CLI proxies the marker stream so the\n // wd-review skill flow is unchanged. Falls back to a standalone\n // comment-server when work web isn't up.\n if (await tryReviewViaWorkWeb(ctx)) return;\n\n const onComment = (c: import('../core/comment-server.js').Comment) => {\n process.stdout.write(formatSingleComment(c));\n };\n const onCommentDeleted = (id: string) => {\n process.stdout.write(`--- comment deleted ---\\nid: ${id}\\n\\n`);\n };\n const onSubmitReviewStart = (info: {\n count: number;\n summary: import('../core/comment-server.js').Comment | null;\n }) => {\n const head = `--- review submitted ---\\ncount: ${info.count}${info.summary ? `\\nsummary-id: ${info.summary.id}` : ''}\\n\\n`;\n process.stdout.write(head);\n };\n const onSubmitReviewEnd = () => {\n process.stdout.write(`--- review batch end ---\\n\\n`);\n };\n\n const handle = await startCommentServer({\n repos: ctx.repoSpecs,\n scopeLabel: ctx.scopeLabel,\n sessionBaseBranch: ctx.scope.session?.baseBranch,\n onComment,\n onCommentDeleted,\n onSubmitReviewStart,\n onSubmitReviewEnd,\n });\n\n // URL discovery is intentionally not persisted to disk. The only\n // consumer (Claude via the wd-review skill) reads it from the\n // `--- review started ---` marker on stdout below. Skipping the\n // file write removes the only way a stale or wrong URL could leak\n // into a different Claude session.\n info(chalk.gray('Opening browser for review. Comments stream as you save them.'));\n info(chalk.gray('Page reloads automatically when you save a file. Click \"End review\" (or Ctrl+C) when finished.'));\n process.stdout.write(\n `--- review started ---\\nrepos: ${ctx.repoSpecs.map((r) => r.name).join(', ')}\\nfiles: ${initialTotal}\\nurl: ${handle.url}\\n\\n`,\n );\n openUrl(handle.url);\n\n const cleanup = () => {\n handle.stop();\n };\n\n const onSignal = () => {\n process.stdout.write(`--- review aborted (signal) ---\\n`);\n cleanup();\n process.exit(0);\n };\n process.on('SIGINT', onSignal);\n process.on('SIGTERM', onSignal);\n\n const comments = await handle.waitForDone();\n await new Promise((r) => setTimeout(r, 100));\n cleanup();\n process.stdout.write(`--- review done ---\\ntotal: ${comments.length}\\n`);\n}\n\n// command export\nexport const diffCommand: CommandModule = {\n command: 'diff [base]',\n describe: 'Open a GitHub-PR-style diff overview in your browser',\n builder: (yargs) =>\n yargs\n .positional('base', {\n describe:\n 'Base ref to compare against. Default: HEAD (uncommitted only). Use --branch for a full PR-style diff vs the parent branch.',\n type: 'string',\n })\n .option('branch', {\n type: 'boolean',\n default: false,\n describe:\n 'Open the \"Since branch\" tab by default (still shows uncommitted as the other tab — toggle in the browser).',\n })\n .option('static', {\n type: 'boolean',\n default: false,\n describe:\n 'Write a self-contained HTML file with the current diff inlined (no server, no live reload). The default is a live server you can refresh.',\n })\n .option('server', {\n type: 'boolean',\n default: false,\n hidden: true,\n describe:\n 'Run a live server (now the default; flag kept for back-compat).',\n })\n .option('watch', {\n type: 'boolean',\n default: false,\n hidden: true,\n describe: 'Alias for --server. Kept for back-compat.',\n })\n .option('stop', {\n type: 'boolean',\n default: false,\n describe: 'Stop the background server for this scope.',\n })\n .option('watch-daemon', {\n type: 'boolean',\n default: false,\n hidden: true,\n describe: 'Internal: run the foreground watcher loop.',\n })\n .option('comments', {\n type: 'boolean',\n alias: 'c',\n default: false,\n describe:\n 'Review mode: open the diff in a browser with a comment UI; block until you click \"Done & Send\", then print all comments to stdout as markdown.',\n }),\n handler: async (argv) => {\n const scope = resolveScope(process.cwd());\n if (!scope) {\n console.error(chalk.red('Not inside a git repository or known worktree.'));\n process.exit(1);\n }\n\n const { base, source: baseSource } = resolveBase(scope, {\n base: argv.base as string | undefined,\n branch: argv.branch as boolean | undefined,\n });\n const repoSpecs = buildRepoSpecs(scope, base);\n const paths = pathsForScope(repoSpecs);\n\n // Use stderr for status messages so review mode's stdout stays clean\n // (the comments markdown is the only data wd writes to stdout).\n if (base === 'HEAD') {\n info(chalk.gray('Showing uncommitted changes vs HEAD.'));\n } else {\n info(\n chalk.gray(\n `Showing diff vs ${base} [${baseSource}]${scope.isGroup ? `, across ${scope.repos.length} repos` : ''}.`,\n ),\n );\n }\n\n const scopeLabel = `${scope.repos.map((r) => r.name).join(', ')} · ${base}`;\n const ctx: RenderContext = {\n scope,\n base,\n baseSource,\n repoSpecs,\n paths,\n scopeLabel,\n };\n\n if (argv.stop) return runStop(ctx.paths);\n if (argv['watch-daemon']) return runDaemon(ctx);\n if (argv.comments) return runReview(ctx);\n if (argv.static) return runStatic(ctx, !!argv.branch);\n // Default: live server. Refresh in the browser to see changes; stop\n // with `wd --stop`. Use --static for a self-contained HTML file.\n return runLauncher(ctx);\n },\n};\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport crypto from 'node:crypto';\nimport spawn from 'cross-spawn';\nimport { git } from './git.js';\nimport {\n parseGitDiff,\n type MarkdownContent,\n type ParsedFile,\n} from './diff-parse.js';\nimport { coverageLookup } from './lcov.js';\n\nconst MARKDOWN_EXT_RE = /\\.(md|markdown|mdx)$/i;\n\n/** Per-side cap on markdown content embedded in the diff payload. A\n * 256 KiB ceiling covers normal docs (README, CHANGELOG up to a year\n * of releases, design docs) while preventing auto-generated multi-MB\n * markdown (API refs, vendored docs) from ballooning SSE reload\n * traffic and the browser heap. When either side hits the cap, both\n * sides are dropped and `tooLarge: true` is set so the SPA hides the\n * Preview/Split toggle. */\nconst MARKDOWN_SIZE_CAP = 256 * 1024;\n\nfunction isMarkdownPath(p: string): boolean {\n return p !== '/dev/null' && MARKDOWN_EXT_RE.test(p);\n}\n\n/**\n * Reject paths that would resolve outside the repo root. Diff output is\n * parsed text — a pathological commit (or a manipulated diff stream) could\n * carry `../../etc/passwd`-style entries. Without this check, the markdown\n * \"after\" read would happily exfiltrate arbitrary local files into the\n * SPA's diff payload.\n *\n * Exported for unit testing — also useful to other call sites that\n * consume `ParsedFile.path` for filesystem access.\n */\nexport function isInsideRoot(root: string, rel: string): boolean {\n const resolvedRoot = path.resolve(root);\n const resolvedTarget = path.resolve(resolvedRoot, rel);\n // path.relative returns \"\" for the same dir, \"subdir/x\" for nested, or\n // a \"../\" path for escapes — easiest portable check.\n const r = path.relative(resolvedRoot, resolvedTarget);\n if (r === '') return true;\n if (r.startsWith('..')) return false;\n if (path.isAbsolute(r)) return false;\n return true;\n}\n\n/**\n * For markdown files, fetch the \"before\" content via `git show <fromRef>:<oldPath>`\n * and the \"after\" content either from the working tree (when comparing\n * against the working tree) or from `git show <toRef>:<newPath>` (when\n * comparing two committed snapshots). Either side may be absent for added\n * / deleted / failed fetches.\n *\n * Uses `cross-spawn` directly (not the `git()` helper) because that helper\n * trims stdout, which would silently drop trailing newlines in the source.\n */\nfunction readMarkdownContent(\n root: string,\n file: ParsedFile,\n fromRef: string,\n toRef: string | 'working',\n): MarkdownContent | undefined {\n if (file.isBinary) return undefined;\n if (!isMarkdownPath(file.oldPath) && !isMarkdownPath(file.newPath)) {\n return undefined;\n }\n\n const showAt = (ref: string, p: string): string | undefined => {\n const r = spawn.sync('git', ['show', `${ref}:${p}`], {\n cwd: root,\n encoding: 'utf-8',\n maxBuffer: 16 * 1024 * 1024,\n windowsHide: true,\n });\n if (r.status === 0 && typeof r.stdout === 'string') return r.stdout;\n return undefined;\n };\n\n const result: MarkdownContent = {};\n\n if (\n file.status !== 'added' &&\n isMarkdownPath(file.oldPath) &&\n isInsideRoot(root, file.oldPath)\n ) {\n result.before = showAt(fromRef, file.oldPath);\n }\n\n if (\n file.status !== 'deleted' &&\n isMarkdownPath(file.newPath) &&\n isInsideRoot(root, file.newPath)\n ) {\n if (toRef === 'working') {\n try {\n const absPath = path.join(root, file.newPath);\n // Resolve symlinks BEFORE reading. The `isInsideRoot` check\n // above blocks `../`-style traversal in the textual path, but\n // a repo could legitimately contain a symlink whose target\n // sits outside the worktree (e.g. `notes.md -> /etc/passwd`).\n // Without this, opening a clone of an adversarial repo would\n // exfiltrate the target into the diff payload.\n //\n // Both sides of the containment comparison MUST be canonical:\n // on macOS `/tmp` and `/var` are themselves symlinks (to\n // `/private/tmp` and `/private/var`), and `path.resolve` does\n // NOT follow symlinks — only `realpath` does. Without\n // canonicalising `root` too, every working-tree path under a\n // symlinked ancestor (default `/var/folders/...` TMPDIR target\n // on macOS, plus Linux mounts and Windows directory junctions)\n // resolves to a different namespace from `root`, the relative\n // path comes out `..`-prefixed, and the guard silently rejects\n // legitimate in-repo files.\n const realRoot = fs.realpathSync(path.resolve(root));\n const realPath = fs.realpathSync(absPath);\n const sep = path.sep;\n if (\n realPath === realRoot ||\n realPath.startsWith(realRoot + sep)\n ) {\n result.after = fs.readFileSync(absPath, 'utf-8');\n }\n } catch {\n // Working-tree file missing OR realpath failed — no after-content.\n }\n } else {\n result.after = showAt(toRef, file.newPath);\n }\n }\n\n if (result.before === undefined && result.after === undefined) {\n return undefined;\n }\n // Drop both sides when either exceeds the cap. We compare byte length\n // (Buffer.byteLength) rather than `.length`, which counts UTF-16\n // code units in JS and undercounts multi-byte characters — the\n // browser pays for bytes, not chars.\n const tooBig =\n (result.before !== undefined &&\n Buffer.byteLength(result.before, 'utf-8') > MARKDOWN_SIZE_CAP) ||\n (result.after !== undefined &&\n Buffer.byteLength(result.after, 'utf-8') > MARKDOWN_SIZE_CAP);\n if (tooBig) {\n return { tooLarge: true };\n }\n return result;\n}\n\nexport interface ComputeDiffOptions {\n /** Git toplevel working directory. */\n root: string;\n /** Argument to `git diff` (ref, sha, or \"HEAD\"). */\n diffArg: string;\n}\n\n/**\n * Build a tree-sha snapshot of the current working tree (including\n * untracked files, subject to .gitignore) without disturbing the real\n * git index — same temp-`GIT_INDEX_FILE` dance as `checkpoint.ts`. Used\n * by `computeRangeDiff` to do a true tree-vs-tree diff against a\n * checkpoint commit: `git diff <ref>` would otherwise produce phantom\n * \"deleted\" entries for files that were untracked-at-snapshot-time,\n * because git's working-tree diff goes through the real index and\n * untracked files appear \"absent\" from its perspective.\n */\nfunction workingTreeTreeSha(root: string): string | null {\n const tmpIndex = path.join(\n os.tmpdir(),\n `wd-diff-${process.pid}-${crypto.randomBytes(6).toString('hex')}.idx`,\n );\n const env: NodeJS.ProcessEnv = { ...process.env, GIT_INDEX_FILE: tmpIndex };\n const run = (args: string[]) =>\n spawn.sync('git', args, {\n cwd: root,\n encoding: 'utf-8',\n env,\n windowsHide: true,\n maxBuffer: 64 * 1024 * 1024,\n });\n try {\n const headExists =\n (\n spawn.sync('git', ['rev-parse', '--verify', 'HEAD'], {\n cwd: root,\n encoding: 'utf-8',\n windowsHide: true,\n }).stdout ?? ''\n ).trim().length > 0;\n if (headExists) {\n const r = run(['read-tree', 'HEAD']);\n if (r.status !== 0) return null;\n }\n const add = run(['add', '-A']);\n if (add.status !== 0) return null;\n const wt = run(['write-tree']);\n if (wt.status !== 0 || !wt.stdout) return null;\n return wt.stdout.trim();\n } finally {\n try {\n if (fs.existsSync(tmpIndex)) fs.unlinkSync(tmpIndex);\n } catch {\n /* */\n }\n }\n}\n\n/**\n * Detect binary content by scanning the first 8 KiB for null bytes. Matches\n * what most text-handling tools (git itself, less, vim) use as a heuristic.\n */\nfunction isBinaryContent(buffer: Buffer): boolean {\n const len = Math.min(buffer.length, 8192);\n for (let i = 0; i < len; i++) {\n if (buffer[i] === 0) return true;\n }\n return false;\n}\n\n/**\n * Render a unified-diff block for a single untracked file as if it were\n * being added. Avoids mutating the git index (which the previous\n * `add --intent-to-add` / `reset` approach did, with risk of leaving stale\n * index entries on crash).\n */\nfunction synthesizeUntrackedDiff(root: string, relPath: string): string {\n const absPath = path.join(root, relPath);\n let buffer: Buffer;\n try {\n buffer = fs.readFileSync(absPath);\n } catch {\n return '';\n }\n\n const header = `diff --git a/${relPath} b/${relPath}\\nnew file mode 100644\\n`;\n\n if (isBinaryContent(buffer)) {\n return `${header}Binary files /dev/null and b/${relPath} differ\\n`;\n }\n\n const content = buffer.toString('utf-8');\n if (content.length === 0) {\n // Empty file — no hunks, just the headers.\n return `${header}--- /dev/null\\n+++ b/${relPath}\\n`;\n }\n\n const hasTrailingNewline = content.endsWith('\\n');\n const lines = content.split('\\n');\n if (hasTrailingNewline) lines.pop();\n\n let out = `${header}--- /dev/null\\n+++ b/${relPath}\\n@@ -0,0 +1,${lines.length} @@\\n`;\n for (const line of lines) {\n out += `+${line}\\n`;\n }\n if (!hasTrailingNewline) {\n out += `\\\\\n`;\n }\n return out;\n}\n\n/**\n * Run `git diff <ref>` against the working tree, then synthesize unified-diff\n * blocks for any untracked files (so they show up as new). Read-only — no\n * changes to the git index, safe under concurrent invocations and crash.\n */\nexport function computeDiff(opts: ComputeDiffOptions): ParsedFile[] {\n const { root, diffArg } = opts;\n\n const trackedResult = spawn.sync(\n 'git',\n // -w (ignore-all-space) hides pure whitespace changes so a reformat\n // of indentation doesn't drown out the real changes.\n ['diff', '--no-color', '--no-ext-diff', '-w', diffArg],\n {\n cwd: root,\n encoding: 'utf-8',\n maxBuffer: 500 * 1024 * 1024,\n windowsHide: true,\n },\n );\n if (trackedResult.status !== 0) {\n if (trackedResult.stderr) console.error(trackedResult.stderr);\n return [];\n }\n\n let combined = trackedResult.stdout;\n\n // Append synthetic diffs for untracked files (respecting .gitignore).\n const untrackedResult = git(\n ['ls-files', '--others', '--exclude-standard'],\n root,\n );\n if (untrackedResult.exitCode === 0 && untrackedResult.stdout) {\n const files = untrackedResult.stdout.split('\\n').filter(Boolean);\n for (const file of files) {\n const block = synthesizeUntrackedDiff(root, file);\n if (block) combined += block;\n }\n }\n\n const files = parseGitDiff(combined);\n\n // Attach per-file line-coverage from an lcov.info if the repo has one.\n // Conservative: only files with a confident repo-relative path match get a\n // coverage value; everything else is left undefined (no badge rendered).\n // The lcov parse is memoized by (path, mtimeMs) inside `coverageLookup`, so\n // a multi-MB lcov is NOT re-read on every SSE / chokidar refresh tick.\n attachCoverage(root, files);\n\n // Augment markdown files with full before/after content so the SPA can\n // render a preview alongside the diff. Cheap (one `git show` + one fs\n // read per .md file), skipped entirely for non-markdown files.\n for (const file of files) {\n const md = readMarkdownContent(root, file, diffArg, 'working');\n if (md) file.mdContent = md;\n }\n\n return files;\n}\n\n/**\n * Attach line-coverage (+ lcov mtime + staleness) to each file in place.\n * Staleness: a file whose working-tree source `mtimeMs` is NEWER than the\n * lcov.info it was measured against has been edited since coverage was last\n * recorded, so its percent is no longer authoritative. We flag it\n * (`coverageStale`) rather than dropping the number outright — the SPA\n * de-emphasizes / suppresses the badge and the tooltip carries the lcov\n * timestamp so stale coverage is never presented as current.\n */\nfunction attachCoverage(root: string, files: ParsedFile[]): void {\n const { byPath, lcovMtimeMs } = coverageLookup(\n root,\n files.map((f) => f.path),\n );\n if (byPath.size === 0) return;\n for (const f of files) {\n const pct = byPath.get(f.path);\n if (typeof pct !== 'number') continue;\n f.coverage = pct;\n if (lcovMtimeMs != null) {\n f.coverageMtimeMs = lcovMtimeMs;\n let srcMtimeMs: number | null = null;\n try {\n srcMtimeMs = fs.statSync(path.join(root, f.path)).mtimeMs;\n } catch {\n srcMtimeMs = null;\n }\n if (srcMtimeMs != null && srcMtimeMs > lcovMtimeMs) {\n f.coverageStale = true;\n }\n }\n }\n}\n\nexport interface ComputeRangeDiffOptions {\n /** Git toplevel working directory. */\n root: string;\n /** Commit sha (or ref) for the left side. */\n fromRef: string;\n /** Commit sha (or ref) for the right side, or `'working'` for the\n * working tree (in which case behaviour matches `computeDiff` and\n * untracked files are synthesised). */\n toRef: string | 'working';\n}\n\n/**\n * Diff between two snapshot points. When `toRef === 'working'` this is\n * `computeDiff` with the same diffArg — the only path that needs untracked\n * synthesis. When `toRef` is a real commit, both sides are committed trees\n * (checkpoints already include what was untracked at capture time via the\n * temp-index `git add -A`), so a plain `git diff <from> <to>` covers it.\n *\n * Markdown content is fetched from the appropriate ref on each side so the\n * Preview/Split view works for historical range views too.\n */\nexport function computeRangeDiff(opts: ComputeRangeDiffOptions): ParsedFile[] {\n const { root, fromRef, toRef } = opts;\n\n if (toRef === 'working') {\n if (fromRef === 'HEAD') {\n // Legacy HEAD-vs-working path — keep the existing untracked\n // synthesis. This is what `wd` (no checkpoints) has always done.\n return computeDiff({ root, diffArg: fromRef });\n }\n // Range against a real checkpoint commit. `git diff <ref>` can't be\n // used directly here: it routes through the real index, so any file\n // that was untracked-at-snapshot-time (and captured into the\n // checkpoint's tree via the temp-index `add -A`) appears DELETED\n // from the working tree — a phantom that would render as\n // \"removed\" on every range view. Build a tree-sha snapshot of the\n // live working tree (same temp-index dance) and do a clean\n // tree-vs-tree diff. No untracked synth needed; the working-tree\n // tree already contains everything.\n const wtTreeSha = workingTreeTreeSha(root);\n if (!wtTreeSha) return [];\n const result = spawn.sync(\n 'git',\n ['diff-tree', '-r', '-p', '--no-color', '--no-ext-diff', fromRef, wtTreeSha],\n {\n cwd: root,\n encoding: 'utf-8',\n maxBuffer: 500 * 1024 * 1024,\n windowsHide: true,\n },\n );\n if (result.status !== 0) {\n if (result.stderr) console.error(result.stderr);\n return [];\n }\n const parsed = parseGitDiff(result.stdout);\n for (const file of parsed) {\n const md = readMarkdownContent(root, file, fromRef, 'working');\n if (md) file.mdContent = md;\n }\n return parsed;\n }\n\n // No `-w` here. `computeDiff` (HEAD-vs-working-tree) uses `-w` to hide\n // reformatting noise — but checkpoint-to-checkpoint diffs are user-\n // selected ranges where a whitespace-only change between snapshots is\n // a legitimate change the user explicitly asked to see. Suppressing it\n // would make the strip silently show \"no changes\" for a turn that did\n // exactly one reformatting pass.\n const result = spawn.sync(\n 'git',\n ['diff', '--no-color', '--no-ext-diff', fromRef, toRef],\n {\n cwd: root,\n encoding: 'utf-8',\n maxBuffer: 500 * 1024 * 1024,\n windowsHide: true,\n },\n );\n if (result.status !== 0) {\n if (result.stderr) console.error(result.stderr);\n return [];\n }\n\n const files = parseGitDiff(result.stdout);\n for (const file of files) {\n const md = readMarkdownContent(root, file, fromRef, toRef);\n if (md) file.mdContent = md;\n }\n return files;\n}\n","export type FileStatus = 'added' | 'deleted' | 'modified' | 'renamed' | 'binary';\n\nexport interface ParsedFile {\n /** Path used by GitHub-style anchors / sidebar tree. For renames, this is the new path. */\n path: string;\n oldPath: string;\n newPath: string;\n status: FileStatus;\n isBinary: boolean;\n added: number;\n deleted: number;\n hunks: Hunk[];\n /** Line-coverage percent for this file (from lcov). undefined when no lcov data is available. */\n coverage?: number;\n /** Epoch-ms mtime of the lcov.info the `coverage` value came from. Lets the\n * SPA show *when* the coverage was measured. undefined when no lcov data. */\n coverageMtimeMs?: number;\n /** True when this file's working-tree source is newer than the lcov.info the\n * coverage came from — i.e. the percent is stale and MUST NOT be presented\n * as authoritative. The SPA suppresses / de-emphasizes the badge. */\n coverageStale?: boolean;\n /** Full file contents for markdown rendering. Populated only for `.md` /\n * `.markdown` / `.mdx` files by the diff pipeline so the SPA can show a\n * rendered preview. `before` is from `git show <diffArg>:<oldPath>`,\n * `after` is the working-tree file. Either side may be absent for\n * added / deleted files. */\n mdContent?: MarkdownContent;\n}\n\nexport interface MarkdownContent {\n before?: string;\n after?: string;\n /** Set when either side exceeded the per-side size cap. The SPA uses\n * this to hide the Preview/Split toggle — rendering a 10 MB\n * markdown blob in the diff payload would balloon SSE reloads and\n * blow the browser heap. */\n tooLarge?: boolean;\n}\n\nexport interface Hunk {\n oldStart: number;\n oldLines: number;\n newStart: number;\n newLines: number;\n /** Trailing text after \"@@ ... @@\" — typically the enclosing function/class. */\n context: string;\n lines: HunkLine[];\n}\n\nexport type LineKind = 'context' | 'add' | 'delete' | 'no-newline';\n\nexport interface HunkLine {\n kind: LineKind;\n content: string;\n oldNum: number | null;\n newNum: number | null;\n}\n\nconst HUNK_RE = /^@@ -(\\d+)(?:,(\\d+))? \\+(\\d+)(?:,(\\d+))? @@(.*)$/;\n\n/**\n * Parse the output of `git diff` (or `git diff <ref>`) into a list of files\n * with hunks. The input is assumed to be in unified diff format with the\n * default git headers (`diff --git`, `--- a/...`, `+++ b/...`).\n */\nexport function parseGitDiff(input: string): ParsedFile[] {\n const lines = input.split(/\\r?\\n/);\n const files: ParsedFile[] = [];\n let current: ParsedFile | null = null;\n let currentHunk: Hunk | null = null;\n let oldLineNum = 0;\n let newLineNum = 0;\n\n function startFile(oldPath: string, newPath: string): ParsedFile {\n const f: ParsedFile = {\n path: newPath !== '/dev/null' ? newPath : oldPath,\n oldPath,\n newPath,\n status: 'modified',\n isBinary: false,\n added: 0,\n deleted: 0,\n hunks: [],\n };\n files.push(f);\n return f;\n }\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n\n // `diff --git a/path b/path` — start of a new file. We may not learn\n // the real paths until the `---`/`+++` lines below, so just close out\n // the previous file here.\n if (line.startsWith('diff --git ')) {\n current = null;\n currentHunk = null;\n const match = line.match(/^diff --git \"?a\\/(.+?)\"? \"?b\\/(.+?)\"?$/);\n if (match) {\n current = startFile(match[1], match[2]);\n }\n continue;\n }\n\n if (!current) continue;\n\n if (line.startsWith('new file mode')) {\n current.status = 'added';\n continue;\n }\n if (line.startsWith('deleted file mode')) {\n current.status = 'deleted';\n continue;\n }\n if (line.startsWith('rename from ')) {\n current.status = 'renamed';\n current.oldPath = line.slice('rename from '.length);\n continue;\n }\n if (line.startsWith('rename to ')) {\n current.newPath = line.slice('rename to '.length);\n current.path = current.newPath;\n continue;\n }\n if (line.startsWith('Binary files ')) {\n current.isBinary = true;\n continue;\n }\n if (line.startsWith('--- ')) {\n const p = stripPathPrefix(line.slice(4));\n if (p === '/dev/null') {\n current.status = 'added';\n } else {\n current.oldPath = p;\n }\n continue;\n }\n if (line.startsWith('+++ ')) {\n const p = stripPathPrefix(line.slice(4));\n if (p === '/dev/null') {\n current.status = 'deleted';\n } else {\n current.newPath = p;\n current.path = p;\n }\n continue;\n }\n\n const hunkMatch = line.match(HUNK_RE);\n if (hunkMatch) {\n currentHunk = {\n oldStart: Number(hunkMatch[1]),\n oldLines: hunkMatch[2] ? Number(hunkMatch[2]) : 1,\n newStart: Number(hunkMatch[3]),\n newLines: hunkMatch[4] ? Number(hunkMatch[4]) : 1,\n context: hunkMatch[5].trim(),\n lines: [],\n };\n oldLineNum = currentHunk.oldStart;\n newLineNum = currentHunk.newStart;\n current.hunks.push(currentHunk);\n continue;\n }\n\n if (!currentHunk) continue;\n\n // Inside a hunk: the first character of each line says what kind it is.\n // We accept empty lines defensively (some diffs trim trailing space).\n const marker = line[0] ?? ' ';\n const body = line.slice(1);\n\n if (marker === '\\\\') {\n // \"\\" — attach to previous line, don't count.\n currentHunk.lines.push({\n kind: 'no-newline',\n content: line,\n oldNum: null,\n newNum: null,\n });\n continue;\n }\n\n if (marker === '+') {\n currentHunk.lines.push({\n kind: 'add',\n content: body,\n oldNum: null,\n newNum: newLineNum++,\n });\n current.added++;\n } else if (marker === '-') {\n currentHunk.lines.push({\n kind: 'delete',\n content: body,\n oldNum: oldLineNum++,\n newNum: null,\n });\n current.deleted++;\n } else {\n // Context (space) and anything else gets treated as context.\n currentHunk.lines.push({\n kind: 'context',\n content: body,\n oldNum: oldLineNum++,\n newNum: newLineNum++,\n });\n }\n }\n\n // Derive status for renames where the diff omitted /dev/null markers.\n for (const f of files) {\n if (f.status === 'modified' && f.oldPath !== f.newPath) {\n f.status = 'renamed';\n }\n }\n\n return files;\n}\n\nfunction stripPathPrefix(p: string): string {\n // `--- a/path/to/file` / `+++ b/path/to/file` / `--- /dev/null`\n const trimmed = p.trim().replace(/^\"|\"$/g, '');\n if (trimmed === '/dev/null') return trimmed;\n if (trimmed.startsWith('a/') || trimmed.startsWith('b/')) {\n return trimmed.slice(2);\n }\n return trimmed;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\n/**\n * Parse standard lcov.info content into a map of file path → line-coverage\n * percent (a float in [0, 100]). Keys are the `SF:` paths exactly as written\n * in the file (may be absolute or repo-relative depending on the tool).\n *\n * Per-file percent prefers the `LF`/`LH` summary lines (lines found / lines\n * hit) when present; otherwise it is derived from the individual `DA:` line\n * records (`DA:<line>,<hits>`). A file with zero lines found yields 0.\n */\nexport function parseLcov(content: string): Map<string, number> {\n const result = new Map<string, number>();\n\n let sf: string | null = null;\n let lf: number | null = null;\n let lh: number | null = null;\n let daTotal = 0;\n let daHit = 0;\n\n const finish = () => {\n if (sf === null) return;\n let pct: number;\n if (lf !== null && lf > 0) {\n pct = ((lh ?? 0) / lf) * 100;\n } else if (lf === 0) {\n pct = 0;\n } else if (daTotal > 0) {\n pct = (daHit / daTotal) * 100;\n } else {\n pct = 0;\n }\n result.set(sf, pct);\n };\n\n const reset = () => {\n sf = null;\n lf = null;\n lh = null;\n daTotal = 0;\n daHit = 0;\n };\n\n for (const raw of content.split('\\n')) {\n const line = raw.trim();\n if (line.startsWith('SF:')) {\n sf = line.slice(3).trim();\n } else if (line.startsWith('DA:')) {\n const rest = line.slice(3);\n const comma = rest.indexOf(',');\n if (comma !== -1) {\n const hits = Number(rest.slice(comma + 1).split(',')[0]);\n if (Number.isFinite(hits)) {\n daTotal += 1;\n if (hits > 0) daHit += 1;\n }\n }\n } else if (line.startsWith('LF:')) {\n const n = Number(line.slice(3).trim());\n if (Number.isFinite(n)) lf = n;\n } else if (line.startsWith('LH:')) {\n const n = Number(line.slice(3).trim());\n if (Number.isFinite(n)) lh = n;\n } else if (line === 'end_of_record') {\n finish();\n reset();\n }\n }\n // Tolerate a trailing record with no terminating `end_of_record`.\n finish();\n\n return result;\n}\n\n/**\n * Locate an lcov.info file for a repo root. Checks `<root>/coverage/lcov.info`\n * then `<root>/lcov.info`. Returns the first that exists, else null.\n */\nexport function findLcov(root: string): string | null {\n const candidates = [\n path.join(root, 'coverage', 'lcov.info'),\n path.join(root, 'lcov.info'),\n ];\n for (const c of candidates) {\n try {\n if (fs.statSync(c).isFile()) return c;\n } catch {\n // not present — try next\n }\n }\n return null;\n}\n\n/**\n * Cache of parsed lcov files keyed by absolute lcov path. `computeDiff` runs\n * on every SSE / chokidar tick; an lcov.info can be multiple MB, so we MUST\n * NOT re-read + re-parse it each refresh. The cache is invalidated when the\n * file's `mtimeMs` changes (a fresh `npm test -- --coverage` run rewrites it).\n */\ninterface LcovCacheEntry {\n mtimeMs: number;\n parsed: Map<string, number>;\n}\nconst lcovCache = new Map<string, LcovCacheEntry>();\n\n/**\n * Read + parse the lcov at `lcovPath`, memoizing by `(path, mtimeMs)`. Returns\n * the parsed `SF: → percent` map and the lcov's mtime (ms-since-epoch), or\n * null when the file can't be stat'd / read. A multi-MB lcov is parsed once\n * and reused until it is rewritten.\n *\n * Exported for testing the cache behavior.\n */\nexport function readParsedLcov(\n lcovPath: string,\n): { parsed: Map<string, number>; mtimeMs: number } | null {\n let mtimeMs: number;\n try {\n mtimeMs = fs.statSync(lcovPath).mtimeMs;\n } catch {\n lcovCache.delete(lcovPath);\n return null;\n }\n\n const cached = lcovCache.get(lcovPath);\n if (cached && cached.mtimeMs === mtimeMs) {\n return { parsed: cached.parsed, mtimeMs };\n }\n\n let content: string;\n try {\n content = fs.readFileSync(lcovPath, 'utf-8');\n } catch {\n lcovCache.delete(lcovPath);\n return null;\n }\n\n const parsed = parseLcov(content);\n lcovCache.set(lcovPath, { mtimeMs, parsed });\n return { parsed, mtimeMs };\n}\n\n/** Test hook: drop the in-memory parse cache. */\nexport function clearLcovCache(): void {\n lcovCache.clear();\n}\n\n/** Normalize a path for comparison: forward slashes, drop a leading `./`. */\nfunction normRel(p: string): string {\n return path.normalize(p).replace(/\\\\/g, '/').replace(/^\\.\\//, '');\n}\n\n/**\n * Canonicalize `root` once so SF:-path matching survives symlink / realpath\n * divergence (macOS `/tmp` → `/private/tmp`, Linux bind mounts, Windows\n * junctions) and case-insensitive filesystems. An absolute `SF:` path emitted\n * by the coverage tool is resolved through `realpath` too, so both sides share\n * a canonical namespace before `path.relative` runs — otherwise the relative\n * path comes out `..`-prefixed and the match is silently lost.\n */\nfunction realRoot(root: string): string {\n try {\n return fs.realpathSync(path.resolve(root));\n } catch {\n return path.resolve(root);\n }\n}\n\n/**\n * Canonicalize an absolute path by realpath-ing its deepest EXISTING ancestor\n * and re-appending the not-yet-existing tail. `fs.realpathSync(absSf)` throws\n * when the leaf doesn\\'t exist on disk (lcov records files that may since have\n * moved, or simply aren\\'t present in a sparse checkout); a plain\n * `path.resolve` fallback would leave the path in a different symlink\n * namespace from `canonRoot` (macOS `/var/folders` TMPDIR, etc.) and the\n * relative path would come out `..`-prefixed — silently dropping the match.\n * Resolving the existing prefix keeps both sides in one canonical namespace.\n */\nfunction canonicalize(absPath: string): string {\n const resolved = path.resolve(absPath);\n let dir = resolved;\n const tail: string[] = [];\n // Walk up until we hit a path that exists (realpath-able) or the root.\n for (;;) {\n try {\n const realDir = fs.realpathSync(dir);\n return tail.length ? path.join(realDir, ...tail.reverse()) : realDir;\n } catch {\n const parent = path.dirname(dir);\n if (parent === dir) return resolved; // reached fs root, nothing realpath-able\n tail.push(path.basename(dir));\n dir = parent;\n }\n }\n}\n\n/** Resolve an absolute `SF:` path to repo-relative against the canonical root,\n * following symlinks (even when the leaf file is absent) so realpath\n * divergence and case-insensitive filesystems don\\'t break the match. */\nfunction relForAbsSf(absSf: string, canonRoot: string): string {\n return path.relative(canonRoot, canonicalize(absSf));\n}\n\n/**\n * Result of a per-repo coverage lookup. `byPath` maps each requested\n * repo-relative path that matched to its line-coverage percent. `lcovMtimeMs`\n * is the mtime of the lcov.info the data came from (so callers can surface\n * staleness), or null when no lcov was found.\n */\nexport interface CoverageLookup {\n byPath: Map<string, number>;\n lcovMtimeMs: number | null;\n}\n\n/**\n * Read and parse the repo's lcov (if any) and return a map of repo-relative\n * path → line-coverage percent for the requested files only, plus the lcov\n * mtime. lcov `SF:` paths may be absolute or relative; both sides are\n * normalized to a canonical repo-relative path and compared. Entries are only\n * included on a confident path match.\n *\n * The underlying lcov parse is memoized by `(path, mtimeMs)` (see\n * `readParsedLcov`) so a multi-MB file isn't re-read on every diff refresh.\n */\nexport function coverageLookup(\n root: string,\n relPaths: string[],\n): CoverageLookup {\n const out = new Map<string, number>();\n const lcovPath = findLcov(root);\n if (!lcovPath) return { byPath: out, lcovMtimeMs: null };\n\n const read = readParsedLcov(lcovPath);\n if (!read || read.parsed.size === 0) {\n return { byPath: out, lcovMtimeMs: read?.mtimeMs ?? null };\n }\n\n // Build a lookup keyed by normalized repo-relative SF path.\n const canonRoot = realRoot(root);\n const byRel = new Map<string, number>();\n for (const [sf, pct] of read.parsed) {\n const rel = path.isAbsolute(sf) ? relForAbsSf(sf, canonRoot) : sf;\n byRel.set(normRel(rel), pct);\n }\n\n for (const rp of relPaths) {\n const pct = byRel.get(normRel(rp));\n if (typeof pct === 'number') out.set(rp, pct);\n }\n\n return { byPath: out, lcovMtimeMs: read.mtimeMs };\n}\n\n/**\n * Back-compat thin wrapper: returns just the `repo-relative path → percent`\n * map (drops the lcov mtime). Prefer `coverageLookup` when you need staleness\n * information.\n */\nexport function coverageForFiles(\n root: string,\n relPaths: string[],\n): Map<string, number> {\n return coverageLookup(root, relPaths).byPath;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\nimport crypto from 'node:crypto';\n\nexport interface RepoSpec {\n /** Display name (becomes tab label / repo slug). */\n name: string;\n /** Git working tree root for this repo. */\n root: string;\n /** Argument to `git diff` (sha or ref like HEAD). */\n diffArg: string;\n}\n\n/** Stable per-scope path stem under ~/.work/diffs/. Pass one root for a\n * single repo, all roots (sorted) for a group. The hash gives each scope\n * its own stem; callers append `.pid`, `.url`, `.log` as needed. */\nexport function stableDiffPath(keyPaths: string[]): string {\n const key = keyPaths.slice().sort().join('|');\n const id = crypto.createHash('sha1').update(key).digest('hex').slice(0, 12);\n const dir = path.join(os.homedir(), '.work', 'diffs');\n fs.mkdirSync(dir, { recursive: true });\n return path.join(dir, id);\n}\n","import path from 'node:path';\nimport chalk from 'chalk';\nimport { git } from './git.js';\nimport { loadHistory, type WorktreeSession } from './history.js';\nimport type { RepoSpec } from './repo-spec.js';\n\nexport interface DiffScope {\n isGroup: boolean;\n session: WorktreeSession | null;\n /** Repos to diff (1 for single, N for group). */\n repos: { name: string; root: string }[];\n /** Name of the repo whose subtree the user is in (initial active tab). */\n activeRepoName: string | null;\n}\n\nexport interface ResolvedBase {\n base: string;\n source: 'arg' | 'session' | 'auto-detected' | 'default';\n}\n\nfunction normPath(p: string): string {\n return path.resolve(p).replace(/\\\\/g, '/').toLowerCase();\n}\n\n/**\n * Resolve what scope to diff based on cwd. Handles single-repo worktrees,\n * group worktrees (cwd at group root or anywhere inside a sub-repo), and\n * \"random\" git repos not managed by `work`.\n */\nexport function resolveScope(cwd: string): DiffScope | null {\n const normCwd = normPath(cwd);\n const sessions = loadHistory();\n\n // 1. cwd is at or inside one of a session's repo paths.\n for (const s of sessions) {\n for (const p of s.paths) {\n const np = normPath(p);\n if (normCwd === np || normCwd.startsWith(np + '/')) {\n if (s.isGroup) {\n return {\n isGroup: true,\n session: s,\n repos: s.paths.map((rp) => ({ name: path.basename(rp), root: rp })),\n activeRepoName: path.basename(p),\n };\n }\n return {\n isGroup: false,\n session: s,\n repos: [{ name: path.basename(p), root: p }],\n activeRepoName: path.basename(p),\n };\n }\n }\n }\n\n // 2. cwd is at the group root (parent of all of a group's repo paths).\n for (const s of sessions) {\n if (!s.isGroup || s.paths.length === 0) continue;\n const parents = s.paths.map((p) => normPath(path.dirname(p)));\n const groupRoot = parents[0];\n if (!parents.every((par) => par === groupRoot)) continue;\n if (normCwd === groupRoot || normCwd.startsWith(groupRoot + '/')) {\n return {\n isGroup: true,\n session: s,\n repos: s.paths.map((rp) => ({ name: path.basename(rp), root: rp })),\n activeRepoName: null,\n };\n }\n }\n\n // 3. Fall back to git rev-parse for repos not managed by `work`.\n const toplevel = git(['rev-parse', '--show-toplevel'], cwd);\n if (toplevel.exitCode !== 0 || !toplevel.stdout) return null;\n return {\n isGroup: false,\n session: null,\n repos: [{ name: path.basename(toplevel.stdout), root: toplevel.stdout }],\n activeRepoName: path.basename(toplevel.stdout),\n };\n}\n\n/**\n * Find any plausible parent branch — same candidate list as\n * `detectParentBranch`, but DOESN'T require the parent to have commits\n * past HEAD. Returns the candidate with the most-recent merge-base (or\n * null only if no candidates exist at all).\n *\n * Used by the static renderer and the diff server's `?base=branch`\n * route to decide whether to offer the \"Since branch\" tab. `detectParentBranch`\n * skips parents where merge-base == HEAD (correct for the CLI's \"show\n * me what I added past parent\" semantic — empty diff), but the toggle\n * should still appear in that case so the user can confirm \"no, there's\n * nothing committed yet\".\n */\nexport function findAnyParentBranch(cwd: string): string | null {\n return findParent(cwd, false);\n}\n\n/** Walk candidate base branches and pick the one with the most recent merge-base. */\nexport function detectParentBranch(cwd: string): string | null {\n return findParent(cwd, true);\n}\n\nfunction findParent(cwd: string, requireAheadOfParent: boolean): string | null {\n const currentResult = git(['rev-parse', '--abbrev-ref', 'HEAD'], cwd);\n const currentBranch = currentResult.exitCode === 0 ? currentResult.stdout : '';\n\n const candidates = ['main', 'master', 'dev', 'develop'].flatMap((name) => [\n name,\n `origin/${name}`,\n ]);\n\n let best: { ref: string; sha: string; time: number } | null = null;\n\n for (const ref of candidates) {\n if (ref === currentBranch) continue;\n const exists = git(['rev-parse', '--verify', '--quiet', ref], cwd);\n if (exists.exitCode !== 0 || !exists.stdout) continue;\n\n const mb = git(['merge-base', ref, 'HEAD'], cwd);\n if (mb.exitCode !== 0 || !mb.stdout) continue;\n\n // Skip parents where the merge-base IS HEAD only when the caller\n // asked us to (CLI \"show me what I added\" semantics). When called\n // for the toggle UI, we want to surface the parent even though the\n // diff will be empty.\n if (requireAheadOfParent) {\n const headSha = git(['rev-parse', 'HEAD'], cwd).stdout;\n if (mb.stdout === headSha) continue;\n }\n\n const timeResult = git(['show', '-s', '--format=%ct', mb.stdout], cwd);\n if (timeResult.exitCode !== 0) continue;\n const time = Number(timeResult.stdout);\n if (!Number.isFinite(time)) continue;\n\n if (!best || time > best.time) {\n best = { ref, sha: mb.stdout, time };\n }\n }\n\n return best?.ref ?? null;\n}\n\nexport function resolveBase(\n scope: DiffScope,\n argv: { base?: string; branch?: boolean },\n): ResolvedBase {\n if (argv.base) return { base: argv.base, source: 'arg' };\n\n if (argv.branch) {\n if (scope.session?.baseBranch) {\n return { base: scope.session.baseBranch, source: 'session' };\n }\n const primaryRoot =\n scope.repos.find((r) => r.name === scope.activeRepoName)?.root ??\n scope.repos[0].root;\n const detected = detectParentBranch(primaryRoot);\n if (detected) return { base: detected, source: 'auto-detected' };\n\n console.error(\n chalk.red('Could not determine a parent branch for this worktree.'),\n );\n console.error(chalk.gray('Pass one explicitly: diff <ref>'));\n process.exit(1);\n }\n\n return { base: 'HEAD', source: 'default' };\n}\n\n/** Compute per-repo merge-base when comparing against a non-HEAD ref. */\nexport function buildRepoSpecs(scope: DiffScope, base: string): RepoSpec[] {\n return scope.repos.map((r) => {\n let diffArg = base;\n if (base !== 'HEAD') {\n const mb = git(['merge-base', base, 'HEAD'], r.root);\n if (mb.exitCode === 0 && mb.stdout) diffArg = mb.stdout;\n }\n return { name: r.name, root: r.root, diffArg };\n });\n}\n\nexport interface ResolvedRepoDiff {\n /** The git ref the diff was computed against. `HEAD` for uncommitted,\n * the resolved parent (e.g. `origin/main`) for branch mode. */\n resolvedBase: string;\n /** The actual argument passed to `git diff` — typically the merge-base\n * sha for branch mode, or `HEAD` for uncommitted. */\n diffArg: string;\n}\n\n/**\n * Resolve the diff target for a single repo. Centralises the\n * \"find-parent → merge-base → fall back to HEAD\" dance that previously\n * lived in three places (scope-routes, web-server, the static renderer).\n *\n * For `base === 'uncommitted'` this is trivially `HEAD`. For\n * `base === 'branch'` it prefers an explicit `sessionBaseBranch` (the\n * value the user recorded with `work tree --base`), falling back to\n * `findAnyParentBranch` (so the toggle stays available even when the\n * branch has no commits past parent yet — diff just renders empty).\n */\nexport function resolveRepoDiff(\n root: string,\n base: 'uncommitted' | 'branch',\n sessionBaseBranch?: string | null,\n): ResolvedRepoDiff {\n if (base === 'uncommitted') {\n return { resolvedBase: 'HEAD', diffArg: 'HEAD' };\n }\n const parent = sessionBaseBranch ?? findAnyParentBranch(root);\n if (!parent) return { resolvedBase: 'HEAD', diffArg: 'HEAD' };\n let diffArg = 'HEAD';\n const mb = git(['merge-base', parent, 'HEAD'], root);\n if (mb.exitCode === 0 && mb.stdout) diffArg = mb.stdout;\n return { resolvedBase: parent, diffArg };\n}\n","import { Hono } from 'hono';\nimport { zValidator } from '@hono/zod-validator';\nimport { createCommentStore } from './comment-store.js';\nimport {\n startDiffServer,\n type DiffServerApi,\n type DiffServerHandle,\n} from './diff-server.js';\nimport type { RepoSpec } from './repo-spec.js';\nimport type { Comment } from './comment-types.js';\nimport { commentInputSchema, submitReviewSchema } from './comment-schemas.js';\n\nexport type {\n Comment,\n CommentAuthor,\n CommentSide,\n CommentStatus,\n} from './comment-types.js';\n\nexport interface CommentServerOptions {\n repos: RepoSpec[];\n scopeLabel: string;\n /** Session baseBranch — threaded to the diff server so `?base=branch`\n * honours the user's recorded base instead of auto-detecting. */\n sessionBaseBranch?: string;\n onComment?: (comment: Comment) => void;\n onCommentDeleted?: (id: string) => void;\n onSubmitReviewStart?: (info: { count: number; summary: Comment | null }) => void;\n onSubmitReviewEnd?: () => void;\n watchDebounceMs?: number;\n}\n\nexport interface CommentServerHandle {\n url: string;\n waitForDone(): Promise<Comment[]>;\n snapshot(): Comment[];\n stop(): Promise<void>;\n}\n\nexport async function startCommentServer(\n opts: CommentServerOptions,\n): Promise<CommentServerHandle> {\n const store = createCommentStore();\n let resolveDone: ((comments: Comment[]) => void) | null = null;\n const donePromise = new Promise<Comment[]>((resolve) => {\n resolveDone = resolve;\n });\n\n function attachRoutes(api: DiffServerApi): void {\n const routes = new Hono();\n\n routes.get('/api/comments', (c) => c.json({ comments: store.snapshot() }));\n\n routes.post(\n '/api/comments',\n zValidator('json', commentInputSchema),\n (c) => {\n try {\n const input = c.req.valid('json');\n const comment = store.post(input);\n if (\n comment.status === 'published' &&\n comment.author === 'user' &&\n opts.onComment\n ) {\n opts.onComment(comment);\n }\n if (comment.author === 'claude') {\n api.broadcast('comments-changed', { id: comment.id });\n }\n return c.json({ comment, comments: store.snapshot() });\n } catch (err) {\n return c.json({ error: (err as Error).message }, 400);\n }\n },\n );\n\n routes.delete('/api/comments/:id', (c) => {\n const id = c.req.param('id');\n const removed = store.remove(id);\n if (removed && opts.onCommentDeleted) opts.onCommentDeleted(id);\n return c.json({ comments: store.snapshot() });\n });\n\n routes.post(\n '/api/submit-review',\n zValidator('json', submitReviewSchema),\n (c) => {\n const body = c.req.valid('json');\n const result = store.submit(body.summary);\n if (opts.onSubmitReviewStart) {\n opts.onSubmitReviewStart({\n count: result.drafts.length,\n summary: result.summary,\n });\n }\n if (result.summary && opts.onComment) opts.onComment(result.summary);\n if (opts.onComment) {\n for (const d of result.drafts) opts.onComment(d);\n }\n if (opts.onSubmitReviewEnd) opts.onSubmitReviewEnd();\n return c.json({\n count: result.drafts.length,\n comments: store.snapshot(),\n });\n },\n );\n\n routes.post('/api/discard-review', (c) => {\n const discarded = store.discardDrafts();\n return c.json({ discarded, comments: store.snapshot() });\n });\n\n routes.post('/api/done', (c) => {\n const out = c.json({ ok: true, count: store.list().length });\n if (resolveDone) {\n resolveDone(store.snapshot());\n resolveDone = null;\n }\n return out;\n });\n\n api.route('/', routes);\n }\n\n const server: DiffServerHandle = await startDiffServer({\n repos: opts.repos,\n scopeLabel: opts.scopeLabel,\n sessionBaseBranch: opts.sessionBaseBranch,\n watchDebounceMs: opts.watchDebounceMs,\n attachRoutes,\n });\n\n return {\n url: server.url,\n waitForDone: () => donePromise,\n snapshot: () => store.snapshot(),\n stop: () => server.stop(),\n };\n}\n\n/** Read-only variant: diff + watch + SSE only. Used by `wd` / `wd --watch`. */\nexport async function startReadOnlyDiffServer(opts: {\n repos: RepoSpec[];\n scopeLabel: string;\n sessionBaseBranch?: string;\n watchDebounceMs?: number;\n}): Promise<{ url: string; stop: () => Promise<void> }> {\n const server = await startDiffServer({\n repos: opts.repos,\n scopeLabel: opts.scopeLabel,\n sessionBaseBranch: opts.sessionBaseBranch,\n watchDebounceMs: opts.watchDebounceMs,\n readOnly: true,\n });\n return { url: server.url, stop: () => server.stop() };\n}\n\n/** Format a single comment as a markdown chunk for stdout. */\nexport function formatSingleComment(c: Comment): string {\n const bodyLines = c.body.split('\\n').map((l) => `> ${l}`).join('\\n');\n const header =\n c.side === 'general'\n ? `**General review comment**`\n : `**${c.repo}/${c.file}** : line ${c.line} (${c.side})`;\n const meta: string[] = [];\n if (c.author === 'claude') meta.push('author: claude');\n if (c.parentId) meta.push(`reply-to: ${c.parentId}`);\n meta.push(`id: ${c.id}`);\n return [`--- comment ---`, header, meta.join(' · '), bodyLines, ``].join('\\n');\n}\n","import crypto from 'node:crypto';\nimport type {\n Comment,\n CommentAuthor,\n CommentSide,\n CommentStatus,\n} from './comment-types.js';\n\nexport interface CommentInput {\n repo?: string;\n file?: string;\n line?: number;\n side?: CommentSide;\n body: string;\n status?: CommentStatus;\n lineContent?: string;\n parentId?: string;\n author?: CommentAuthor;\n}\n\nexport interface SubmitInfo {\n /** Number of drafts promoted to published, in chronological order. */\n drafts: Comment[];\n /** Optional summary comment created from `summary` text. */\n summary: Comment | null;\n}\n\n/**\n * In-memory comment store. Pure model — no I/O, no HTTP, no SSE. The server\n * wires hooks (`onPost`, `onSubmit`, etc.) so its routes can stream events\n * to clients and stdout without the store knowing how delivery happens.\n */\nexport interface CommentStore {\n list(): Comment[];\n /** Add a comment. Throws if parentId is given but not found. Returns the\n * newly-created comment. Does NOT fire onPost — the caller decides\n * whether to stream this immediately (published+user) or hold it\n * (drafts, claude echoes). */\n post(input: CommentInput): Comment;\n /** Remove a comment by id. Returns true if anything was removed. */\n remove(id: string): boolean;\n /** Promote all drafts to published, optionally creating a summary comment\n * from `summary` text. Returns the batch info. */\n submit(summary: string | undefined): SubmitInfo;\n /** Drop every draft comment. Returns the number removed. */\n discardDrafts(): number;\n /** Snapshot — a defensive copy. */\n snapshot(): Comment[];\n}\n\nexport function createCommentStore(): CommentStore {\n const comments: Comment[] = [];\n\n function findParent(parentId: string | undefined): Comment | undefined {\n if (typeof parentId !== 'string') return undefined;\n const parent = comments.find((c) => c.id === parentId);\n if (!parent) throw new Error('parent comment not found');\n return parent;\n }\n\n return {\n list: () => comments,\n snapshot: () => [...comments],\n\n post(input) {\n if (typeof input.body !== 'string' || !input.body.trim()) {\n throw new Error('comment body is required');\n }\n const parent = findParent(input.parentId);\n const side = input.side ?? parent?.side ?? 'general';\n if (side !== 'left' && side !== 'right' && side !== 'general') {\n throw new Error('invalid side');\n }\n const c: Comment = {\n id: crypto.randomBytes(8).toString('hex'),\n repo:\n (typeof input.repo === 'string' ? input.repo : parent?.repo) ?? '',\n file:\n (typeof input.file === 'string' ? input.file : parent?.file) ?? '',\n line: typeof input.line === 'number' ? input.line : (parent?.line ?? 0),\n side,\n body: input.body.trim(),\n createdAt: new Date().toISOString(),\n lineContent:\n typeof input.lineContent === 'string'\n ? input.lineContent\n : parent?.lineContent,\n author: input.author === 'claude' ? 'claude' : 'user',\n parentId: parent?.id,\n status: input.status === 'draft' ? 'draft' : 'published',\n };\n comments.push(c);\n return c;\n },\n\n remove(id) {\n const idx = comments.findIndex((c) => c.id === id);\n if (idx < 0) return false;\n comments.splice(idx, 1);\n return true;\n },\n\n submit(summary) {\n const drafts = comments\n .filter((c) => c.status === 'draft')\n .sort((a, b) => a.createdAt.localeCompare(b.createdAt));\n let summaryComment: Comment | null = null;\n if (typeof summary === 'string' && summary.trim()) {\n summaryComment = {\n id: crypto.randomBytes(8).toString('hex'),\n repo: '',\n file: '',\n line: 0,\n side: 'general',\n body: summary.trim(),\n createdAt: new Date().toISOString(),\n author: 'user',\n status: 'published',\n };\n comments.push(summaryComment);\n }\n for (const d of drafts) {\n d.status = 'published';\n }\n return { drafts, summary: summaryComment };\n },\n\n discardDrafts() {\n const before = comments.length;\n for (let i = comments.length - 1; i >= 0; i--) {\n if (comments[i].status === 'draft') comments.splice(i, 1);\n }\n return before - comments.length;\n },\n };\n}\n","import chalk from 'chalk';\nimport { Hono, type Context } from 'hono';\nimport { serve, type ServerType } from '@hono/node-server';\nimport { streamSSE } from 'hono/streaming';\nimport { computeDiff } from './diff-pipeline.js';\nimport { createFsWatcher } from './fs-watcher.js';\nimport { resolveRepoDiff } from './diff-scope.js';\nimport { resolveWebRoot } from './web-static.js';\nimport { serveSpa } from './spa-handler.js';\nimport type { RepoSpec } from './repo-spec.js';\n\nexport interface DiffServerOptions {\n /** Repos this server scopes to. */\n repos: RepoSpec[];\n /** Short human-readable label for /api/context. */\n scopeLabel: string;\n /** Optional session baseBranch (the value captured at `work tree --base`).\n * When set, the `?base=branch` route uses it instead of auto-detection,\n * so the diff matches what the user actually declared. */\n sessionBaseBranch?: string;\n /** Debounce for fs.watch events before broadcasting diff-changed. */\n watchDebounceMs?: number;\n /** When true, /api/context advertises readOnly=true and the SPA hides\n * the comment UI. Default false. */\n readOnly?: boolean;\n /** Called once the base app is built so feature apps (e.g. comments) can\n * mount themselves and emit events via the SSE hub. */\n attachRoutes?: (api: DiffServerApi) => void;\n}\n\nexport interface DiffServerHandle {\n url: string;\n port: number;\n stop(): Promise<void>;\n}\n\n/** A typed SSE event broadcast to every connected client. */\nexport interface SseEvent {\n event: string;\n data: unknown;\n}\n\nexport interface DiffServerApi {\n /** Mount a Hono sub-app at the given path prefix. */\n route(prefix: string, app: Hono): void;\n /** Broadcast on the shared SSE hub. Fan-out is one-shot per client. */\n broadcast(event: string, data: unknown): void;\n}\n\n/**\n * The shared local HTTP server used by both `wd` (read-only) and `wd -c`\n * (comments). Owns the fs watcher, the SSE hub, and the three routes every\n * mode needs: GET /api/context, GET /api/diff, GET /events. Comment routes\n * register themselves via `attachRoutes`.\n */\nexport async function startDiffServer(\n opts: DiffServerOptions,\n): Promise<DiffServerHandle> {\n const webRoot = resolveWebRoot();\n if (!webRoot) {\n throw new Error('Could not find dist/web/. Run `npm run build` first.');\n }\n\n const sseListeners = new Set<(e: SseEvent) => void>();\n const watcher = createFsWatcher({\n roots: opts.repos.map((r) => r.root),\n debounceMs: opts.watchDebounceMs,\n onChange: () => broadcast('diff-changed', { ts: Date.now() }),\n });\n\n function broadcast(event: string, data: unknown): void {\n const payload: SseEvent = { event, data };\n for (const cb of sseListeners) cb(payload);\n }\n\n const app = new Hono();\n // No CORS headers and no OPTIONS preflight handler — the SPA is served\n // same-origin so its requests don't trigger preflight. The Host guard in\n // `launch` rejects cross-origin requests before they reach any handler.\n\n app.get('/api/context', (c) =>\n c.json({\n mode: 'review',\n scopeLabel: opts.scopeLabel,\n repos: opts.repos.map((r) => ({ name: r.name })),\n readOnly: !!opts.readOnly,\n }),\n );\n\n app.get('/api/diff', (c) => {\n const base = c.req.query('base') === 'branch' ? 'branch' : 'uncommitted';\n try {\n // For 'uncommitted' we honour the configured `diffArg` (typically\n // HEAD) so single-repo callers preserve whatever ref they\n // configured. For 'branch' we delegate to `resolveRepoDiff` per\n // repo — each one resolves its own parent and merge-base. The\n // top-level `resolvedBase` is the primary repo's value (matches\n // scope-routes and web-server behavior for the badge label).\n const resolved = opts.repos.map((r) =>\n base === 'uncommitted'\n ? { resolvedBase: 'HEAD', diffArg: r.diffArg }\n : resolveRepoDiff(r.root, 'branch', opts.sessionBaseBranch),\n );\n const repos = opts.repos.map((r, i) => ({\n name: r.name,\n root: r.root,\n resolvedBase: resolved[i].resolvedBase,\n files: computeDiff({ root: r.root, diffArg: resolved[i].diffArg }),\n }));\n const resolvedBase = resolved[0]?.resolvedBase ?? 'HEAD';\n return c.json({ repos, base, resolvedBase });\n } catch (err) {\n return c.json({ error: (err as Error).message }, 500);\n }\n });\n\n app.get('/events', (c) =>\n streamSSE(c, async (stream) => {\n const listener = (e: SseEvent) => {\n stream\n .writeSSE({ event: e.event, data: JSON.stringify(e.data) })\n .catch(() => { /* client gone */ });\n };\n sseListeners.add(listener);\n await stream.writeSSE({ event: 'connected', data: '' });\n // Keep the stream open until the client disconnects.\n await new Promise<void>((resolve) => {\n stream.onAbort(() => {\n sseListeners.delete(listener);\n resolve();\n });\n });\n }),\n );\n\n // attachRoutes can mount Hono sub-apps with their own routes.\n if (opts.attachRoutes) {\n opts.attachRoutes({\n route: (prefix, sub) => app.route(prefix, sub),\n broadcast,\n });\n }\n\n // SPA fallback for the bundled web assets.\n app.get('*', (c: Context) => serveSpa(c, webRoot));\n\n // Wrap the handle so server shutdown also tears down our chokidar watcher.\n // Without this the watcher keeps Node's event loop open after `stop()` —\n // a real test/teardown leak (vitest hangs after a suite that exercises\n // the server) and a wasteful long-running ghost in dev.\n const handle = await launch(app);\n const baseStop = handle.stop;\n return {\n ...handle,\n stop: async () => {\n try { watcher.stop(); } catch { /* */ }\n await baseStop();\n },\n };\n}\n\n/** Start a Hono app on a random port; resolve when it's listening. The\n * raw Node server is exposed via `httpServer` so callers can attach\n * WebSocket upgrade handlers (the terminal bridge).\n *\n * Installs a Host-header guard so DNS-rebinding attacks can't trick a\n * browser into POSTing to our local server from an attacker-controlled\n * origin: only `127.0.0.1:<port>` and `localhost:<port>` are accepted.\n * The SPA is served same-origin so legitimate requests always carry one\n * of those Host headers.\n */\nexport function launch(app: Hono): Promise<DiffServerHandle & { httpServer: ServerType }> {\n return new Promise((resolve) => {\n // Captured before serve() resolves; first guarded request runs after\n // this is set because serve() doesn't accept connections until it's\n // bound, and bind precedes the info-callback.\n let listenPort = 0;\n const guard = new Hono();\n guard.use('*', async (c, next) => {\n const host = c.req.header('host');\n if (\n host !== `127.0.0.1:${listenPort}` &&\n host !== `localhost:${listenPort}`\n ) {\n return c.text('Forbidden', 403);\n }\n await next();\n });\n guard.route('/', app);\n\n const server: ServerType = serve(\n { fetch: guard.fetch, port: 0, hostname: '127.0.0.1' },\n (info) => {\n listenPort = info.port;\n const url = `http://127.0.0.1:${info.port}/`;\n process.stderr.write(chalk.gray(`[server] listening at ${url}\\n`));\n resolve({\n url,\n port: info.port,\n httpServer: server,\n stop: () =>\n new Promise<void>((res) => {\n server.close(() => res());\n }),\n });\n },\n );\n });\n}\n","import path from 'node:path';\nimport chalk from 'chalk';\nimport chokidar from 'chokidar';\n\nexport interface FsWatcherOptions {\n /** Working-tree roots to watch. .git/ subdirs are filtered out. */\n roots: string[];\n /** Debounce window for fs events before firing `onChange`. */\n debounceMs?: number;\n /** Fires once per debounce window when anything under `roots` changed. */\n onChange: () => void;\n}\n\nexport interface FsWatcher {\n stop(): void;\n}\n\n/**\n * Debounced chokidar watcher over one-or-more repo roots. Filters out\n * `.git/` to avoid the constant noise git generates internally. Shared\n * between `wd` (the diff server's reload trigger) and any other live\n * file-watcher need.\n */\nexport function createFsWatcher(opts: FsWatcherOptions): FsWatcher {\n const debounceMs = opts.debounceMs ?? 150;\n let debounceTimer: NodeJS.Timeout | null = null;\n\n const watcher = chokidar.watch(opts.roots, {\n ignored: (filePath) => {\n for (const root of opts.roots) {\n const rel = path.relative(root, filePath).replace(/\\\\/g, '/');\n if (rel === '.git' || rel.startsWith('.git/')) return true;\n }\n return false;\n },\n ignoreInitial: true,\n persistent: true,\n awaitWriteFinish: { stabilityThreshold: 50, pollInterval: 20 },\n });\n\n watcher.on('all', () => {\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n debounceTimer = null;\n opts.onChange();\n }, debounceMs);\n });\n watcher.on('error', (err) => {\n process.stderr.write(\n chalk.yellow('[watcher] fs error: ') + (err as Error).message + '\\n',\n );\n });\n\n return {\n stop() {\n if (debounceTimer) clearTimeout(debounceTimer);\n watcher.close().catch(() => { /* */ });\n },\n };\n}\n","import fs from 'node:fs';\nimport http from 'node:http';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst MIME: Record<string, string> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'application/javascript; charset=utf-8',\n '.mjs': 'application/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.ico': 'image/x-icon',\n '.map': 'application/json; charset=utf-8',\n};\n\n/** Resolve the directory shipped by the Vite build. */\nexport function resolveWebRoot(): string | null {\n const entryDir = path.dirname(process.argv[1] ?? '');\n const moduleDir = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.join(entryDir, 'web'),\n // bundled: this module is inlined into dist/<bin>.js, so dist/web is a\n // sibling of the bundle. Works even when argv[1] is an npm bin symlink\n // (which is not realpath'd, so the entryDir candidate above misses).\n path.join(moduleDir, 'web'),\n // dev/tsx fallback: walk up from src/core to repo root then into dist/web.\n path.resolve(moduleDir, '../../dist/web'),\n ];\n for (const c of candidates) {\n if (fs.existsSync(path.join(c, 'index.html'))) return c;\n }\n return null;\n}\n\n/**\n * Serve a static file under `root`. Returns true if it served a file. Does\n * NOT do SPA fallback — caller decides whether to re-try with /index.html\n * on miss.\n */\nexport function serveStatic(\n root: string,\n urlPath: string,\n res: http.ServerResponse,\n): boolean {\n const clean = urlPath.split('?')[0];\n const requested = clean === '/' ? '/index.html' : clean;\n const filePath = path.join(root, requested);\n const norm = path.normalize(filePath);\n const base = path.normalize(root);\n if (norm !== base && !norm.startsWith(base + path.sep)) return false;\n let stat: fs.Stats;\n try {\n stat = fs.statSync(norm);\n } catch {\n return false;\n }\n if (!stat.isFile()) return false;\n const ext = path.extname(norm).toLowerCase();\n res.writeHead(200, {\n 'Content-Type': MIME[ext] ?? 'application/octet-stream',\n 'Cache-Control': 'no-cache',\n });\n fs.createReadStream(norm).pipe(res);\n return true;\n}\n\n/** Convenience: serve the file if it exists, else fall back to index.html. */\nexport function serveStaticOrShell(\n root: string,\n urlPath: string,\n res: http.ServerResponse,\n): boolean {\n if (serveStatic(root, urlPath, res)) return true;\n if (serveStatic(root, '/index.html', res)) return true;\n return false;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { Context } from 'hono';\n\nconst MIME: Record<string, string> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'application/javascript; charset=utf-8',\n '.mjs': 'application/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.ico': 'image/x-icon',\n '.map': 'application/json; charset=utf-8',\n};\n\nfunction readFile(root: string, relPath: string): { body: Buffer; ext: string } | null {\n const clean = relPath.split('?')[0];\n const requested = clean === '/' ? '/index.html' : clean;\n const filePath = path.join(root, requested);\n const norm = path.normalize(filePath);\n if (!norm.startsWith(path.normalize(root))) return null;\n let stat: fs.Stats;\n try {\n stat = fs.statSync(norm);\n } catch {\n return null;\n }\n if (!stat.isFile()) return null;\n const ext = path.extname(norm).toLowerCase();\n return { body: fs.readFileSync(norm), ext };\n}\n\n/**\n * Hono handler for the bundled SPA. Tries to serve the requested file; if it\n * doesn't exist, falls back to index.html so client-side routing works on\n * deep links. Skips /api/* paths entirely (those should be handled by\n * earlier routes; if we reach here it's a 404).\n */\nexport function serveSpa(c: Context, webRoot: string): Response {\n const url = new URL(c.req.url);\n if (url.pathname.startsWith('/api/')) {\n return c.json({ error: 'not found' }, 404);\n }\n const hit = readFile(webRoot, url.pathname) ?? readFile(webRoot, '/index.html');\n if (!hit) return c.json({ error: 'not found' }, 404);\n return new Response(new Uint8Array(hit.body), {\n status: 200,\n headers: {\n 'Content-Type': MIME[hit.ext] ?? 'application/octet-stream',\n 'Cache-Control': 'no-cache',\n },\n });\n}\n","/**\n * Shared zod schemas for the comment HTTP surface. Kept separate from\n * `comment-types.ts` because the SPA imports `comment-types.ts` for the\n * type-only exports and pulling zod into the SPA bundle is wasteful —\n * the schemas are server-side validation only.\n */\n\nimport { z } from 'zod';\n\nexport const commentInputSchema = z.object({\n repo: z.string().optional(),\n file: z.string().optional(),\n line: z.number().int().optional(),\n side: z.enum(['left', 'right', 'general']).optional(),\n body: z.string().min(1),\n status: z.enum(['published', 'draft']).optional(),\n lineContent: z.string().optional(),\n parentId: z.string().optional(),\n author: z.enum(['user', 'claude']).optional(),\n});\n\nexport const submitReviewSchema = z.object({\n summary: z.string().optional(),\n});\n","/**\n * Pure helper for the `wd -c` polling loop in `commands/diff.ts`.\n *\n * Given the previous-tick set of \"seen\" comment ids and the current\n * snapshot, return:\n * - new published comments to emit (in snapshot order)\n * - deleted ids to emit\n * - the next `seen` set to track going into the following tick\n *\n * The function MUTATES `seen` in place by adding new ids and removing\n * deleted ones. Callers can read `seen.size` after this returns to get\n * the running total — that's what's reported as `--- review done --- total`.\n *\n * Extracted from inline logic in `tryReviewViaWorkWeb` so it can be\n * tested without standing up a fake HTTP server or mocking process.exit.\n * The C-1 review finding (deleted ids never pruned, inflating the\n * reported total) lived here.\n */\n\nimport type { Comment } from './comment-types.js';\n\nexport interface SnapshotDiff {\n /** Published comments not already in `seen`, in snapshot order. */\n newComments: Comment[];\n /** Ids present in `seen` but missing from the current snapshot. */\n deleted: string[];\n}\n\nexport function diffReviewSnapshot(\n snapshot: Comment[],\n seen: Set<string>,\n): SnapshotDiff {\n const now = new Set(snapshot.map((c) => c.id));\n\n const deleted: string[] = [];\n for (const id of seen) {\n if (!now.has(id)) deleted.push(id);\n }\n for (const id of deleted) seen.delete(id);\n\n const newComments: Comment[] = [];\n for (const c of snapshot) {\n if (seen.has(c.id)) continue;\n if (c.status === 'draft') continue;\n newComments.push(c);\n }\n for (const c of snapshot) {\n if (c.status === 'published') seen.add(c.id);\n }\n\n return { newComments, deleted };\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { computeDiff } from './diff-pipeline.js';\nimport { resolveWebRoot } from './web-static.js';\nimport type { RepoSpec } from './repo-spec.js';\n\nexport type DiffBase = 'uncommitted' | 'branch';\n\nexport interface DiffPayload {\n repos: {\n name: string;\n root: string;\n files: ReturnType<typeof computeDiff>;\n }[];\n /** The ref the diff was computed against (HEAD for uncommitted, e.g.\n * `origin/main` for branch). */\n resolvedBase: string;\n}\n\nexport interface StaticBootData {\n context: {\n mode: 'review';\n scopeLabel: string;\n repos: { name: string }[];\n readOnly: true;\n staticMode: true;\n /** Which tab the SPA opens on. The user can toggle to the other one\n * in-browser; both diffs are inlined. */\n initialBase: DiffBase;\n };\n /** Both scopes, computed ahead of time. The SPA picks one based on\n * the toggle and re-uses the other when the user clicks across. */\n diffs: {\n uncommitted: DiffPayload;\n branch?: DiffPayload;\n };\n /** Legacy single-scope payload for backward compatibility with any\n * earlier static HTML. Mirrors diffs[initialBase] so an older client\n * that only looks at `diff` still renders. */\n diff: DiffPayload;\n}\n\nexport interface RenderStaticOptions {\n scopeLabel: string;\n /** Always computed (default tab). */\n uncommitted: RepoSpec[];\n /** Optional branch-scope specs. When provided, the SPA gains a\n * \"Since branch\" tab. The resolvedBase shows up next to the file\n * count and in the empty state. */\n branch?: { specs: RepoSpec[]; resolvedBase: string };\n /** Which tab opens by default. Defaults to 'uncommitted' or, when\n * branch specs are provided AND uncommitted has nothing to show,\n * the caller can set this to 'branch'. */\n initialBase?: DiffBase;\n}\n\nfunction buildDiff(specs: RepoSpec[], resolvedBase: string): DiffPayload {\n return {\n repos: specs.map((r) => ({\n name: r.name,\n root: r.root,\n files: computeDiff({ root: r.root, diffArg: r.diffArg }),\n })),\n resolvedBase,\n };\n}\n\n/**\n * Build a self-contained HTML file from the React SPA bundle. Reads the\n * built `dist/web/index.html`, inlines the JS/CSS as <script>/<style>, and\n * injects the boot data so the SPA can render without a server.\n *\n * Computes both diff scopes (Uncommitted vs Since branch) when branch\n * specs are provided so the toggle works entirely client-side — no\n * fetch needed, file:// works.\n */\nexport function renderStatic(opts: RenderStaticOptions): string {\n const webRoot = resolveWebRoot();\n if (!webRoot) {\n throw new Error('Could not find dist/web/. Run `npm run build` first.');\n }\n const shellPath = path.join(webRoot, 'index.html');\n let shell = fs.readFileSync(shellPath, 'utf-8');\n\n const uncommitted = buildDiff(opts.uncommitted, 'HEAD');\n const branch = opts.branch\n ? buildDiff(opts.branch.specs, opts.branch.resolvedBase)\n : undefined;\n const initialBase: DiffBase = opts.initialBase ?? 'uncommitted';\n const initial = initialBase === 'branch' && branch ? branch : uncommitted;\n\n const boot: StaticBootData = {\n context: {\n mode: 'review',\n scopeLabel: opts.scopeLabel,\n repos: opts.uncommitted.map((r) => ({ name: r.name })),\n readOnly: true,\n staticMode: true,\n initialBase: branch ? initialBase : 'uncommitted',\n },\n diffs: { uncommitted, branch },\n diff: initial,\n };\n\n // ORDER MATTERS: inject the boot script BEFORE inlining the bundle. The\n // SPA bundle contains literal `</head>` substrings (HTML template helpers\n // inside react-dom / marked / etc.), so replacing `</head>` *after*\n // inlining matches the wrong occurrence and splices JSON into JavaScript.\n // See tests/core/static-renderer.test.ts for the regression test.\n const bootJson = escapeForScriptTag(JSON.stringify(boot));\n const bootScript = `<script>window.__WD_BOOT__=${bootJson};</script>`;\n // String.prototype.replace interprets `$&`, `$'`, `$\\``, `$1`, `$$` in\n // the replacement string. The boot JSON can absolutely contain `$'` —\n // e.g. a shell regex like `'^(None)?$'` — which would silently splice\n // the substring AFTER `</head>` (the SPA's body) into the middle of\n // our JSON. Use a function callback so the replacement is taken\n // verbatim, no $-substitution. (This bit us in real CI logs.)\n shell = shell.replace(/<\\/head>/i, () => `${bootScript}</head>`);\n\n // Inline external CSS/JS so the file works over file:// with no server.\n // Same `</tag` escape rule as the JS inliner — a literal `</style` in\n // the CSS bundle (CSS-in-JS comment strings, minifier quirks) would\n // close the wrapping <style> tag early and dump the rest into HTML.\n shell = shell.replace(\n /<link rel=\"stylesheet\"[^>]*href=\"([^\"]+)\"[^>]*>/g,\n (_match, href) => {\n const css = readAsset(webRoot, href);\n if (!css) return '';\n const escaped = css.replace(/<\\/style/gi, '<\\\\/style');\n return `<style>${escaped}</style>`;\n },\n );\n shell = shell.replace(\n /<script\\b[^>]*\\bsrc=\"([^\"]+)\"[^>]*><\\/script>/g,\n (_match, src) => {\n const js = readAsset(webRoot, src);\n if (!js) return '';\n // Any literal `</script>` inside the bundle would terminate the outer\n // <script> tag early. Browsers match `</script` case-insensitively\n // with optional whitespace, so escape that form.\n const escaped = js.replace(/<\\/script/gi, '<\\\\/script');\n return `<script type=\"module\">${escaped}</script>`;\n },\n );\n\n return shell;\n}\n\n/**\n * Make a JSON payload safe to embed as a literal value inside a `<script>`\n * tag in HTML. JSON itself is fine, but several characters break when it's\n * inlined as JS source:\n *\n * 1. `</script` (case-insensitive) — closes the wrapping tag.\n * 2. `<!--` — flips the HTML parser into \"script data escaped\" state.\n * Combined with a later `<script` / `-->` it can hide code from the\n * parser; a defensive escape is cheaper than reasoning about it.\n * 3. U+2028 (LINE SEPARATOR) and U+2029 (PARAGRAPH SEPARATOR) — legal\n * inside JSON strings, but treated as line terminators inside JS\n * string literals on older engines. JSON.stringify emits them raw.\n *\n * Patterns use \\uXXXX escapes (not literal characters) so esbuild doesn't\n * interpret them as regex line terminators at parse time.\n */\nexport function escapeForScriptTag(json: string): string {\n // We escape the leading `<` of any HTML-sensitive opener (`</script`,\n // `<!--`) as `<` instead of `\\/` or `\\!`. `<` is the\n // strict-JSON form of `<`, so the output is parsable as JSON AND safe\n // to embed in a <script> tag.\n //\n // U+2028/U+2029: legal in JSON strings but treated as line terminators\n // inside legacy JS string literals. Escape both. RegExp constructor\n // avoids putting a literal line-separator in the source file (esbuild\n // refuses, and they're invisible in diffs).\n //\n // Raw control chars (LF/CR/NUL/etc.): we've seen JSON.stringify output\n // in the wild that somehow contains raw newlines mid-string (cause\n // still under investigation — likely something upstream that hands us\n // a pre-built string masquerading as a JSON fragment). JSON.parse\n // rejects raw control chars in strings, so the SPA crashes with\n // \"Bad control character\" or \"failed to fetch\". Belt-and-suspenders:\n // re-escape every raw control char so the output is always valid JSON\n // regardless of what produced it.\n const LS_RE = new RegExp('\\\\u2028', 'g');\n const PS_RE = new RegExp('\\\\u2029', 'g');\n return json\n .replace(/<(\\/script|!--)/gi, '\\\\u003c$1')\n .replace(LS_RE, '\\\\u2028')\n .replace(PS_RE, '\\\\u2029')\n .replace(/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f]/g, (c) => {\n // Skip \\t (0x09), \\n (0x0a), \\r (0x0d) — those are handled below\n // as proper JSON escapes so the parser keeps the line breaks\n // visible if they were intentional.\n return '\\\\u' + c.charCodeAt(0).toString(16).padStart(4, '0');\n })\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r');\n}\n\nfunction readAsset(webRoot: string, urlPath: string): string | null {\n // Strip query string and leading slash; resolve under webRoot.\n const clean = urlPath.split('?')[0].replace(/^\\//, '');\n const full = path.join(webRoot, clean);\n // Guard against path traversal even though the shell controls the URLs.\n if (!path.normalize(full).startsWith(path.normalize(webRoot))) return null;\n try {\n return fs.readFileSync(full, 'utf-8');\n } catch {\n return null;\n }\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport type { CommandModule } from 'yargs';\nimport { startWebServer } from '../core/web-server.js';\nimport {\n installCommandHook,\n removeCommandHookSync,\n} from '../core/command-hook-installer.js';\nimport { openUrl } from '../utils/platform.js';\n\nfunction info(message: string): void {\n process.stderr.write(message + '\\n');\n}\n\nfunction urlFilePath(): string {\n return path.join(os.homedir(), '.work', 'web.url');\n}\nfunction pidFilePath(): string {\n return path.join(os.homedir(), '.work', 'web.pid');\n}\n\nfunction isPidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction readPid(): number | null {\n try {\n const raw = fs.readFileSync(pidFilePath(), 'utf-8').trim();\n const n = Number(raw);\n return Number.isFinite(n) && n > 0 ? n : null;\n } catch {\n return null;\n }\n}\n\nfunction readUrl(): string | null {\n try {\n const v = fs.readFileSync(urlFilePath(), 'utf-8').trim();\n return v || null;\n } catch {\n return null;\n }\n}\n\n/** Best-effort ping. We don't strictly need it — the PID check above is\n * authoritative — but a 200 from /api/context confirms the server is\n * actually serving, not just a stale process holding the port. */\nasync function pingsAlive(url: string, timeoutMs = 500): Promise<boolean> {\n try {\n const ctrl = new AbortController();\n const timer = setTimeout(() => ctrl.abort(), timeoutMs);\n const res = await fetch(url + 'api/context', { signal: ctrl.signal });\n clearTimeout(timer);\n return res.ok;\n } catch {\n return false;\n }\n}\n\nfunction stopExisting(): boolean {\n const pid = readPid();\n if (!pid) {\n info(chalk.gray('No work web running.'));\n return false;\n }\n if (!isPidAlive(pid)) {\n info(chalk.gray(`Stale PID ${pid} — cleaning up.`));\n try { fs.unlinkSync(pidFilePath()); } catch { /* */ }\n try { fs.unlinkSync(urlFilePath()); } catch { /* */ }\n return false;\n }\n try {\n process.kill(pid);\n info(chalk.gray(`Stopped work web (PID ${pid}).`));\n try { fs.unlinkSync(pidFilePath()); } catch { /* */ }\n try { fs.unlinkSync(urlFilePath()); } catch { /* */ }\n return true;\n } catch (err) {\n info(chalk.red(`Failed to stop PID ${pid}: ${(err as Error).message}`));\n return false;\n }\n}\n\nexport const webCommand: CommandModule = {\n command: 'web',\n describe:\n 'Open the browser dashboard: every worktree session in one tab. Singleton — one process per user.',\n builder: (yargs) =>\n yargs\n .option('open', {\n type: 'boolean',\n default: true,\n describe: 'Auto-open the dashboard in the default browser. Use --no-open to skip.',\n })\n .option('stop', {\n type: 'boolean',\n default: false,\n describe: 'Stop a running work web instance and exit.',\n }),\n handler: async (argv) => {\n if (argv.stop) {\n stopExisting();\n process.exit(0);\n }\n\n // Singleton enforcement. Two work web servers running at once is\n // strictly bad: they fight over `~/.work/settings.json` hooks,\n // each one holds a port, and only the most-recently-started is\n // discoverable via `web.url`. Detect a live previous instance and\n // either reuse it (open browser) or refuse to start.\n const existingPid = readPid();\n if (existingPid && isPidAlive(existingPid)) {\n const url = readUrl();\n if (url && (await pingsAlive(url))) {\n info(\n chalk.gray(\n `work web already running at ${url} (PID ${existingPid}). Opening browser.`,\n ),\n );\n if (argv.open) openUrl(url);\n process.exit(0);\n }\n info(\n chalk.yellow(\n `PID ${existingPid} is alive but not responding at ${url ?? '<unknown>'}. Use \\`work web --stop\\` to kill it, then re-run.`,\n ),\n );\n process.exit(1);\n }\n // Stale files from a crashed previous run — wipe before we write\n // our own.\n try { fs.unlinkSync(pidFilePath()); } catch { /* */ }\n try { fs.unlinkSync(urlFilePath()); } catch { /* */ }\n\n const handle = await startWebServer();\n try {\n fs.mkdirSync(path.dirname(urlFilePath()), { recursive: true });\n fs.writeFileSync(urlFilePath(), handle.url);\n fs.writeFileSync(pidFilePath(), String(process.pid));\n } catch { /* */ }\n\n info(chalk.gray(`work web running at ${handle.url}`));\n info(chalk.gray('Press Ctrl+C to stop. Or: `work web --stop` from another shell.'));\n if (argv.open) openUrl(handle.url);\n\n // Install hooks so any live Claude in a worktree we know about picks\n // up pending review comments without the user having to type. Both\n // are no-ops when nothing's pending. Removed cleanly on shutdown.\n await Promise.all([\n installCommandHook({\n owner: 'web',\n event: 'UserPromptSubmit',\n command: 'work hook prompt-submit',\n timeoutSec: 5,\n }),\n installCommandHook({\n owner: 'web',\n event: 'Stop',\n command: 'work hook stop',\n timeoutSec: 5,\n }),\n ]).catch(() => { /* best-effort */ });\n\n const shutdown = () => {\n info(chalk.gray('\\nStopping work web.'));\n try { fs.unlinkSync(urlFilePath()); } catch { /* */ }\n try { fs.unlinkSync(pidFilePath()); } catch { /* */ }\n try { removeCommandHookSync('web', 'UserPromptSubmit'); } catch { /* */ }\n try { removeCommandHookSync('web', 'Stop'); } catch { /* */ }\n handle.stop();\n process.exit(0);\n };\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n // Windows doesn't deliver SIGTERM reliably; trap exit too so we\n // best-effort clean up our pid/url files even on abrupt deaths.\n process.on('exit', () => {\n try { fs.unlinkSync(pidFilePath()); } catch { /* */ }\n try { fs.unlinkSync(urlFilePath()); } catch { /* */ }\n });\n await new Promise(() => {});\n },\n};\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport { Hono } from 'hono';\nimport { streamSSE } from 'hono/streaming';\nimport { computeDiff } from './diff-pipeline.js';\nimport { resolveRepoDiff } from './diff-scope.js';\nimport { loadHistory, type WorktreeSession } from './history.js';\nimport {\n disposeAllWatchers,\n findSession,\n sessionIdFor,\n subscribeSession,\n} from './web-state.js';\nimport { readSessionMeta } from './session-meta.js';\nimport { claudeProjectsRoot } from './claude-activity.js';\nimport { createFsWatcher } from './fs-watcher.js';\nimport { mountSessionCommentRoutes } from './session-comment-routes.js';\nimport { mountPanesRoutes } from './panes-routes.js';\nimport { mountWorktreeRoutes } from './worktree-routes.js';\nimport { mountScopeRoutes } from './scope-routes.js';\nimport { mountTerminalRoutes } from './terminal-routes.js';\nimport { disposeAllScopes, listScopes } from './scope-manager.js';\nimport { clearCheckpoints } from './checkpoint.js';\nimport { attachTerminalWs } from './terminal-ws.js';\nimport { disposeAllPtys } from './pty-pool.js';\nimport { resolveWebRoot } from './web-static.js';\nimport { serveSpa } from './spa-handler.js';\nimport { launch, type DiffServerHandle, type SseEvent } from './diff-server.js';\nimport type { ParsedFile } from './diff-parse.js';\n\nexport type WebServerHandle = DiffServerHandle;\n\nfunction sessionToWire(s: WorktreeSession) {\n const id = sessionIdFor(s);\n const meta = readSessionMeta(id, s);\n return {\n id,\n target: s.target,\n branch: s.branch,\n isGroup: s.isGroup,\n paths: s.paths,\n baseBranch: s.baseBranch,\n jiraKey: s.jiraKey,\n createdAt: s.createdAt,\n lastAccessedAt: s.lastAccessedAt,\n draftCount: meta.draftCount,\n commentCount: meta.commentCount,\n claudeCount: meta.claudeCount,\n ptyStatus: meta.ptyStatus,\n lastActivity: meta.lastActivity,\n activityState: meta.activityState,\n pendingForClaudeCount: meta.pendingForClaudeCount,\n };\n}\n\nexport interface RepoData {\n name: string;\n root: string;\n files: ParsedFile[];\n /** Per-repo resolved parent for branch-mode diffs. `HEAD` for uncommitted. */\n resolvedBase: string;\n}\n\nexport type DiffBase = 'uncommitted' | 'branch';\n\ninterface SessionDiffResult {\n repos: RepoData[];\n /** Primary repo's resolved parent — used for the single-line \"vs X\"\n * badge. For groups this is `paths[0]`'s parent. */\n resolvedBase: string;\n}\n\n/**\n * Compute the diff for a session under one of two scopes:\n * - 'uncommitted' — `git diff HEAD` (default). Just the working-tree\n * deltas — what's not committed yet.\n * - 'branch' — everything since this worktree was forked. Uses the\n * session's recorded `baseBranch` when known, falls back to\n * auto-detection against main/master/dev/develop.\n *\n * Per-repo `resolvedBase` is included on each entry so the UI can label\n * per-repo if it wants to (groups may have different parents per repo).\n */\nfunction computeSessionDiff(s: WorktreeSession, base: DiffBase): SessionDiffResult {\n const resolved = s.paths.map((p) => resolveRepoDiff(p, base, s.baseBranch));\n const repos = s.paths.map((p, i) => ({\n name: path.basename(p),\n root: p,\n resolvedBase: resolved[i].resolvedBase,\n files: computeDiff({ root: p, diffArg: resolved[i].diffArg }),\n }));\n return { repos, resolvedBase: resolved[0]?.resolvedBase ?? 'HEAD' };\n}\n\nexport async function startWebServer(): Promise<WebServerHandle> {\n const webRoot = resolveWebRoot();\n if (!webRoot) {\n throw new Error(\n 'Could not find dist/web/. Run `npm run build:web` (or `npm run build`) first.',\n );\n }\n\n const sseListeners = new Set<(e: SseEvent) => void>();\n const broadcast = (event: string, data: unknown) => {\n for (const cb of sseListeners) cb({ event, data });\n };\n\n // Watch ~/.work/history.json so the sidebar reflects worktrees created\n // (or removed) by other terminals in real time.\n const home = os.homedir();\n const historyPath = path.join(home, '.work', 'history.json');\n const onHistoryChange = () =>\n broadcast('sessions-changed', { ts: Date.now() });\n fs.watchFile(historyPath, { interval: 1000 }, onHistoryChange);\n\n // Same idea for tasks — `work todo add` from a separate terminal\n // should refresh the dashboard's Tasks pane without a manual reload.\n const tasksPath = path.join(home, '.work', 'tasks.json');\n const onTasksChange = () => broadcast('tasks-changed', { ts: Date.now() });\n fs.watchFile(tasksPath, { interval: 1000 }, onTasksChange);\n\n // Watch Claude's per-project transcripts so the dashboard sees external\n // terminals coming alive. Claude writes constantly while it's thinking;\n // the watcher debounces to 250 ms so we don't spam the sidebar 100×/s\n // mid-turn. The same broadcast also covers our own PTYs writing here.\n const projectsRoot = claudeProjectsRoot();\n let activityWatcher: { stop(): void } | null = null;\n try {\n if (fs.existsSync(projectsRoot)) {\n activityWatcher = createFsWatcher({\n roots: [projectsRoot],\n debounceMs: 250,\n onChange: () => broadcast('sessions-changed', { ts: Date.now() }),\n });\n }\n } catch { /* watcher startup is best-effort */ }\n\n // Decay tick: even when nothing writes, sessions transition active → open\n // → stale purely by elapsed time. Re-broadcast every 10 s so the badges\n // catch up. Cheap — the client just refetches /api/sessions.\n const decayTick = setInterval(\n () => broadcast('sessions-changed', { ts: Date.now() }),\n 10_000,\n );\n\n const app = new Hono();\n\n app.get('/api/context', (c) => c.json({ mode: 'dashboard' }));\n\n app.get('/api/sessions', (c) =>\n c.json({ sessions: loadHistory().map(sessionToWire) }),\n );\n\n app.get('/api/sessions/:id/diff', (c) => {\n const id = c.req.param('id');\n const session = findSession(id);\n if (!session) return c.json({ error: 'unknown session' }, 404);\n const baseParam = c.req.query('base') ?? 'uncommitted';\n const base: DiffBase =\n baseParam === 'branch' ? 'branch' : 'uncommitted';\n try {\n const { repos, resolvedBase } = computeSessionDiff(session, base);\n return c.json({ sessionId: id, base, resolvedBase, repos });\n } catch (err) {\n return c.json({ error: (err as Error).message }, 500);\n }\n });\n\n // Per-session comments (file-backed). Emits comments-changed via broadcast.\n mountSessionCommentRoutes(app, { broadcast });\n\n // PRs / Jira / Tasks read endpoints + tasks CRUD. Emits tasks-changed.\n mountPanesRoutes(app, { broadcast });\n\n // Worktree mutations (create/remove/sync/rebase/open-editor). Each\n // emits sessions-changed so the sidebar refreshes.\n mountWorktreeRoutes(app, { broadcast });\n\n // Ad-hoc scopes registered by `wd` invocations — gives the dashboard\n // an addressable URL per scope (/diff/<hash>, /review/<hash>) so we\n // can collapse the standalone wd-server/wd -c daemons into this\n // single process.\n mountScopeRoutes(app, { broadcast });\n\n // PTY upgrade endpoint. Returns a noop response — the upgrade is handled\n // by the server's `upgrade` event below.\n mountTerminalRoutes(app);\n\n app.get('/events', (c) => {\n const wantedSession = c.req.query('session');\n return streamSSE(c, async (stream) => {\n const listener = (e: SseEvent) => {\n stream\n .writeSSE({ event: e.event, data: JSON.stringify(e.data) })\n .catch(() => { /* */ });\n };\n sseListeners.add(listener);\n await stream.writeSSE({ event: 'connected', data: '' });\n\n let unsubscribe: (() => void) | null = null;\n if (wantedSession) {\n unsubscribe = subscribeSession(wantedSession, () => {\n stream\n .writeSSE({\n event: 'diff-changed',\n data: JSON.stringify({ sessionId: wantedSession }),\n })\n .catch(() => { /* */ });\n });\n }\n await new Promise<void>((resolve) => {\n stream.onAbort(() => {\n sseListeners.delete(listener);\n if (unsubscribe) unsubscribe();\n resolve();\n });\n });\n });\n });\n\n // SPA fallback last.\n app.get('*', (c) => serveSpa(c, webRoot));\n\n const handle = await launch(app);\n const wsBridge = attachTerminalWs(handle.httpServer, handle.port);\n process.stderr.write(chalk.gray(`[web] dashboard at ${handle.url}\\n`));\n\n return {\n url: handle.url,\n port: handle.port,\n stop: async () => {\n fs.unwatchFile(historyPath, onHistoryChange);\n fs.unwatchFile(tasksPath, onTasksChange);\n clearInterval(decayTick);\n activityWatcher?.stop();\n disposeAllWatchers();\n // Sweep checkpoint refs + manifests for every active scope BEFORE\n // wiping the registry — otherwise `refs/wd/<hash>/*` refs leak\n // across `work web` restarts and accumulate without bound in\n // every repo the user has reviewed.\n for (const scope of listScopes()) {\n try {\n clearCheckpoints(scope.hash, scope.paths);\n } catch {\n // Best-effort — partial cleanup is fine, log only matters for\n // diagnosis and doesn't change the shutdown outcome.\n }\n }\n disposeAllScopes();\n disposeAllPtys();\n wsBridge.close();\n await handle.stop();\n },\n };\n}\n","import path from 'node:path';\nimport crypto from 'node:crypto';\nimport chokidar, { type FSWatcher } from 'chokidar';\nimport { loadHistory, type WorktreeSession } from './history.js';\n\n/** Stable per-session id, same algorithm as web-server. */\nexport function sessionIdFor(s: WorktreeSession): string {\n return crypto\n .createHash('sha1')\n .update(`${s.target}:${s.branch}`)\n .digest('hex')\n .slice(0, 12);\n}\n\nexport function findSession(sessionId: string): WorktreeSession | null {\n return loadHistory().find((s) => sessionIdFor(s) === sessionId) ?? null;\n}\n\ninterface WatcherEntry {\n watcher: FSWatcher;\n subscribers: Set<() => void>;\n debounce: NodeJS.Timeout | null;\n}\n\nconst sessionWatchers = new Map<string, WatcherEntry>();\nconst DEBOUNCE_MS = 150;\n\n/**\n * Subscribe to filesystem changes for a session's worktree(s). chokidar is\n * started on first subscriber and stopped when the last one leaves —\n * reference-counted so the cost stays proportional to what's actually being\n * viewed in the browser.\n *\n * Returns an unsubscribe function. Safe to call multiple times.\n */\nexport function subscribeSession(\n sessionId: string,\n onChange: () => void,\n): () => void {\n const session = findSession(sessionId);\n if (!session) return () => { /* unknown session */ };\n\n let entry = sessionWatchers.get(sessionId);\n if (!entry) {\n const roots = session.paths;\n const watcher = chokidar.watch(roots, {\n ignored: (filePath) => {\n for (const r of roots) {\n const rel = path.relative(r, filePath).replace(/\\\\/g, '/');\n if (rel === '.git' || rel.startsWith('.git/')) return true;\n }\n return false;\n },\n ignoreInitial: true,\n persistent: true,\n awaitWriteFinish: { stabilityThreshold: 50, pollInterval: 20 },\n });\n const newEntry: WatcherEntry = {\n watcher,\n subscribers: new Set(),\n debounce: null,\n };\n watcher.on('all', () => {\n if (newEntry.debounce) clearTimeout(newEntry.debounce);\n newEntry.debounce = setTimeout(() => {\n newEntry.debounce = null;\n for (const cb of newEntry.subscribers) {\n try { cb(); } catch { /* swallow */ }\n }\n }, DEBOUNCE_MS);\n });\n sessionWatchers.set(sessionId, newEntry);\n entry = newEntry;\n }\n\n entry.subscribers.add(onChange);\n\n let released = false;\n return () => {\n if (released) return;\n released = true;\n entry!.subscribers.delete(onChange);\n if (entry!.subscribers.size === 0) {\n if (entry!.debounce) clearTimeout(entry!.debounce);\n entry!.watcher.close().catch(() => { /* */ });\n sessionWatchers.delete(sessionId);\n }\n };\n}\n\n/** Stop every active session watcher. Called on server shutdown. */\nexport function disposeAllWatchers(): void {\n for (const [, entry] of sessionWatchers) {\n if (entry.debounce) clearTimeout(entry.debounce);\n entry.watcher.close().catch(() => { /* */ });\n }\n sessionWatchers.clear();\n}\n","import { PtySession } from '../tui/session.js';\nimport { findSession } from './web-state.js';\nimport { loadConfig } from './config.js';\nimport { getAiTool } from './ai-launcher.js';\n\nexport interface PooledPty {\n session: PtySession;\n readonly sessionId: string;\n /** Replay buffer of recent PTY output, sent to each new attaching client. */\n replay(): string;\n write(data: string): void;\n resize(cols: number, rows: number): void;\n /** Subscribe to live PTY output. Returns an unsubscribe disposer. */\n subscribe(cb: (data: string) => void): () => void;\n dispose(): void;\n isExited(): boolean;\n}\n\n/**\n * Per-session PTY pool. Lazily spawns Claude (or the configured AI tool)\n * for a session the first time someone attaches a terminal. PTYs survive\n * browser disconnects — only `dispose()` (server shutdown) kills them.\n *\n * Each PTY has an in-memory replay buffer; a fresh WS attach replays it so\n * the user sees the existing scrollback rather than a blank screen.\n */\nconst pool = new Map<string, PooledPty>();\nconst REPLAY_MAX = 64 * 1024;\n\nexport function getOrCreatePty(sessionId: string): PooledPty | null {\n const existing = pool.get(sessionId);\n if (existing && !existing.isExited()) return existing;\n if (existing) pool.delete(sessionId);\n\n const session = findSession(sessionId);\n if (!session) return null;\n\n const cwd = session.paths[0];\n if (!cwd) return null;\n\n const config = loadConfig() ?? {};\n const tool = getAiTool(config);\n const pty = new PtySession(cwd, 120, 32, undefined, {\n tool,\n resume: true,\n port: session.port,\n });\n\n const subscribers = new Set<(data: string) => void>();\n let replayBuf = '';\n pty.setOutputHandler((data) => {\n replayBuf += data;\n if (replayBuf.length > REPLAY_MAX) {\n replayBuf = replayBuf.slice(replayBuf.length - REPLAY_MAX);\n }\n for (const cb of subscribers) {\n try { cb(data); } catch { /* */ }\n }\n });\n\n const entry: PooledPty = {\n session: pty,\n sessionId,\n replay: () => replayBuf,\n write: (data) => pty.write(data),\n resize: (cols, rows) => pty.resize(cols, rows),\n subscribe(cb) {\n subscribers.add(cb);\n return () => subscribers.delete(cb);\n },\n dispose: () => {\n subscribers.clear();\n pty.dispose();\n pool.delete(sessionId);\n },\n isExited: () => pty.exited,\n };\n pool.set(sessionId, entry);\n return entry;\n}\n\n/** Side-effect-free check: does an active (non-exited) PTY exist for this\n * session? Used by session-meta to compute the \"running/idle\" badge. */\nexport function peekPty(sessionId: string): boolean {\n const existing = pool.get(sessionId);\n return !!existing && !existing.isExited();\n}\n\nexport function disposeAllPtys(): void {\n for (const p of pool.values()) p.dispose();\n pool.clear();\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\nimport { createCommentStore, type CommentInput, type CommentStore } from './comment-store.js';\nimport { ensureFile, withFileLockSync, atomicWriteFile } from './fs-safe.js';\nimport type { Comment } from './comment-types.js';\n\n/**\n * File-backed comment store. One JSON file per session at\n * `~/.work/comments/<sessionId>.json`. Mutations are serialized in-process\n * via a simple in-memory queue so two browser tabs writing at the same\n * instant cannot interleave atomic-write attempts.\n *\n * Atomicity: every save writes a tmp file in the same dir then renames.\n * Crash-resistant for the common case (single mutator at a time).\n */\nexport interface CommentFileStore extends CommentStore {\n /** Drop the cached in-memory store and reload from disk. */\n reload(): void;\n}\n\n/** Canonical location of all per-session comment files. A function so it\n * re-resolves `os.homedir()` on every call — tests that mock `homedir()`\n * need this, and the cost is one `path.join` per access. */\nexport function commentsDir(): string {\n return path.join(os.homedir(), '.work', 'comments');\n}\n\nfunction ensureDir(): void {\n fs.mkdirSync(commentsDir(), { recursive: true });\n}\n\n/** File path for one session's comment store. Use this rather than rolling\n * your own concat. */\nexport function commentsFileFor(sessionId: string): string {\n return path.join(commentsDir(), `${sessionId}.json`);\n}\n\nfunction pathFor(sessionId: string): string {\n return commentsFileFor(sessionId);\n}\n\nfunction readDisk(sessionId: string): Comment[] {\n try {\n const raw = fs.readFileSync(pathFor(sessionId), 'utf-8');\n const parsed = JSON.parse(raw);\n return Array.isArray(parsed) ? (parsed as Comment[]) : [];\n } catch {\n return [];\n }\n}\n\nfunction writeDisk(sessionId: string, comments: Comment[]): void {\n ensureDir();\n atomicWriteFile(pathFor(sessionId), JSON.stringify(comments, null, 2));\n}\n\nconst cache = new Map<string, CommentFileStore>();\n\nexport function getCommentFileStore(sessionId: string): CommentFileStore {\n const existing = cache.get(sessionId);\n if (existing) return existing;\n\n const inner = createCommentStore();\n\n /** Replace the in-memory contents with the current on-disk contents.\n * Used to reconcile before a mutation so concurrent writers (e.g. a\n * `work broadcast` process appending under the same lock) aren't lost. */\n function reloadInner(): void {\n const list = inner.list() as Comment[];\n list.length = 0;\n for (const c of readDisk(sessionId)) list.push(c);\n }\n\n // Seed from disk.\n reloadInner();\n\n /**\n * Run a read-modify-write of this session's comment file under a\n * cross-process lock (§5.2): acquire the lock, reload the in-memory store\n * from disk so it reflects any concurrent appends, apply `mutate`, persist\n * atomically, then release. `persisted` reports whether anything changed so\n * a no-op (e.g. removing a missing id) can skip the write.\n */\n function lockedMutate<T>(\n mutate: () => { value: T; persisted: boolean },\n ): T {\n const file = pathFor(sessionId);\n ensureDir();\n ensureFile(file, '[]');\n return withFileLockSync(file, () => {\n reloadInner();\n const { value, persisted } = mutate();\n if (persisted) writeDisk(sessionId, inner.snapshot());\n return value;\n });\n }\n\n const store: CommentFileStore = {\n list: () => inner.list(),\n snapshot: () => inner.snapshot(),\n post(input: CommentInput) {\n return lockedMutate(() => {\n const c = inner.post(input);\n return { value: c, persisted: true };\n });\n },\n remove(id: string) {\n return lockedMutate(() => {\n const r = inner.remove(id);\n return { value: r, persisted: r };\n });\n },\n submit(summary: string | undefined) {\n return lockedMutate(() => {\n const result = inner.submit(summary);\n return { value: result, persisted: true };\n });\n },\n discardDrafts() {\n return lockedMutate(() => {\n const n = inner.discardDrafts();\n return { value: n, persisted: n > 0 };\n });\n },\n reload() {\n reloadInner();\n },\n };\n cache.set(sessionId, store);\n return store;\n}\n\n/** Clear the in-memory cache. Test/server-shutdown hook. */\nexport function clearCommentStoreCache(): void {\n cache.clear();\n}\n","/**\n * Bridge between `work web` review comments and any live Claude session\n * running in the same worktree. Storage layout:\n *\n * ~/.work/comments/<sessionId>.json — full comment store (existing)\n * ~/.work/comments/<sessionId>.delivered.json — array of comment ids that\n * have been surfaced to Claude\n *\n * \"Pending\" = published, user-authored, not in the delivered list. Replies\n * authored by Claude (author === 'claude') are excluded — Claude wrote\n * them; we don't need to echo them back.\n *\n * Delivery is lazy: when a Claude in the matching worktree submits a\n * prompt, the hook reads pending comments via this module, prints them as\n * a system-reminder block to stdout (which Claude Code injects into the\n * conversation), and marks them delivered.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { loadHistory, type WorktreeSession } from './history.js';\nimport { sessionIdFor } from './web-state.js';\nimport {\n commentsDir,\n commentsFileFor,\n getCommentFileStore,\n} from './comment-file-store.js';\nimport type { Comment } from './comment-types.js';\n\nfunction pathFor(sessionId: string): {\n comments: string;\n delivered: string;\n} {\n return {\n comments: commentsFileFor(sessionId),\n delivered: path.join(commentsDir(), `${sessionId}.delivered.json`),\n };\n}\n\nfunction readJson<T>(filePath: string, fallback: T): T {\n try {\n return JSON.parse(fs.readFileSync(filePath, 'utf8')) as T;\n } catch {\n return fallback;\n }\n}\n\nfunction writeAtomic(filePath: string, content: string): void {\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;\n fs.writeFileSync(tmp, content, 'utf-8');\n fs.renameSync(tmp, filePath);\n}\n\n/** Norm-path comparison that mirrors what we do server-side. */\nfunction normalize(p: string): string {\n return path.resolve(p).replace(/\\\\/g, '/').toLowerCase();\n}\n\n/** Map a Claude cwd back to a session. Tries direct-match against any\n * session's path first, then ancestor match (so cwd inside a subdir of a\n * worktree still resolves to the worktree's session). */\nexport function findSessionForCwd(cwd: string): WorktreeSession | null {\n const norm = normalize(cwd);\n const sessions = loadHistory();\n // Direct match: cwd == one of the session's paths.\n for (const s of sessions) {\n for (const p of s.paths) {\n if (normalize(p) === norm) return s;\n }\n }\n // Ancestor match: cwd starts with one of the session's paths + sep.\n // Pick the longest matching prefix so nested worktrees disambiguate.\n let best: { session: WorktreeSession; len: number } | null = null;\n for (const s of sessions) {\n for (const p of s.paths) {\n const np = normalize(p);\n if (norm.startsWith(np + '/') && (!best || np.length > best.len)) {\n best = { session: s, len: np.length };\n }\n }\n }\n return best?.session ?? null;\n}\n\n/** Returns published user comments that haven't been delivered yet.\n * Reads through the file-store cache so we see in-flight writes\n * (`session-meta.ts` and other readers couldn't, when they re-read the\n * disk directly). */\nexport function readPendingForSession(sessionId: string): Comment[] {\n const paths = pathFor(sessionId);\n const comments = getCommentFileStore(sessionId).snapshot();\n const delivered = new Set(readJson<string[]>(paths.delivered, []));\n return comments.filter(\n (c) =>\n c.status === 'published' &&\n c.author === 'user' &&\n !delivered.has(c.id),\n );\n}\n\n/** Persist a delivery batch. Adds these ids to the delivered set so they\n * never get re-surfaced. */\nexport function markDelivered(sessionId: string, ids: string[]): void {\n if (ids.length === 0) return;\n const paths = pathFor(sessionId);\n const delivered = new Set(readJson<string[]>(paths.delivered, []));\n for (const id of ids) delivered.add(id);\n writeAtomic(paths.delivered, JSON.stringify(Array.from(delivered), null, 2));\n}\n\n/** Cap the size of one comment body we surface to Claude. A pathologically\n * long comment shouldn't blow out Claude's context — we truncate, then\n * hint at the rest via \"(truncated)\". 4 KB is generous for a code-review\n * note while leaving headroom for batches. */\nconst MAX_BODY_BYTES = 4 * 1024;\n/** Overall cap on the whole system-reminder payload. Multiple long\n * comments at once still get bounded. */\nconst MAX_TOTAL_BYTES = 32 * 1024;\n\n/**\n * Format pending comments as a system-reminder block suitable for stdout.\n * Returns empty string when there's nothing pending — the caller (the\n * `work hook` CLI) just exits silently in that case.\n *\n * Truncates pathologically long bodies and caps the overall payload so\n * one runaway comment can't displace the rest of Claude's context.\n */\nexport function formatPendingForPrompt(pending: Comment[]): string {\n if (pending.length === 0) return '';\n\n const sorted = [...pending].sort((a, b) =>\n a.createdAt.localeCompare(b.createdAt),\n );\n\n const general = sorted.filter((c) => c.side === 'general' && !c.parentId);\n const inline = sorted.filter((c) => c.side !== 'general' && !c.parentId);\n const replies = sorted.filter((c) => c.parentId);\n\n const lines: string[] = [];\n lines.push('<system-reminder>');\n lines.push(\n `New review comments from \\`work web\\` (${pending.length} item${pending.length === 1 ? '' : 's'}):`,\n );\n lines.push('');\n\n if (general.length > 0) {\n lines.push('## General notes');\n for (const c of general) {\n // General notes (this is where `work broadcast` lands) may be multi-line\n // prompts — deliver the whole body, not just line 1. Only the byte cap\n // applies. Inline/reply comments still use the one-line `formatBody`.\n lines.push(formatFullBody('-', c));\n }\n lines.push('');\n }\n\n if (inline.length > 0) {\n lines.push('## Inline comments');\n for (const c of inline) {\n const where = `${c.repo}/${c.file}:${c.line} (${c.side})`;\n lines.push(formatBody(`- ${where}`, c));\n }\n lines.push('');\n }\n\n if (replies.length > 0) {\n lines.push('## Replies');\n for (const c of replies) {\n lines.push(formatBody(`- (reply to ${c.parentId})`, c));\n }\n lines.push('');\n }\n\n lines.push(\n 'Address them as part of your next response. You can reply via the same review UI by posting back to the latest review URL at `~/.work/web.url` + `/api/sessions/<id>/comments` with `author: \"claude\"`.',\n );\n lines.push('</system-reminder>');\n const out = lines.join('\\n');\n if (out.length <= MAX_TOTAL_BYTES) return out;\n const trimmed = out.slice(0, MAX_TOTAL_BYTES - 200);\n return (\n trimmed +\n '\\n\\n(…review payload truncated for context-window safety; ' +\n `${pending.length} comment(s) total — open work web for the full list.)\\n` +\n '</system-reminder>'\n );\n}\n\nfunction formatBody(prefix: string, c: Comment): string {\n const lead = c.body.split('\\n')[0].trim();\n const capped =\n lead.length > MAX_BODY_BYTES ? `${lead.slice(0, MAX_BODY_BYTES)}…` : lead;\n return `${prefix}: ${capped}${c.body.includes('\\n') ? ' …' : ''}`;\n}\n\n/** Like `formatBody` but preserves the full multi-line body (only the byte\n * cap applies). Multi-line bodies are emitted under the bullet, indented, so\n * a broadcast prompt arrives intact rather than truncated to its first line. */\nfunction formatFullBody(prefix: string, c: Comment): string {\n const body = c.body.trim();\n const capped =\n body.length > MAX_BODY_BYTES ? `${body.slice(0, MAX_BODY_BYTES)}…` : body;\n const bodyLines = capped.split('\\n');\n if (bodyLines.length === 1) return `${prefix}: ${bodyLines[0]}`;\n const [first, ...rest] = bodyLines;\n return [`${prefix}: ${first}`, ...rest.map((l) => ` ${l}`)].join('\\n');\n}\n\n/** Re-export so consumers don't have to know which module owns it. */\nexport { sessionIdFor };\n","import { peekPty } from './pty-pool.js';\nimport {\n readSessionActivity,\n type ActivityState,\n} from './claude-activity.js';\nimport { getCommentFileStore } from './comment-file-store.js';\nimport { readPendingForSession } from './pending-delivery.js';\nimport type { WorktreeSession } from './history.js';\n\nexport type PtyStatus = 'running' | 'idle';\n\nexport interface SessionMeta {\n draftCount: number;\n commentCount: number;\n /** Comments authored by claude — used by the client to compute unread. */\n claudeCount: number;\n /** Set when *our* PTY pool has a live Claude for this session. Doesn't\n * catch external terminals — `activityState` does. */\n ptyStatus: PtyStatus;\n /** ms since epoch of Claude's last write across this worktree, or null. */\n lastActivity: number | null;\n /** Derived from `lastActivity`: 'active' (≤30 s), 'open' (≤5 min), 'stale'. */\n activityState: ActivityState;\n /** Published user comments not yet surfaced to Claude via the\n * UserPromptSubmit hook. Drops to zero after Claude takes its next turn. */\n pendingForClaudeCount: number;\n}\n\n/**\n * Cheap per-session metadata. Goes through the file-store cache so badge\n * counts reflect in-flight writes that haven't yet hit disk — reading the\n * raw JSON behind the cache's back produced stale counts under load.\n *\n * `session` is required because we need the worktree paths to look up\n * Claude's transcript directory (the sessionId hash alone isn't enough).\n */\nexport function readSessionMeta(\n sessionId: string,\n session: WorktreeSession,\n): SessionMeta {\n const comments = getCommentFileStore(sessionId).snapshot();\n let drafts = 0;\n let claude = 0;\n for (const c of comments) {\n if (c.status === 'draft') drafts++;\n if (c.author === 'claude') claude++;\n }\n const activity = readSessionActivity(session);\n const pending = readPendingForSession(sessionId).length;\n return {\n draftCount: drafts,\n commentCount: comments.length,\n claudeCount: claude,\n ptyStatus: peekPty(sessionId) ? 'running' : 'idle',\n lastActivity: activity.lastActivity,\n activityState: activity.state,\n pendingForClaudeCount: pending,\n };\n}\n","import { Hono } from 'hono';\nimport { zValidator } from '@hono/zod-validator';\nimport { getCommentFileStore } from './comment-file-store.js';\nimport { findSession } from './web-state.js';\nimport { peekPty, getOrCreatePty } from './pty-pool.js';\nimport {\n formatPendingForPrompt,\n markDelivered,\n readPendingForSession,\n} from './pending-delivery.js';\nimport { commentInputSchema, submitReviewSchema } from './comment-schemas.js';\n\nexport interface MountOptions {\n /** Server-level broadcast — used to emit comments-changed events scoped\n * by sessionId so the SPA can refetch the right session's comments. */\n broadcast: (event: string, data: unknown) => void;\n}\n\n/**\n * Per-session comment endpoints under `/api/sessions/:id/`. Each session's\n * comments are persisted to its own JSON file. The dashboard SPA's\n * ReviewProvider uses these endpoints when it's in `dashboard` context.\n */\nexport function mountSessionCommentRoutes(\n app: Hono,\n opts: MountOptions,\n): void {\n function requireSession(id: string) {\n return findSession(id);\n }\n\n app.get('/api/sessions/:id/comments', (c) => {\n const id = c.req.param('id');\n if (!requireSession(id)) return c.json({ error: 'unknown session' }, 404);\n const store = getCommentFileStore(id);\n return c.json({ comments: store.snapshot() });\n });\n\n app.post(\n '/api/sessions/:id/comments',\n zValidator('json', commentInputSchema),\n (c) => {\n const id = c.req.param('id');\n if (!requireSession(id)) return c.json({ error: 'unknown session' }, 404);\n const store = getCommentFileStore(id);\n try {\n const comment = store.post(c.req.valid('json'));\n opts.broadcast('comments-changed', { sessionId: id, id: comment.id });\n // If a Claude is sitting in OUR own PTY (the Terminal tab is open\n // for this session), nudge it immediately by writing the pending\n // comments to stdin. The Stop / UserPromptSubmit hooks already\n // cover the cases where Claude is mid-turn or the user types; this\n // closes the \"idle in our PTY, user not typing\" case.\n deliverViaOwnedPty(id, comment.author);\n return c.json({ comment, comments: store.snapshot() });\n } catch (err) {\n return c.json({ error: (err as Error).message }, 400);\n }\n },\n );\n\n app.delete('/api/sessions/:id/comments/:cid', (c) => {\n const id = c.req.param('id');\n const cid = c.req.param('cid');\n if (!requireSession(id)) return c.json({ error: 'unknown session' }, 404);\n const store = getCommentFileStore(id);\n const removed = store.remove(cid);\n if (removed) opts.broadcast('comments-changed', { sessionId: id, deleted: cid });\n return c.json({ comments: store.snapshot() });\n });\n\n app.post(\n '/api/sessions/:id/submit-review',\n zValidator('json', submitReviewSchema),\n (c) => {\n const id = c.req.param('id');\n if (!requireSession(id)) return c.json({ error: 'unknown session' }, 404);\n const store = getCommentFileStore(id);\n const result = store.submit(c.req.valid('json').summary);\n opts.broadcast('comments-changed', {\n sessionId: id,\n submittedCount: result.drafts.length,\n });\n return c.json({\n count: result.drafts.length,\n comments: store.snapshot(),\n });\n },\n );\n\n app.post('/api/sessions/:id/discard-review', (c) => {\n const id = c.req.param('id');\n if (!requireSession(id)) return c.json({ error: 'unknown session' }, 404);\n const store = getCommentFileStore(id);\n const discarded = store.discardDrafts();\n if (discarded > 0) opts.broadcast('comments-changed', { sessionId: id });\n return c.json({ discarded, comments: store.snapshot() });\n });\n}\n\n/**\n * If `work web` owns a live PTY for this session (the user opened the\n * Terminal tab and Claude is running there), push the pending comments\n * directly to stdin so Claude sees them without the user typing anything.\n *\n * We only push for user-authored comments — Claude-authored ones are\n * replies we already routed via the API. We deliberately don't spawn a\n * PTY here (use `peekPty`, not `getOrCreatePty`) — pushing to a freshly\n * spawned Claude is weird, and the user expects to control when Claude\n * starts.\n */\nfunction deliverViaOwnedPty(sessionId: string, author: string): void {\n if (author !== 'user') return;\n if (!peekPty(sessionId)) return;\n\n const pending = readPendingForSession(sessionId);\n if (pending.length === 0) return;\n\n // peekPty returned true so this won't spawn — it'll return the existing\n // entry. We use getOrCreatePty because it's the only public way to get\n // the entry handle. (Could refactor to expose a pure peek that returns\n // the PooledPty, but not yet worth it.)\n const pty = getOrCreatePty(sessionId);\n if (!pty) return;\n\n const text = formatPendingForPrompt(pending);\n if (!text) return;\n\n // Mark delivered ONLY after a successful stdin write. If pty.write\n // throws (PTY exited mid-call, encoding error, anything) we leave the\n // comment as pending so the UserPromptSubmit / Stop hook can still\n // pick it up on the next Claude turn — better duplicate delivery than\n // silent loss. Writing the system reminder + newline so Claude treats\n // it as a submitted user prompt.\n try {\n pty.write(text + '\\n');\n } catch {\n return;\n }\n markDelivered(\n sessionId,\n pending.map((c) => c.id),\n );\n}\n","import { Hono } from 'hono';\nimport { zValidator } from '@hono/zod-validator';\nimport { z } from 'zod';\nimport { loadConfig } from './config.js';\nimport { fetchAllPullRequests, type PullRequestInfo } from './pr.js';\nimport { fetchJiraPane, type JiraIssue } from './jira.js';\nimport {\n addTask,\n completeTask,\n editTask,\n getTasks,\n removeTask,\n uncompleteTask,\n} from './tasks.js';\n\nexport interface PanesMountOptions {\n /** Server-level broadcast so mutations emit *-changed events. */\n broadcast: (event: string, data: unknown) => void;\n}\n\n/**\n * Hono sub-app exposing the read endpoints and the tasks-CRUD that drive\n * the dashboard's PRs / Jira / Tasks sidebars. Mirrors the data sources\n * the TUI already uses (`core/pr.ts`, `core/jira.ts`, `core/tasks.ts`)\n * one-to-one — no new logic, just a network surface.\n */\nexport function mountPanesRoutes(\n app: Hono,\n opts: PanesMountOptions,\n): void {\n // -- Projects ----------------------------------------------------------\n //\n // Used by the new-worktree modal's project picker. Lists configured\n // single repos and groups together; the client filters.\n app.get('/api/projects', (c) => {\n const config = loadConfig();\n if (!config) {\n return c.json({ singles: [], groups: [] });\n }\n const singles = Object.keys(config.repos).map((alias) => ({\n name: alias,\n kind: 'single' as const,\n path: config.repos[alias],\n }));\n const groups = Object.entries(config.groups).map(([name, aliases]) => ({\n name,\n kind: 'group' as const,\n members: aliases,\n }));\n return c.json({ singles, groups });\n });\n\n // -- PRs ---------------------------------------------------------------\n //\n // In-flight dedup: `gh pr list` is slow and the pane fires on a 60s\n // interval. A burst of refresh clicks (or interval + sessions-changed\n // racing) would otherwise spawn N × gh subprocesses concurrently. The\n // first concurrent request triggers the fetch; everyone else awaits\n // the same promise.\n let prsInFlight: Promise<{ prs: PullRequestInfo[] }> | null = null;\n app.get('/api/prs', async (c) => {\n const config = loadConfig();\n if (!config) return c.json({ prs: [] });\n if (!prsInFlight) {\n prsInFlight = (async () => {\n try {\n const map = await fetchAllPullRequests(config.repos);\n // Flatten: one entry per PR, with the resolved repo alias attached.\n const prs = Array.from(map.values()).flat();\n return { prs };\n } finally {\n prsInFlight = null;\n }\n })();\n }\n try {\n const result = await prsInFlight;\n return c.json(result);\n } catch (err) {\n // gh missing or unauthenticated — surface empty rather than 500;\n // the client renders a \"gh not available\" hint.\n return c.json({\n prs: [],\n error: (err as Error).message,\n available: false,\n });\n }\n });\n\n // -- Jira --------------------------------------------------------------\n //\n // Single `acli jira auth status` probe (combined with the issue search\n // inside fetchJiraPane) — replaces the previous two-call pattern. Also\n // dedups concurrent refreshes; `acli` can be slow.\n let jiraInFlight: Promise<{ available: boolean; issues: JiraIssue[] }> | null =\n null;\n app.get('/api/jira', async (c) => {\n if (!jiraInFlight) {\n jiraInFlight = (async () => {\n try {\n return await fetchJiraPane();\n } finally {\n jiraInFlight = null;\n }\n })();\n }\n try {\n const result = await jiraInFlight;\n return c.json(result);\n } catch (err) {\n return c.json({\n issues: [],\n available: false,\n error: (err as Error).message,\n });\n }\n });\n\n // -- Tasks -------------------------------------------------------------\n //\n // File-watched on the server side via the existing `tasks-changed`\n // broadcast (added below). Mutations both write through the\n // `core/tasks.ts` API and broadcast so other tabs refresh.\n\n app.get('/api/tasks', (c) => c.json({ tasks: getTasks() }));\n\n const newTaskSchema = z.object({\n text: z.string().min(1),\n link: z.string().optional(),\n });\n app.post(\n '/api/tasks',\n zValidator('json', newTaskSchema),\n async (c) => {\n const { text, link } = c.req.valid('json');\n const task = await addTask(text, link);\n opts.broadcast('tasks-changed', { id: task.id });\n return c.json({ task, tasks: getTasks() });\n },\n );\n\n const editSchema = z.object({\n text: z.string().min(1).optional(),\n done: z.boolean().optional(),\n });\n app.patch(\n '/api/tasks/:id',\n zValidator('json', editSchema),\n async (c) => {\n const id = Number(c.req.param('id'));\n if (!Number.isFinite(id)) {\n return c.json({ error: 'invalid id' }, 400);\n }\n const body = c.req.valid('json');\n let updated = null;\n if (typeof body.text === 'string') {\n updated = await editTask(id, body.text);\n }\n if (typeof body.done === 'boolean') {\n updated = body.done\n ? await completeTask(id)\n : await uncompleteTask(id);\n }\n if (!updated) return c.json({ error: 'not found' }, 404);\n opts.broadcast('tasks-changed', { id });\n return c.json({ task: updated, tasks: getTasks() });\n },\n );\n\n app.delete('/api/tasks/:id', async (c) => {\n const id = Number(c.req.param('id'));\n if (!Number.isFinite(id)) return c.json({ error: 'invalid id' }, 400);\n const removed = await removeTask(id);\n if (!removed) return c.json({ error: 'not found' }, 404);\n opts.broadcast('tasks-changed', { id });\n return c.json({ tasks: getTasks() });\n });\n}\n","import path from 'node:path';\nimport { spawn } from 'node:child_process';\nimport { Hono } from 'hono';\nimport { zValidator } from '@hono/zod-validator';\nimport { z } from 'zod';\nimport { loadConfig } from './config.js';\nimport { setupWorktree, teardownWorktree } from './worktree.js';\nimport { removeSession } from './history.js';\nimport { findSession, sessionIdFor } from './web-state.js';\nimport { git } from './git.js';\nimport { detectParentBranch } from './diff-scope.js';\n\nexport interface WorktreeMutOptions {\n broadcast: (event: string, data: unknown) => void;\n}\n\n/**\n * Hono sub-app exposing the worktree mutation surface that the dashboard\n * needs to reach parity with `work dash`:\n *\n * POST /api/worktrees — create (target + branch [+ base])\n * DELETE /api/sessions/:id/worktree — remove (with force flag)\n * POST /api/sessions/:id/sync — git fetch (+ pull where safe)\n * POST /api/sessions/:id/rebase — rebase on detected/recorded parent\n * POST /api/sessions/:id/open-editor — spawn `code <path>`\n *\n * All mutations broadcast `sessions-changed` so the SPA refetches and\n * the sidebar updates without a manual refresh.\n */\nexport function mountWorktreeRoutes(\n app: Hono,\n opts: WorktreeMutOptions,\n): void {\n // -- Create ------------------------------------------------------------\n const createSchema = z.object({\n target: z.string().min(1),\n branch: z.string().min(1),\n base: z.string().optional(),\n jiraKey: z.string().optional(),\n });\n app.post(\n '/api/worktrees',\n zValidator('json', createSchema),\n async (c) => {\n const { target, branch, base, jiraKey } = c.req.valid('json');\n const config = loadConfig();\n if (!config) return c.json({ error: 'no config' }, 400);\n\n try {\n const result = await setupWorktree(\n target,\n branch,\n config,\n base,\n jiraKey,\n );\n if (!result) {\n return c.json({ error: 'setup failed (target not found?)' }, 400);\n }\n opts.broadcast('sessions-changed', { ts: Date.now() });\n // Re-derive the new session id so the client can route to it\n // immediately. sessionIdFor takes a WorktreeSession, but we\n // have the same inputs — hash sha1(target+':'+branch).\n const id = sessionIdFor({\n target,\n isGroup: result.isGroup,\n branch,\n paths: result.paths,\n createdAt: '',\n lastAccessedAt: '',\n });\n return c.json({\n sessionId: id,\n launchDir: result.launchDir,\n paths: result.paths,\n });\n } catch (err) {\n return c.json({ error: (err as Error).message }, 500);\n }\n },\n );\n\n // -- Remove ------------------------------------------------------------\n const removeSchema = z.object({ force: z.boolean().optional() });\n app.delete(\n '/api/sessions/:id/worktree',\n zValidator('json', removeSchema),\n async (c) => {\n const id = c.req.param('id');\n const session = findSession(id);\n if (!session) return c.json({ error: 'unknown session' }, 404);\n const config = loadConfig();\n if (!config) return c.json({ error: 'no config' }, 400);\n\n const { force } = c.req.valid('json');\n try {\n const ok = teardownWorktree(\n session.target,\n session.isGroup,\n session.branch,\n config,\n force ?? false,\n );\n if (!ok) {\n return c.json(\n {\n error:\n 'remove blocked (uncommitted changes — pass force:true to override)',\n },\n 409,\n );\n }\n await removeSession(session.target, session.branch);\n opts.broadcast('sessions-changed', { ts: Date.now() });\n return c.json({ ok: true });\n } catch (err) {\n return c.json({ error: (err as Error).message }, 500);\n }\n },\n );\n\n // -- Sync (fetch + try to pull) ---------------------------------------\n app.post('/api/sessions/:id/sync', (c) => {\n const id = c.req.param('id');\n const session = findSession(id);\n if (!session) return c.json({ error: 'unknown session' }, 404);\n const results = session.paths.map((p) => {\n const fetch = git(['fetch', '--all', '--prune', '--quiet'], p);\n const pull = git(['pull', '--ff-only', '--quiet'], p);\n return {\n path: p,\n fetched: fetch.exitCode === 0,\n pulled: pull.exitCode === 0,\n pullError: pull.exitCode === 0 ? undefined : pull.stderr.trim(),\n };\n });\n opts.broadcast('sessions-changed', { ts: Date.now() });\n return c.json({ results });\n });\n\n // -- Rebase on parent --------------------------------------------------\n app.post('/api/sessions/:id/rebase', (c) => {\n const id = c.req.param('id');\n const session = findSession(id);\n if (!session) return c.json({ error: 'unknown session' }, 404);\n const results = session.paths.map((p) => {\n const parent = session.baseBranch ?? detectParentBranch(p);\n if (!parent) {\n return { path: p, ok: false, error: 'no parent branch detected' };\n }\n const r = git(['rebase', parent], p);\n return {\n path: p,\n ok: r.exitCode === 0,\n parent,\n error: r.exitCode === 0 ? undefined : r.stderr.trim(),\n };\n });\n opts.broadcast('sessions-changed', { ts: Date.now() });\n return c.json({ results });\n });\n\n // -- Open in editor ----------------------------------------------------\n app.post('/api/sessions/:id/open-editor', (c) => {\n const id = c.req.param('id');\n const session = findSession(id);\n if (!session) return c.json({ error: 'unknown session' }, 404);\n // Open the worktree (or group root) in VS Code. Detached + ignored\n // stdio so the spawn returns immediately and the parent doesn't\n // hold on to a zombie.\n const target = session.isGroup\n ? path.dirname(session.paths[0])\n : session.paths[0];\n try {\n // Resolve `code` vs `code.cmd` by platform instead of using\n // shell:true. shell:true routes through cmd.exe / sh -c, which\n // means any shell metacharacters in `target` (an `&`, a backtick,\n // an unescaped quote) would execute — a TOCTOU risk if anything\n // ever writes a malformed path into history.json.\n const cmd = process.platform === 'win32' ? 'code.cmd' : 'code';\n const child = spawn(cmd, [target], {\n detached: true,\n stdio: 'ignore',\n shell: false,\n });\n child.unref();\n return c.json({ ok: true, opened: target });\n } catch (err) {\n return c.json({ error: (err as Error).message }, 500);\n }\n });\n}\n","import { Hono } from 'hono';\nimport { zValidator } from '@hono/zod-validator';\nimport { z } from 'zod';\nimport { EventEmitter } from 'node:events';\nimport path from 'node:path';\nimport spawn from 'cross-spawn';\nimport { computeDiff, computeRangeDiff } from './diff-pipeline.js';\nimport { resolveRepoDiff } from './diff-scope.js';\nimport {\n clearCheckpoints,\n loadManifest,\n takeCheckpoint,\n type CheckpointEntry,\n} from './checkpoint.js';\nimport {\n commentStoreIdForScope,\n getScope,\n listScopes,\n markScopeEnded,\n registerScope,\n removeScope,\n ScopePathRejectedError,\n subscribeScope,\n} from './scope-manager.js';\nimport { getCommentFileStore } from './comment-file-store.js';\nimport { commentInputSchema, submitReviewSchema } from './comment-schemas.js';\nimport { streamSSE } from 'hono/streaming';\n\nexport interface ScopeMountOptions {\n /** Server-level broadcast. Scope events go here too. */\n broadcast: (event: string, data: unknown) => void;\n}\n\n\n/**\n * Hono sub-app exposing the per-scope diff + review surface that lets\n * `wd` and `wd -c` consolidate onto the `work web` server.\n *\n * Routes:\n * POST /api/scopes — register a scope; returns\n * `{ hash, diffUrl, reviewUrl }`\n * GET /api/scopes — list active scopes\n * DELETE /api/scopes/:hash — deregister\n * GET /api/scopes/:hash/diff?base=… — diff data (same shape as\n * session diff)\n * GET /api/scopes/:hash/comments — list comments\n * POST /api/scopes/:hash/comments — add (zod-validated)\n * DELETE /api/scopes/:hash/comments/:cid — remove\n * POST /api/scopes/:hash/submit-review — promote drafts\n * POST /api/scopes/:hash/discard-review — drop drafts\n * GET /api/scopes/:hash/events — SSE: diff-changed,\n * comments-changed\n */\nexport function mountScopeRoutes(app: Hono, opts: ScopeMountOptions): void {\n // -- Lifecycle -----------------------------------------------------------\n\n // Scopes that already have an auto-snapshot subscriber wired. The\n // register endpoint is idempotent (same paths → same hash), and we\n // only want one subscriber per scope no matter how many times `wd`\n // re-registers in the same `work web` lifetime.\n const checkpointWatched = new Set<string>();\n\n // Per-scope fingerprint of the last working-tree state we observed.\n // `git status --porcelain --no-renames -z` is dramatically cheaper\n // than the full snapshot path (`git add -A` against a temp index +\n // write-tree + commit-tree + update-ref ≈ 6 spawns per repo) — when\n // a save touches an `.gitignore`'d file or doesn't actually change\n // content (editor rewriting the same bytes), the status output is\n // unchanged and we can skip the whole snapshot pipeline.\n const lastStatus = new Map<string, string>();\n\n function workingTreeFingerprint(paths: string[]): string {\n const parts: string[] = [];\n for (const p of paths) {\n const r = spawn.sync(\n 'git',\n ['status', '--porcelain', '--no-renames', '-z'],\n { cwd: p, encoding: 'utf-8', windowsHide: true },\n );\n parts.push(r.stdout ?? '');\n }\n return parts.join('\\0|\\0');\n }\n\n // In-process bus for checkpoint events — lets the per-scope SSE handler\n // forward `checkpoints-changed` to subscribers of a single scope's\n // stream. Server-level `/events` still gets the same event via\n // `opts.broadcast`; both paths are needed because the SPA chooses one\n // stream or the other depending on whether it's mounted via `work web`\n // or running standalone.\n const scopeBus = new EventEmitter();\n scopeBus.setMaxListeners(0);\n\n /** Build the repo list for `takeCheckpoint`. The `name` field is the\n * manifest key — must be unique within a scope, otherwise two repos\n * with the same basename in a group worktree would overwrite each\n * other's SHA in the entry. Using the full resolved path guarantees\n * uniqueness (paths in a scope are already de-duplicated by\n * `registerScope`'s sort+normalise). */\n function scopeRepos(scopePaths: string[]) {\n return scopePaths.map((p) => ({ name: p, root: p }));\n }\n\n app.post(\n '/api/scopes',\n zValidator(\n 'json',\n z.object({\n paths: z.array(z.string().min(1)).min(1),\n label: z.string().optional(),\n }),\n ),\n (c) => {\n const { paths, label } = c.req.valid('json');\n try {\n const scope = registerScope(paths, label);\n opts.broadcast('scopes-changed', { hash: scope.hash });\n\n const announceCheckpoint = (entry: CheckpointEntry) => {\n const payload = { scopeHash: scope.hash, id: entry.id };\n opts.broadcast('checkpoints-changed', payload);\n scopeBus.emit('checkpoints-changed', payload);\n };\n\n // Initial snapshot — establishes the \"Initial\" checkpoint that\n // every subsequent fs-change snapshot diffs against. Fire and\n // forget: `git add -A` against a large untracked tree can take\n // hundreds of ms, and the `wd` foreground caller is waiting on\n // this response to open the browser. The SPA fetches checkpoints\n // separately and will see the initial entry once it lands (or\n // via the `checkpoints-changed` SSE event below).\n if (loadManifest(scope.hash).entries.length === 0) {\n takeCheckpoint(scope.hash, scopeRepos(scope.paths))\n .then((entry) => {\n if (entry) announceCheckpoint(entry);\n })\n .catch((err) => {\n // Best-effort, but DO surface persistent failures (disk\n // full on `~/.work/diffs/`, stale lockfile, git permission\n // denied). `installConsoleLogger` mirrors console.error\n // into `~/.work/debug.log`, giving the user a diagnostic\n // trail even though we don't fail the request.\n console.error('[checkpoint] initial snapshot failed:', err);\n });\n }\n\n // Wire an auto-snapshot subscriber that fires on every debounced\n // fs change for this scope. A cheap `git status` fingerprint\n // gate short-circuits before the expensive snapshot pipeline\n // when the working tree hasn't moved — common when editor\n // autosave rewrites a file with identical content, or when\n // chokidar fires for an `.gitignore`'d path that git wouldn't\n // capture anyway.\n if (!checkpointWatched.has(scope.hash)) {\n checkpointWatched.add(scope.hash);\n subscribeScope(scope.hash, () => {\n try {\n const fp = workingTreeFingerprint(scope.paths);\n if (lastStatus.get(scope.hash) === fp) return;\n lastStatus.set(scope.hash, fp);\n } catch {\n // Fingerprint failure shouldn't block the snapshot — fall\n // through to takeCheckpoint and let its own logic decide.\n }\n takeCheckpoint(scope.hash, scopeRepos(scope.paths))\n .then((entry) => {\n if (entry) announceCheckpoint(entry);\n })\n .catch((err) => {\n console.error('[checkpoint] auto-snapshot failed:', err);\n });\n });\n }\n\n return c.json({\n hash: scope.hash,\n diffUrl: `/diff/${scope.hash}`,\n reviewUrl: `/review/${scope.hash}`,\n });\n } catch (err) {\n if (err instanceof ScopePathRejectedError) {\n return c.json({ error: err.message, rejected: err.rejected }, 403);\n }\n throw err;\n }\n },\n );\n\n app.get('/api/scopes', (c) => c.json({ scopes: listScopes() }));\n\n app.delete('/api/scopes/:hash', (c) => {\n const hash = c.req.param('hash');\n // Capture the paths BEFORE removeScope — the scope-manager entry\n // is gone after that, and clearCheckpoints needs the repo roots\n // to delete each `refs/wd/<hash>/<n>` ref. Without this cleanup,\n // refs + the manifest leak forever: git GC can't reclaim the\n // commits behind a named ref, and a re-register with the same\n // paths would skip the Initial snapshot because the stale\n // manifest still has entries.\n const scope = getScope(hash);\n const repoPaths = scope ? [...scope.paths] : [];\n const ok = removeScope(hash);\n // `removeScope` clears the scope's fs-watch subscribers; drop our\n // record of having wired one so a re-register of the same paths\n // rewires the auto-snapshot subscriber instead of silently doing\n // nothing.\n checkpointWatched.delete(hash);\n lastStatus.delete(hash);\n if (repoPaths.length > 0) clearCheckpoints(hash, repoPaths);\n if (ok) opts.broadcast('scopes-changed', { hash });\n return c.json({ ok });\n });\n\n // -- Diff ----------------------------------------------------------------\n\n /** Parse \"0\", \"1\", ... as a numeric id; everything else as undefined.\n * The literal \"working\" stays as the sentinel — only meaningful for\n * the `to` parameter. */\n function parseCheckpointParam(\n raw: string | undefined,\n ): number | 'working' | undefined {\n if (raw === undefined || raw === '') return undefined;\n if (raw === 'working') return 'working';\n const n = Number(raw);\n if (Number.isInteger(n) && n >= 0) return n;\n return undefined;\n }\n\n app.get('/api/scopes/:hash/diff', (c) => {\n const scope = getScope(c.req.param('hash'));\n if (!scope) return c.json({ error: 'unknown scope' }, 404);\n const base = c.req.query('base') === 'branch' ? 'branch' : 'uncommitted';\n const fromParam = parseCheckpointParam(c.req.query('from'));\n const toParam = parseCheckpointParam(c.req.query('to'));\n try {\n // `from='working'` is meaningless — the working tree only makes\n // sense as the right endpoint. Reject explicitly instead of\n // silently falling through to legacy mode and serving a HEAD-vs-\n // working diff that looks like the requested range.\n if (fromParam === 'working') {\n return c.json(\n { error: \"'working' is not valid as a from-checkpoint\" },\n 400,\n );\n }\n // Checkpoint-range mode: ignore `base`, look up commits from the\n // manifest, run computeRangeDiff per repo. Either endpoint can be\n // an id or (for `to`) 'working'. Missing checkpoint id for a repo\n // → fall back to HEAD (treats that repo as if there's no snapshot).\n if (fromParam !== undefined) {\n const manifest = loadManifest(scope.hash);\n const fromEntry = manifest.entries.find((e) => e.id === fromParam);\n if (!fromEntry) {\n return c.json({ error: `unknown from checkpoint ${fromParam}` }, 400);\n }\n let toEntry: CheckpointEntry | undefined;\n if (toParam !== undefined && toParam !== 'working') {\n toEntry = manifest.entries.find((e) => e.id === toParam);\n if (!toEntry) {\n return c.json({ error: `unknown to checkpoint ${toParam}` }, 400);\n }\n // Reject reversed ranges. `git diff <toSha> <fromSha>` produces\n // an inverted diff (adds look like deletes) — clearly wrong but\n // would still return 200. Surface the misuse so callers (the\n // SPA or curl users) get a clear error instead of confusing\n // output.\n if (toEntry.id < fromEntry.id) {\n return c.json(\n { error: `to (${toEntry.id}) must be >= from (${fromEntry.id})` },\n 400,\n );\n }\n }\n const repos = scope.paths.map((p) => {\n // Manifest is keyed by full path (see `scopeRepos`). The\n // response's `name` is still the basename — it's the user-\n // visible repo tab label and doesn't need to be unique-by-key.\n const fromSha = fromEntry.repos[p] ?? 'HEAD';\n const toSha: string | 'working' =\n toEntry === undefined ? 'working' : (toEntry.repos[p] ?? 'HEAD');\n return {\n name: path.basename(p),\n root: p,\n files: computeRangeDiff({ root: p, fromRef: fromSha, toRef: toSha }),\n };\n });\n return c.json({\n scopeHash: scope.hash,\n base: 'uncommitted',\n resolvedBase: `checkpoint-${fromEntry.id}`,\n from: fromEntry.id,\n to: toEntry?.id ?? 'working',\n repos,\n });\n }\n\n // Resolve each repo independently — they may have different parent\n // branches in a group worktree. Each entry carries its own\n // `resolvedBase` so the UI can label per-repo if it wants to.\n const resolved = scope.paths.map((p) => resolveRepoDiff(p, base));\n const repos = scope.paths.map((p, i) => ({\n name: path.basename(p),\n root: p,\n resolvedBase: resolved[i].resolvedBase,\n files: computeDiff({ root: p, diffArg: resolved[i].diffArg }),\n }));\n // Top-level `resolvedBase` is the primary repo's value — used for\n // the single-line \"vs X\" badge in the sidebar header. Mirrors what\n // session-diff returns from web-server.ts.\n const resolvedBase = resolved[0]?.resolvedBase ?? 'HEAD';\n return c.json({ scopeHash: scope.hash, base, resolvedBase, repos });\n } catch (err) {\n return c.json({ error: (err as Error).message }, 500);\n }\n });\n\n // -- Checkpoints ---------------------------------------------------------\n\n app.get('/api/scopes/:hash/checkpoints', (c) => {\n const scope = getScope(c.req.param('hash'));\n if (!scope) return c.json({ error: 'unknown scope' }, 404);\n const manifest = loadManifest(scope.hash);\n return c.json({\n scopeHash: scope.hash,\n entries: manifest.entries,\n });\n });\n\n // -- Comments ------------------------------------------------------------\n\n function commentStore(hash: string) {\n return getCommentFileStore(commentStoreIdForScope(hash));\n }\n\n app.get('/api/scopes/:hash/comments', (c) => {\n const scope = getScope(c.req.param('hash'));\n if (!scope) return c.json({ error: 'unknown scope' }, 404);\n return c.json({\n comments: commentStore(scope.hash).snapshot(),\n ended: scope.ended,\n });\n });\n\n app.post(\n '/api/scopes/:hash/comments',\n zValidator('json', commentInputSchema),\n (c) => {\n const scope = getScope(c.req.param('hash'));\n if (!scope) return c.json({ error: 'unknown scope' }, 404);\n const store = commentStore(scope.hash);\n try {\n const comment = store.post(c.req.valid('json'));\n opts.broadcast('comments-changed', {\n scopeHash: scope.hash,\n id: comment.id,\n });\n return c.json({ comment, comments: store.snapshot() });\n } catch (err) {\n return c.json({ error: (err as Error).message }, 400);\n }\n },\n );\n\n app.delete('/api/scopes/:hash/comments/:cid', (c) => {\n const scope = getScope(c.req.param('hash'));\n if (!scope) return c.json({ error: 'unknown scope' }, 404);\n const store = commentStore(scope.hash);\n const removed = store.remove(c.req.param('cid'));\n if (removed) {\n opts.broadcast('comments-changed', {\n scopeHash: scope.hash,\n deleted: c.req.param('cid'),\n });\n }\n return c.json({ comments: store.snapshot() });\n });\n\n app.post(\n '/api/scopes/:hash/submit-review',\n zValidator('json', submitReviewSchema),\n (c) => {\n const scope = getScope(c.req.param('hash'));\n if (!scope) return c.json({ error: 'unknown scope' }, 404);\n const store = commentStore(scope.hash);\n const result = store.submit(c.req.valid('json').summary);\n opts.broadcast('comments-changed', {\n scopeHash: scope.hash,\n submittedCount: result.drafts.length,\n });\n return c.json({\n count: result.drafts.length,\n comments: store.snapshot(),\n });\n },\n );\n\n app.post('/api/scopes/:hash/discard-review', (c) => {\n const scope = getScope(c.req.param('hash'));\n if (!scope) return c.json({ error: 'unknown scope' }, 404);\n const store = commentStore(scope.hash);\n const discarded = store.discardDrafts();\n if (discarded > 0) {\n opts.broadcast('comments-changed', { scopeHash: scope.hash });\n }\n return c.json({ discarded, comments: store.snapshot() });\n });\n\n // End Review button hits this. Sets the scope's `ended` flag so the\n // `wd -c` CLI proxy can emit `--- review done ---` and exit on the\n // next poll. Scope itself stays alive — the browser tab keeps\n // working, reloading the URL keeps working.\n app.post('/api/scopes/:hash/done', (c) => {\n const scope = getScope(c.req.param('hash'));\n if (!scope) return c.json({ error: 'unknown scope' }, 404);\n const store = commentStore(scope.hash);\n const count = store.list().length;\n markScopeEnded(scope.hash);\n opts.broadcast('review-done', { scopeHash: scope.hash, count });\n return c.json({ ok: true, count });\n });\n\n // -- Per-scope SSE -------------------------------------------------------\n //\n // The shared /events stream already broadcasts global events; this\n // endpoint adds a scope-narrowed stream so the `wd -c` CLI can tail\n // *just* its scope's comments without filtering at the client.\n\n app.get('/api/scopes/:hash/events', (c) => {\n const scope = getScope(c.req.param('hash'));\n if (!scope) return c.json({ error: 'unknown scope' }, 404);\n return streamSSE(c, async (stream) => {\n const unsubscribe = subscribeScope(scope.hash, () => {\n stream\n .writeSSE({\n event: 'diff-changed',\n data: JSON.stringify({ scopeHash: scope.hash }),\n })\n .catch(() => { /* */ });\n });\n // Relay checkpoint events for THIS scope only. The auto-snapshot\n // subscriber emits to `scopeBus` whenever a new checkpoint passes\n // the dedup, so the SPA's strip refreshes without polling.\n const onCheckpoint = (payload: { scopeHash: string; id: number }) => {\n if (payload.scopeHash !== scope.hash) return;\n stream\n .writeSSE({\n event: 'checkpoints-changed',\n data: JSON.stringify(payload),\n })\n .catch(() => { /* */ });\n };\n scopeBus.on('checkpoints-changed', onCheckpoint);\n await stream.writeSSE({ event: 'connected', data: '' });\n await new Promise<void>((resolve) => {\n stream.onAbort(() => {\n unsubscribe?.();\n scopeBus.off('checkpoints-changed', onCheckpoint);\n resolve();\n });\n });\n });\n });\n}\n","/**\n * Per-scope diff checkpoints.\n *\n * A \"checkpoint\" is a snapshot of every repo in a scope's working tree\n * (including untracked files) captured as a real git commit and held alive\n * by `refs/wd/<scope-hash>/<n>` in each repo. A small JSON manifest at\n * `~/.work/diffs/<scope-hash>.checkpoints.json` records the per-checkpoint\n * timestamp + repo→sha mapping so the SPA can list checkpoints and ask for\n * a diff between any two (or between one and the live working tree).\n *\n * Snapshots are taken automatically:\n * - once when a scope is first registered (the \"Initial\" point)\n * - again whenever the scope's fs-watch debounce fires AND the working\n * tree differs from the previous snapshot (the dedup keeps idle saves\n * from spawning empty checkpoints)\n *\n * The capture mechanism uses a temp git index file so the real index is\n * never disturbed:\n *\n * GIT_INDEX_FILE=<tmp> git read-tree HEAD\n * GIT_INDEX_FILE=<tmp> git add -A\n * GIT_INDEX_FILE=<tmp> git write-tree → <tree-sha>\n * git commit-tree <tree-sha> -p HEAD -m \"wd checkpoint\" → <commit-sha>\n * git update-ref refs/wd/<hash>/<n> <commit-sha>\n *\n * Untracked files end up in the snapshot because `git add -A` against the\n * temp index promotes them. `.gitignore` is honoured (same as the diff\n * pipeline's untracked detection).\n */\n\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport crypto from 'node:crypto';\nimport spawn from 'cross-spawn';\nimport { atomicWriteFile, ensureFile, withFileLock } from './fs-safe.js';\n\nexport interface CheckpointEntry {\n /** Monotonic per-scope sequence id, starting at 0 for the initial\n * snapshot. */\n id: number;\n /** ISO timestamp of capture. */\n ts: string;\n /** Human label — \"Initial\" for id 0, otherwise empty (the SPA shows\n * \"#1\", \"#2\", ...). Reserved for future user-labelled checkpoints. */\n label?: string;\n /** Per-repo commit sha captured. Keyed by repo name (the same `name`\n * used in `RepoData`). When a repo has no head yet (fresh repo, no\n * commits), the value is null — diff ranges that touch it on the\n * \"from\" side fall back to the empty tree. */\n repos: Record<string, string | null>;\n}\n\nexport interface CheckpointManifest {\n version: 1;\n scopeHash: string;\n entries: CheckpointEntry[];\n}\n\n/** Where this scope's manifest lives on disk. */\nexport function manifestPath(scopeHash: string): string {\n const dir = path.join(os.homedir(), '.work', 'diffs');\n fs.mkdirSync(dir, { recursive: true });\n return path.join(dir, `${scopeHash}.checkpoints.json`);\n}\n\nfunction emptyManifest(scopeHash: string): CheckpointManifest {\n return { version: 1, scopeHash, entries: [] };\n}\n\n/** Read manifest from disk (returns an empty one if the file is missing\n * or unparseable). Never throws. */\nexport function loadManifest(scopeHash: string): CheckpointManifest {\n const file = manifestPath(scopeHash);\n if (!fs.existsSync(file)) return emptyManifest(scopeHash);\n try {\n const raw = fs.readFileSync(file, 'utf-8');\n const parsed = JSON.parse(raw) as Partial<CheckpointManifest>;\n if (parsed.version !== 1 || !Array.isArray(parsed.entries)) {\n return emptyManifest(scopeHash);\n }\n return {\n version: 1,\n scopeHash,\n entries: parsed.entries as CheckpointEntry[],\n };\n } catch {\n return emptyManifest(scopeHash);\n }\n}\n\n// (Previously a separate `appendCheckpoint` lived here. It was folded\n// into `takeCheckpoint` so id assignment + snapshot + ref write + manifest\n// append all happen under a single lock — concurrent fs-watch fires across\n// the same scope must not race on `nextId`.)\n\n/**\n * Capture the working tree (incl. untracked, respecting .gitignore) of a\n * single repo as a commit and return its sha. Updates\n * `refs/wd/<scopeHash>/<id>` to point at the commit so git GC can't\n * reclaim it. Returns null on failure (caller should skip this repo).\n *\n * Internally uses a temp `GIT_INDEX_FILE` so the user's real index is\n * untouched. The temp file is unlinked on success and on most failures.\n */\nexport function snapshotRepo(\n repoRoot: string,\n scopeHash: string,\n id: number,\n): string | null {\n const tmpIndex = path.join(\n os.tmpdir(),\n `wd-cp-${process.pid}-${crypto.randomBytes(6).toString('hex')}.idx`,\n );\n\n const env: NodeJS.ProcessEnv = { ...process.env, GIT_INDEX_FILE: tmpIndex };\n const run = (args: string[]) =>\n spawn.sync('git', args, {\n cwd: repoRoot,\n encoding: 'utf-8',\n env,\n windowsHide: true,\n maxBuffer: 64 * 1024 * 1024,\n });\n\n try {\n // Detect whether HEAD exists. Fresh repos with no commits can still\n // be snapshotted — we just skip the read-tree + parent below.\n const headSha = (\n spawn.sync('git', ['rev-parse', '--verify', 'HEAD'], {\n cwd: repoRoot,\n encoding: 'utf-8',\n windowsHide: true,\n }).stdout ?? ''\n ).trim();\n const hasHead = headSha.length > 0;\n\n if (hasHead) {\n const r = run(['read-tree', 'HEAD']);\n if (r.status !== 0) return null;\n }\n\n // Stage every working-tree change INCLUDING untracked. `-A` against\n // a temp index that was either empty or seeded from HEAD captures\n // the full working-tree state (subject to .gitignore).\n const add = run(['add', '-A']);\n if (add.status !== 0) return null;\n\n const wt = run(['write-tree']);\n if (wt.status !== 0 || !wt.stdout) return null;\n const treeSha = wt.stdout.trim();\n\n // commit-tree doesn't need the temp index. We DO want it to be\n // deterministic where possible — use a fixed author/committer so two\n // snapshots of identical content produce the same commit sha (the\n // tree sha is already identical; matching commit shas help dedup).\n // Constant message + deterministic author/committer date below — so\n // two snapshots of identical content produce the SAME commit sha. The\n // dedup path in `takeCheckpoint` relies on this: when nothing changed\n // between captures, every repo's recomputed sha matches the previous\n // entry and the new manifest row is discarded. (The id isn't in the\n // message; it lives in the ref name + manifest entry.)\n const commitArgs = ['commit-tree', treeSha, '-m', 'wd checkpoint'];\n if (hasHead) commitArgs.push('-p', headSha);\n const commitEnv = {\n ...process.env,\n GIT_AUTHOR_NAME: 'wd',\n GIT_AUTHOR_EMAIL: 'wd@local',\n GIT_AUTHOR_DATE: '2000-01-01T00:00:00Z',\n GIT_COMMITTER_NAME: 'wd',\n GIT_COMMITTER_EMAIL: 'wd@local',\n GIT_COMMITTER_DATE: '2000-01-01T00:00:00Z',\n };\n const commit = spawn.sync('git', commitArgs, {\n cwd: repoRoot,\n encoding: 'utf-8',\n env: commitEnv,\n windowsHide: true,\n });\n if (commit.status !== 0 || !commit.stdout) return null;\n const commitSha = commit.stdout.trim();\n\n const refName = `refs/wd/${scopeHash}/${id}`;\n const updateRef = spawn.sync(\n 'git',\n ['update-ref', refName, commitSha],\n { cwd: repoRoot, encoding: 'utf-8', windowsHide: true },\n );\n if (updateRef.status !== 0) return null;\n\n return commitSha;\n } finally {\n try {\n if (fs.existsSync(tmpIndex)) fs.unlinkSync(tmpIndex);\n } catch {\n // Temp-file leftover isn't fatal — os will clean it eventually.\n }\n }\n}\n\nexport interface ScopeRepo {\n name: string;\n root: string;\n}\n\n/**\n * Take one checkpoint across every repo in a scope. Returns the appended\n * entry, or null when the snapshot is identical to the previous entry's\n * (every repo's commit sha matches — i.e. nothing changed). The caller\n * uses the null return to skip the SSE broadcast.\n *\n * When this is the first checkpoint for the scope, `label` defaults to\n * \"Initial\" so the SPA can render it specially.\n */\nexport async function takeCheckpoint(\n scopeHash: string,\n repos: ScopeRepo[],\n opts: { force?: boolean; label?: string } = {},\n): Promise<CheckpointEntry | null> {\n // Entire flow runs under one file lock so concurrent fs-watch fires\n // (or a register-handler racing with a debounced auto-snapshot) can't\n // assign duplicate ids or interleave manifest writes. Snapshot work\n // itself is git-side-effect-safe to concurrent — git's index/ref\n // locking handles same-repo overlap, and our temp `GIT_INDEX_FILE`\n // means the user's real index is untouched either way. We only need\n // the file lock to serialise the *id assignment + ref write + append*\n // triple, which has to be atomic across processes.\n const file = manifestPath(scopeHash);\n ensureFile(file, JSON.stringify(emptyManifest(scopeHash), null, 2));\n return withFileLock(file, () => {\n const manifest = loadManifest(scopeHash);\n const isFirst = manifest.entries.length === 0;\n const nextId = isFirst\n ? 0\n : manifest.entries[manifest.entries.length - 1].id + 1;\n\n const captured: Record<string, string | null> = {};\n for (const repo of repos) {\n captured[repo.name] = snapshotRepo(repo.root, scopeHash, nextId);\n }\n\n // Delete `refs/wd/<hash>/<id>` in every listed repo. Used by both\n // the partial-failure bailout and the dedup-cancel path —\n // `update-ref -d` is a no-op on a missing ref, so the same helper\n // covers \"rollback what we wrote\" and \"rollback what we might\n // have written\" without needing per-repo state.\n const rollbackRefs = () => {\n const refName = `refs/wd/${scopeHash}/${nextId}`;\n for (const repo of repos) {\n spawn.sync('git', ['update-ref', '-d', refName], {\n cwd: repo.root,\n encoding: 'utf-8',\n windowsHide: true,\n });\n }\n };\n\n // If any repo snapshot failed (returned null), bail — a partial\n // entry would let range diffs against this id silently produce\n // empty diffs for the failed repos (via the `?? 'HEAD'` fallback\n // in scope-routes) instead of surfacing the problem. The next\n // fs-event will retry.\n if (repos.some((r) => captured[r.name] === null)) {\n rollbackRefs();\n return null;\n }\n\n // Dedup: when not forced, compare against the previous entry. If every\n // repo's sha matches (and there's at least one), skip — nothing changed.\n if (!opts.force && !isFirst) {\n const prev = manifest.entries[manifest.entries.length - 1];\n const allMatch = Object.keys(captured).every(\n (k) => captured[k] === prev.repos[k],\n );\n if (allMatch) {\n rollbackRefs();\n return null;\n }\n }\n\n const entry: CheckpointEntry = {\n id: nextId,\n ts: new Date().toISOString(),\n repos: captured,\n };\n const label = opts.label ?? (isFirst ? 'Initial' : undefined);\n if (label) entry.label = label;\n manifest.entries.push(entry);\n atomicWriteFile(file, JSON.stringify(manifest, null, 2));\n return entry;\n });\n}\n\n/** Remove the manifest + every ref for this scope. Called when a scope\n * is explicitly torn down. Best-effort — partial failure leaves orphaned\n * refs but doesn't otherwise corrupt state. */\nexport function clearCheckpoints(\n scopeHash: string,\n repoRoots: string[],\n): void {\n const manifest = loadManifest(scopeHash);\n for (const root of repoRoots) {\n for (const entry of manifest.entries) {\n const refName = `refs/wd/${scopeHash}/${entry.id}`;\n spawn.sync('git', ['update-ref', '-d', refName], {\n cwd: root,\n encoding: 'utf-8',\n windowsHide: true,\n });\n }\n }\n const file = manifestPath(scopeHash);\n if (fs.existsSync(file)) {\n try {\n fs.unlinkSync(file);\n } catch {\n // Leave it — next register will overwrite.\n }\n }\n}\n","/**\n * Stage 1 of the migration to \"one server per machine.\"\n *\n * A scope is an ad-hoc registration from a `wd` (or `wd -c`) invocation:\n * a label and a list of repo roots. Identified by the same sha1-of-roots\n * hash that `stableDiffPath` produces, so the same directory always gets\n * the same scope id across CLI invocations.\n *\n * Scopes are in-memory only — registering one doesn't touch disk. The\n * file watcher and comment store are per-scope, lazily started on first\n * access and reused thereafter.\n *\n * This module is mounted into `work web` so multiple `wd` invocations\n * share a single server process. Eventually `wd` itself will become a\n * thin client that registers a scope and opens a URL.\n */\n\nimport path from 'node:path';\nimport crypto from 'node:crypto';\nimport { createFsWatcher, type FsWatcher } from './fs-watcher.js';\nimport { loadConfig } from './config.js';\n\nexport interface Scope {\n /** Stable hash — same as `stableDiffPath` would emit. */\n hash: string;\n /** Repo roots this scope covers. One for a single-repo worktree,\n * many for a group. */\n paths: string[];\n /** Optional human label (e.g. \"work-tree · feat/x\"). */\n label: string;\n /** When the scope was first registered. */\n createdAt: string;\n /** Set when the user clicks \"End Review\" in the browser. The scope\n * itself stays alive (browser tab still works) — this is just a\n * signal so the `wd -c` CLI proxy knows to emit\n * `--- review done ---` and exit. */\n ended: boolean;\n}\n\ninterface ScopeEntry {\n scope: Scope;\n watcher: FsWatcher | null;\n subscribers: Set<() => void>;\n}\n\nconst scopes = new Map<string, ScopeEntry>();\n\nfunction hashFor(paths: string[]): string {\n const key = paths.slice().sort().join('|');\n return crypto.createHash('sha1').update(key).digest('hex').slice(0, 12);\n}\n\n/** Thrown by registerScope when the requested paths aren't inside any\n * configured repo or under the configured worktrees root. */\nexport class ScopePathRejectedError extends Error {\n constructor(public rejected: string[]) {\n super(\n `paths not allowed: ${rejected.join(', ')} (must be inside a configured repo or worktreesRoot)`,\n );\n this.name = 'ScopePathRejectedError';\n }\n}\n\nfunction normaliseForCompare(p: string): string {\n return path.resolve(p).replace(/\\\\/g, '/').toLowerCase();\n}\n\n/**\n * Reject paths that aren't inside any configured repo or under the\n * configured `worktreesRoot`. The work web server binds to 127.0.0.1\n * only, but any local process can still POST `/api/scopes` with an\n * arbitrary filesystem path — without this check a malicious npm\n * package could cause `git` and chokidar to operate on\n * `C:\\Windows\\System32` or the user's home root.\n *\n * Returns the rejected subset (empty array when everything's allowed).\n */\nfunction rejectedPaths(normalised: string[]): string[] {\n const config = loadConfig();\n if (!config) {\n // No config means no allowlist to check against. Fall back to\n // permissive — `work init` hasn't been run yet, and rejecting\n // everything would break first-run flows.\n return [];\n }\n const allowed = [\n ...Object.values(config.repos),\n ...(config.worktreesRoot ? [config.worktreesRoot] : []),\n ].map(normaliseForCompare);\n if (allowed.length === 0) return [];\n return normalised.filter((p) => {\n const np = normaliseForCompare(p);\n return !allowed.some((a) => np === a || np.startsWith(a + '/'));\n });\n}\n\n/** Register a scope (idempotent). Returns the resolved entry — same call\n * twice with the same paths gets the same hash and the same entry.\n * Throws `ScopePathRejectedError` when any path is outside the configured\n * repos / worktrees root. */\nexport function registerScope(paths: string[], label?: string): Scope {\n const normalised = paths.map((p) => path.resolve(p));\n const rejected = rejectedPaths(normalised);\n if (rejected.length > 0) throw new ScopePathRejectedError(rejected);\n const hash = hashFor(normalised);\n const existing = scopes.get(hash);\n if (existing) {\n if (label && existing.scope.label !== label) {\n existing.scope.label = label;\n }\n return existing.scope;\n }\n const scope: Scope = {\n hash,\n paths: normalised,\n label: label ?? path.basename(normalised[0]),\n createdAt: new Date().toISOString(),\n ended: false,\n };\n scopes.set(hash, { scope, watcher: null, subscribers: new Set() });\n return scope;\n}\n\n/** Mark a scope as ended (the user clicked \"End Review\"). Idempotent.\n * Does NOT remove the scope — the URL stays viewable. */\nexport function markScopeEnded(hash: string): boolean {\n const entry = scopes.get(hash);\n if (!entry) return false;\n if (entry.scope.ended) return false;\n entry.scope.ended = true;\n return true;\n}\n\nexport function getScope(hash: string): Scope | null {\n return scopes.get(hash)?.scope ?? null;\n}\n\nexport function listScopes(): Scope[] {\n return Array.from(scopes.values()).map((e) => e.scope);\n}\n\nexport function removeScope(hash: string): boolean {\n const entry = scopes.get(hash);\n if (!entry) return false;\n entry.watcher?.stop();\n entry.subscribers.clear();\n scopes.delete(hash);\n return true;\n}\n\n/**\n * Subscribe to file-change events for a scope. Lazy-starts the\n * chokidar watcher on first subscribe; tears it down when the last\n * subscriber unsubscribes. Same lifecycle as `web-state.subscribeSession`.\n *\n * The callback fires once per debounced fs event burst.\n */\nexport function subscribeScope(\n hash: string,\n cb: () => void,\n): (() => void) | null {\n const entry = scopes.get(hash);\n if (!entry) return null;\n entry.subscribers.add(cb);\n if (!entry.watcher) {\n entry.watcher = createFsWatcher({\n roots: entry.scope.paths,\n debounceMs: 150,\n onChange: () => {\n for (const sub of entry.subscribers) {\n try { sub(); } catch { /* */ }\n }\n },\n });\n }\n return () => {\n entry.subscribers.delete(cb);\n if (entry.subscribers.size === 0 && entry.watcher) {\n entry.watcher.stop();\n entry.watcher = null;\n }\n };\n}\n\n/** Shut down every scope's watcher. Web server shutdown hook. */\nexport function disposeAllScopes(): void {\n for (const e of scopes.values()) {\n e.watcher?.stop();\n e.subscribers.clear();\n }\n scopes.clear();\n}\n\n/** Build a comment-store id for a scope. Distinguishes from session\n * ids so the two namespaces can't collide on disk. */\nexport function commentStoreIdForScope(hash: string): string {\n return `scope-${hash}`;\n}\n","import type { Hono } from 'hono';\n\n/**\n * PTY route registration. The actual WebSocket upgrade happens at the Node\n * HTTP server level via `attachTerminalUpgrade()` — Hono only owns the\n * \"is this session known?\" gating endpoint that the client hits first to\n * discover whether the terminal tab is available.\n */\nexport function mountTerminalRoutes(app: Hono): void {\n app.get('/api/sessions/:id/terminal/health', (c) => {\n return c.json({ ok: true, sessionId: c.req.param('id') });\n });\n}\n","import type { IncomingMessage } from 'node:http';\nimport type { Socket } from 'node:net';\nimport type EventEmitter from 'node:events';\nimport { WebSocketServer, type WebSocket } from 'ws';\nimport { getOrCreatePty } from './pty-pool.js';\n\nconst TERMINAL_PATH = /^\\/ws\\/sessions\\/([^/]+)\\/terminal$/;\n\n/** Minimal contract over the Node `http.Server` we need — broad enough to\n * accept both HTTP/1 and HTTP/2 servers from `@hono/node-server`. */\ntype UpgradableServer = EventEmitter;\n\n/**\n * Attach a WebSocket handler to the same Node http server Hono is running\n * on. Listens for upgrade requests at /ws/sessions/:id/terminal, attaches\n * to (or spawns) that session's PTY, and bridges traffic both ways.\n *\n * Browser → server frames are JSON:\n * { type: 'input', data: string } stdin bytes\n * { type: 'resize', cols, rows } PTY resize\n *\n * Server → browser frames are binary (PTY output, sent as utf-8 strings).\n *\n * `port` is the listening port — used for the Host-header DNS-rebinding\n * guard, mirroring the one applied to Hono routes in `diff-server.launch`.\n * The WS upgrade bypasses Hono entirely so it needs its own check.\n */\nexport function attachTerminalWs(\n httpServer: UpgradableServer,\n port: number,\n): { close: () => void } {\n const wss = new WebSocketServer({ noServer: true });\n const allowedHosts = new Set([\n `127.0.0.1:${port}`,\n `localhost:${port}`,\n ]);\n\n httpServer.on('upgrade', (req: IncomingMessage, socket: Socket, head) => {\n const host = req.headers.host;\n if (!host || !allowedHosts.has(host)) {\n socket.destroy();\n return;\n }\n const url = req.url ?? '';\n const match = url.match(TERMINAL_PATH);\n if (!match) {\n socket.destroy();\n return;\n }\n const sessionId = decodeURIComponent(match[1]);\n wss.handleUpgrade(req, socket, head, (ws) => {\n const pty = getOrCreatePty(sessionId);\n if (!pty) {\n try {\n ws.send(JSON.stringify({ type: 'error', message: 'unknown session' }));\n ws.close(1011);\n } catch { /* */ }\n return;\n }\n handleConnection(ws, pty);\n });\n });\n\n return {\n close: () => wss.close(),\n };\n}\n\nfunction handleConnection(\n ws: WebSocket,\n pty: ReturnType<typeof getOrCreatePty> & object,\n): void {\n // Replay first so the browser sees existing scrollback immediately.\n const replay = pty.replay();\n if (replay) {\n try { ws.send(replay); } catch { /* */ }\n }\n\n const unsubscribe = pty.subscribe((data) => {\n try { ws.send(data); } catch { /* client gone */ }\n });\n\n ws.on('message', (raw) => {\n let msg: unknown;\n try {\n msg = JSON.parse(raw.toString('utf-8'));\n } catch {\n return;\n }\n if (!msg || typeof msg !== 'object') return;\n const m = msg as {\n type?: string;\n data?: string;\n cols?: number;\n rows?: number;\n };\n if (m.type === 'input' && typeof m.data === 'string') {\n pty.write(m.data);\n } else if (\n m.type === 'resize' &&\n typeof m.cols === 'number' &&\n typeof m.rows === 'number'\n ) {\n pty.resize(m.cols, m.rows);\n }\n });\n\n ws.on('close', () => {\n unsubscribe();\n });\n ws.on('error', () => {\n unsubscribe();\n });\n}\n","/**\n * Installs a `command`-type entry in `~/.claude/settings.json` so Claude\n * Code spawns our hook subcommand and injects its stdout into the\n * conversation. Distinct from `HookServer` (which uses `http`-type hooks\n * for fire-and-forget notifications) — command hooks let us return text\n * that becomes part of Claude's context.\n *\n * Each install is tagged via the shared `settings-editor` (owner + PID)\n * so stale entries from a crashed previous run get pruned automatically\n * and writes are atomic.\n *\n * SECURITY NOTE: the `command` we register is resolved against the user's\n * PATH at hook-fire time (not install time). If an attacker can shadow\n * `work` earlier in PATH between install and fire, they intercept review\n * comments. We accept this for V1 — the tool is local-only — but a future\n * hardening pass should resolve the absolute path of the running `work`\n * binary at install time and embed that instead.\n */\n\nimport {\n editSettings,\n editSettingsSync,\n isOwnerEntry,\n isStaleEntry,\n tag,\n type HookEntry,\n} from './settings-editor.js';\n\nexport interface CommandHookOptions {\n owner: string;\n /** Claude Code hook event to register under (e.g. UserPromptSubmit). */\n event: string;\n /** Shell command to execute. Receives the hook payload on stdin. */\n command: string;\n /** Hook timeout in seconds. Default 5. */\n timeoutSec?: number;\n}\n\nconst HOOK_TYPE = 'command';\n\nexport function installCommandHook(opts: CommandHookOptions): Promise<void> {\n return editSettings((s) => {\n if (!s.hooks) s.hooks = {};\n const list = (s.hooks[opts.event] ?? []) as HookEntry[];\n const cleaned = list.filter(\n (h) => !isStaleEntry(h) && !isOwnerEntry(h, opts.owner),\n );\n cleaned.push(\n tag(\n {\n hooks: [\n {\n type: HOOK_TYPE,\n command: opts.command,\n timeout: opts.timeoutSec ?? 5,\n },\n ],\n },\n opts.owner,\n ),\n );\n s.hooks[opts.event] = cleaned;\n });\n}\n\nexport function removeCommandHook(owner: string, event: string): Promise<void> {\n return editSettings((s) => removeOwnerEntries(s, owner, event));\n}\n\n/** Synchronous variant for signal handlers. */\nexport function removeCommandHookSync(owner: string, event: string): void {\n editSettingsSync((s) => removeOwnerEntries(s, owner, event));\n}\n\nfunction removeOwnerEntries(\n s: { hooks?: Record<string, HookEntry[] | undefined> },\n owner: string,\n event: string,\n): void {\n if (!s.hooks) return;\n const list = s.hooks[event];\n if (!Array.isArray(list)) return;\n s.hooks[event] = list.filter(\n (h) => !isStaleEntry(h) && !isOwnerEntry(h, owner),\n );\n if (s.hooks[event]!.length === 0) delete s.hooks[event];\n}\n","import type { CommandModule } from 'yargs';\nimport { readSessionActivity } from '../core/claude-activity.js';\nimport {\n findSessionForCwd,\n formatPendingForPrompt,\n markDelivered,\n readPendingForSession,\n sessionIdFor,\n} from '../core/pending-delivery.js';\n\n/**\n * `work hook prompt-submit` / `work hook stop` — invoked by Claude Code's\n * UserPromptSubmit / Stop hooks (registered by `work web` on startup).\n * Reads pending review comments for the worktree the user is currently\n * in, prints them in the format Claude Code expects for that event, and\n * marks them delivered so they don't repeat.\n *\n * Exits silently when:\n * - cwd isn't a `work`-managed worktree\n * - there are no pending comments\n * - Claude isn't actively running in this worktree (paranoia — the hook\n * only fires from inside a running Claude, but we double-check)\n */\nexport type HookEvent = 'prompt-submit' | 'stop';\n\nexport interface HookInput {\n event: HookEvent;\n cwd: string;\n}\n\nexport interface HookOutput {\n /** Plain text appended to the user's prompt (prompt-submit) or\n * raw JSON `{decision:'block', reason}` that prevents stop (stop). */\n stdout: string;\n /** The comment ids surfaced — caller marks them delivered. */\n deliveredIds: string[];\n /** The session id that produced the output, when there was something\n * to deliver. Null otherwise (so the caller knows to skip markDelivered). */\n sessionId: string | null;\n}\n\n/**\n * Pure transformation: given an event and cwd, produce the stdout payload\n * Claude Code expects plus the ids to mark delivered. Returns null when\n * there's nothing to surface. No I/O on stdin/stdout — the caller wraps.\n */\nexport function computeHookOutput(input: HookInput): HookOutput | null {\n const session = findSessionForCwd(input.cwd);\n if (!session) return null;\n\n const activity = readSessionActivity(session);\n if (activity.state === 'stale') return null;\n\n const sessionId = sessionIdFor(session);\n const pending = readPendingForSession(sessionId);\n if (pending.length === 0) return null;\n\n const text = formatPendingForPrompt(pending);\n if (!text) return null;\n\n const ids = pending.map((c) => c.id);\n\n if (input.event === 'prompt-submit') {\n return { stdout: text + '\\n', deliveredIds: ids, sessionId };\n }\n // Stop hook: `decision: 'block'` keeps Claude in the turn and feeds\n // `reason` back as additional context.\n return {\n stdout: JSON.stringify({ decision: 'block', reason: text }) + '\\n',\n deliveredIds: ids,\n sessionId,\n };\n}\n\ninterface HookPayload {\n cwd?: string;\n session_id?: string;\n hook_event_name?: string;\n}\n\nasync function readStdinJson(): Promise<HookPayload> {\n if (process.stdin.isTTY) return {};\n return new Promise((resolve) => {\n const chunks: Buffer[] = [];\n let resolved = false;\n const done = (val: HookPayload) => {\n if (resolved) return;\n resolved = true;\n resolve(val);\n };\n process.stdin.on('data', (c: Buffer) => chunks.push(c));\n process.stdin.on('end', () => {\n try {\n const raw = Buffer.concat(chunks).toString('utf-8').trim();\n done(raw ? (JSON.parse(raw) as HookPayload) : {});\n } catch {\n done({});\n }\n });\n process.stdin.on('error', () => done({}));\n setTimeout(() => done({}), 1000);\n });\n}\n\nexport const hookCommand: CommandModule = {\n command: 'hook <event>',\n describe: false, // hidden — humans don't call this directly\n builder: (y) =>\n y.positional('event', {\n type: 'string',\n choices: ['prompt-submit', 'stop'] as const,\n describe: 'Hook event name',\n }),\n handler: async (argv) => {\n const event = argv.event as HookEvent;\n const payload = await readStdinJson();\n const cwd = payload.cwd ?? process.cwd();\n const result = computeHookOutput({ event, cwd });\n if (!result || !result.sessionId) return;\n process.stdout.write(result.stdout);\n markDelivered(result.sessionId, result.deliveredIds);\n },\n};\n","import chalk from 'chalk';\nimport spawn from 'cross-spawn';\nimport type { CommandModule } from 'yargs';\nimport { loadHistory } from '../core/history.js';\nimport {\n selectSessions,\n expandRunUnits,\n anyFailed,\n type RunResult,\n type RunUnit,\n} from '../core/fleet.js';\n\n/** Build the shell invocation for the current platform. `sh -c <cmd>` on\n * POSIX, `cmd.exe /c <cmd>` on Windows. We pass cross-spawn an argv array\n * with shell:false — the user's command string is the intentional payload,\n * but we never interpolate paths/branches into a shell string ourselves. */\nfunction shellInvocation(cmd: string): { bin: string; args: string[] } {\n if (process.platform === 'win32') {\n return { bin: 'cmd.exe', args: ['/c', cmd] };\n }\n return { bin: 'sh', args: ['-c', cmd] };\n}\n\n/** Live children so a SIGINT (Ctrl-C) can tear the whole fleet down instead\n * of orphaning subprocesses. Keyed by the ChildProcess itself. */\nconst liveChildren = new Set<import('node:child_process').ChildProcess>();\n\nfunction killAllChildren(signal: NodeJS.Signals = 'SIGTERM'): void {\n for (const child of liveChildren) {\n try {\n child.kill(signal);\n } catch {\n /* already gone */\n }\n }\n}\n\n/**\n * Run `cmd` in `unit.path`. When `prefix` is set (parallel mode) the child's\n * stdout/stderr are captured and re-emitted line-by-line with a per-worktree\n * tag, so concurrent output stays attributable. Sequential mode passes\n * `stdio: 'inherit'` for a transparent, unprefixed passthrough.\n */\nfunction runInPath(\n unit: RunUnit,\n cmd: string,\n prefix?: string,\n): Promise<RunResult> {\n const { bin, args } = shellInvocation(cmd);\n return new Promise((resolve) => {\n const child = spawn(bin, args, {\n cwd: unit.path,\n stdio: prefix ? ['ignore', 'pipe', 'pipe'] : 'inherit',\n shell: false,\n });\n liveChildren.add(child);\n\n if (prefix) {\n const tag = chalk.cyan(`${prefix} `);\n const pipe = (\n stream: NodeJS.ReadableStream | null,\n sink: NodeJS.WriteStream,\n ) => {\n if (!stream) return;\n let buf = '';\n stream.setEncoding('utf-8');\n stream.on('data', (chunk: string) => {\n buf += chunk;\n let nl: number;\n while ((nl = buf.indexOf('\\n')) >= 0) {\n sink.write(tag + buf.slice(0, nl) + '\\n');\n buf = buf.slice(nl + 1);\n }\n });\n stream.on('end', () => {\n if (buf.length > 0) sink.write(tag + buf + '\\n');\n });\n };\n pipe(child.stdout, process.stdout);\n pipe(child.stderr, process.stderr);\n }\n\n child.on('close', (code) => {\n liveChildren.delete(child);\n resolve({ ...unit, code, ok: code === 0 });\n });\n child.on('error', () => {\n liveChildren.delete(child);\n resolve({ ...unit, code: null, ok: false });\n });\n });\n}\n\n/**\n * Run `units` concurrently with at most `limit` in flight at any time. Order\n * of the returned results matches `units`. A worker-pool rather than a single\n * `Promise.all` so a large fleet doesn't fork every subprocess at once.\n */\nexport async function runPool(\n units: RunUnit[],\n cmd: string,\n limit: number,\n /** Polled before each unit is dequeued. Once it returns true (Ctrl-C),\n * workers stop spawning new units rather than draining the whole queue. */\n isInterrupted: () => boolean = () => false,\n): Promise<RunResult[]> {\n const results: RunResult[] = new Array(units.length);\n let next = 0;\n async function worker(): Promise<void> {\n while (true) {\n // Bail BEFORE dequeuing/spawning the next unit once interrupted — the\n // SIGINT handler has already killed in-flight children; we must not\n // keep launching the remaining ones.\n if (isInterrupted()) return;\n const idx = next++;\n if (idx >= units.length) return;\n const u = units[idx];\n const prefix = `[${u.session.target}/${u.session.branch}]`;\n results[idx] = await runInPath(u, cmd, prefix);\n }\n }\n const workers = Array.from(\n { length: Math.min(limit, units.length) },\n () => worker(),\n );\n await Promise.all(workers);\n return results;\n}\n\nfunction label(r: RunResult): string {\n const head = chalk.cyan(`[${r.session.target}/${r.session.branch}]`);\n const where = chalk.gray(`(${r.path})`);\n if (r.ok) {\n return `${head} ${where} ${chalk.green('✓')} exit 0`;\n }\n const codeStr = r.code === null ? 'signalled' : `exit ${r.code}`;\n return `${head} ${where} ${chalk.red('✗')} ${codeStr}`;\n}\n\n/** Default cap on concurrent worktrees under `--parallel`. Bounded so a large\n * fleet doesn't fork hundreds of subprocesses at once. */\nconst DEFAULT_JOBS = 4;\n\n/** Our own fleet options. Everything else after the first bare token is the\n * user's command and is captured verbatim. Booleans take no value; the rest\n * consume the following token as their value. */\nconst BOOLEAN_FLAGS = new Set(['--parallel', '--halt-on-error', '--all']);\nconst VALUE_FLAGS = new Set(['--target', '--branch', '--jobs', '-j']);\n\n/** Drop the leading `run` command token(s). Under our parser config yargs\n * reports `argv._` as `['run', 'run', ...userArgs]` — the matched command\n * name plus a halt-at-non-option artifact — so we always strip exactly the\n * first two. A user-typed literal `run` (`work run run echo`) survives. */\nexport function stripRunToken(raw: string[]): string[] {\n let drop = 0;\n while (drop < 2 && raw[drop] === 'run') drop++;\n return raw.slice(drop);\n}\n\nexport interface ExtractedRun {\n /** Our parsed fleet options (only the ones present). */\n options: {\n target?: string;\n branch?: string;\n parallel?: boolean;\n haltOnError?: boolean;\n all?: boolean;\n jobs?: number;\n };\n /** The user's command argv, captured verbatim (flags included). */\n cmd: string[];\n}\n\n/**\n * Split `work run`'s raw arguments into our fleet options and the user's\n * command. yargs cannot do this safely: with the default parser it eats the\n * user command's own flags (`work run git log --oneline` drops `--oneline`;\n * `work run x --parallel` steals our fleet flag). So we parse the leading\n * fleet flags ourselves and treat the first bare token (or anything after a\n * literal `--`) as the start of the user command — capturing the rest,\n * including any flags, verbatim.\n *\n * `args` is everything after the `run` subcommand token.\n */\nexport function extractRun(args: string[]): ExtractedRun {\n const options: ExtractedRun['options'] = {};\n let i = 0;\n for (; i < args.length; i++) {\n const tok = args[i];\n if (tok === '--') {\n // Explicit separator: everything after is the command, verbatim.\n i++;\n break;\n }\n if (BOOLEAN_FLAGS.has(tok)) {\n if (tok === '--parallel') options.parallel = true;\n else if (tok === '--halt-on-error') options.haltOnError = true;\n else if (tok === '--all') options.all = true;\n continue;\n }\n if (VALUE_FLAGS.has(tok)) {\n const val = args[i + 1];\n if (val === undefined) break;\n if (tok === '--target') options.target = val;\n else if (tok === '--branch') options.branch = val;\n else if (tok === '--jobs' || tok === '-j') options.jobs = Number(val);\n i++;\n continue;\n }\n // First bare token (or unknown flag): start of the user command.\n break;\n }\n return { options, cmd: args.slice(i) };\n}\n\nexport const runCommand: CommandModule = {\n // `run [cmd..]` keeps yargs happy about the trailing positional while we do\n // the real argument extraction ourselves from the raw process argv.\n command: 'run [cmd..]',\n describe: 'Run a shell command in every worktree (optionally filtered)',\n builder: (yargs) =>\n yargs\n // Keep the user command's own flags out of our parser: unknown flags\n // (`--oneline`, `--fix`) become args instead of errors, and halt-at-non\n // -option stops parsing at the first bare token so a colliding flag\n // (`--parallel`) after the command name is not stolen. We do the real\n // split in `extractRun` from the raw argv.\n .parserConfiguration({\n 'halt-at-non-option': true,\n 'unknown-options-as-args': true,\n })\n .positional('cmd', {\n describe: 'Command to run (everything after `run`)',\n type: 'string',\n array: true,\n })\n .option('target', {\n describe: 'Only run in worktrees for this project/group alias',\n type: 'string',\n })\n .option('branch', {\n describe: 'With --target, only run in this branch',\n type: 'string',\n })\n .option('parallel', {\n describe: 'Run all worktrees concurrently (default: sequential)',\n type: 'boolean',\n default: false,\n })\n .option('jobs', {\n alias: 'j',\n describe: 'Max concurrent worktrees with --parallel (default: 4)',\n type: 'number',\n })\n .option('halt-on-error', {\n describe: 'Stop after the first failure (sequential only)',\n type: 'boolean',\n default: false,\n })\n .option('all', {\n describe: 'Required to run in every worktree when no --target',\n type: 'boolean',\n default: false,\n }),\n handler: async (argv) => {\n // yargs dumps the whole invocation into `_` verbatim under our parser\n // config (`['run', 'run', ...userArgs]` — the command token, doubled by\n // halt-at-non-option). Strip leading `run` tokens, then split off our\n // fleet flags ourselves so the user command (incl. its flags) survives.\n const rawAll = (argv._ as Array<string | number>).map(String);\n const { options, cmd: cmdParts } = extractRun(stripRunToken(rawAll));\n const cmd = cmdParts.join(' ').trim();\n if (!cmd) {\n console.error(chalk.red('No command given. Usage: work run <cmd...>'));\n process.exitCode = 1;\n return;\n }\n\n const target = options.target;\n const branch = options.branch;\n const parallel = options.parallel ?? false;\n const haltOnError = options.haltOnError ?? false;\n const all = options.all ?? false;\n const jobs =\n options.jobs && Number.isFinite(options.jobs) && options.jobs > 0\n ? Math.floor(options.jobs)\n : DEFAULT_JOBS;\n\n if (branch && !target) {\n console.error(chalk.red('--branch requires --target.'));\n process.exitCode = 1;\n return;\n }\n\n // Guardrail (mirrors `work broadcast`): running an arbitrary command in\n // every worktree is a wide blast radius, so a bare unfiltered `work run`\n // must opt in with --all rather than fanning out by accident.\n if (!target && !all) {\n console.error(\n chalk.red(\n 'Refusing to run in every worktree. Pass --all to confirm, or --target <alias> to narrow.',\n ),\n );\n process.exitCode = 1;\n return;\n }\n\n // --halt-on-error only applies to the sequential walk; the parallel pool\n // has no first-failure stop. Warn rather than silently ignoring it.\n if (parallel && haltOnError) {\n console.error(\n chalk.yellow(\n 'Warning: --halt-on-error has no effect with --parallel (it only applies to sequential runs).',\n ),\n );\n }\n\n const sessions = selectSessions(loadHistory(), { target, branch });\n const units = expandRunUnits(sessions);\n\n if (units.length === 0) {\n console.log(chalk.yellow('No matching worktrees.'));\n return;\n }\n\n console.log(\n chalk.cyan(\n `Running in ${units.length} worktree${units.length === 1 ? '' : 's'} (${parallel ? `parallel, up to ${jobs} at a time` : 'sequential'}): `,\n ) + chalk.white(cmd),\n );\n console.log('');\n\n const results: RunResult[] = [];\n\n // Tear the whole fleet down on Ctrl-C rather than orphaning children.\n let interrupted = false;\n const onSigint = () => {\n interrupted = true;\n console.error(chalk.yellow('\\nInterrupted — terminating child processes…'));\n killAllChildren('SIGTERM');\n };\n process.on('SIGINT', onSigint);\n\n try {\n if (parallel) {\n const settled = await runPool(units, cmd, jobs, () => interrupted);\n // On interrupt the pool returns a sparse array (un-dequeued units are\n // never filled); drop the holes before reporting.\n const done = settled.filter((r): r is RunResult => r != null);\n results.push(...done);\n for (const r of done) console.log(label(r));\n } else {\n for (const unit of units) {\n if (interrupted) break;\n const r = await runInPath(unit, cmd);\n results.push(r);\n console.log(label(r));\n if (!r.ok && haltOnError) {\n console.log(chalk.yellow('Halting on first failure (--halt-on-error).'));\n break;\n }\n }\n }\n } finally {\n process.off('SIGINT', onSigint);\n }\n\n if (interrupted) {\n console.log('');\n console.log(chalk.yellow('Aborted.'));\n process.exitCode = 130;\n return;\n }\n\n console.log('');\n const failed = results.filter((r) => !r.ok).length;\n if (anyFailed(results)) {\n console.log(chalk.red(`${failed} of ${results.length} failed.`));\n process.exitCode = 1;\n } else {\n console.log(chalk.green(`All ${results.length} succeeded.`));\n }\n },\n};\n","/**\n * Shared selection logic for the fleet commands (`work run`, `work broadcast`).\n * Pure helpers so command files stay thin and the filtering is unit-testable.\n *\n * Direction: core does NOT import from commands. Commands import these.\n */\n\nimport type { WorktreeSession } from './history.js';\n\nexport interface FleetFilter {\n /** Restrict to sessions whose target matches this alias/group name. */\n target?: string;\n /** With `target`, further restrict to a single branch. */\n branch?: string;\n}\n\n/**\n * Select the sessions a fleet command should act on.\n *\n * - No filter → every session.\n * - `target` only → all sessions for that target.\n * - `target` + `branch` → the single matching session (if any).\n *\n * `branch` without `target` is ignored for selection but the caller is\n * expected to reject that combination up front (it's ambiguous).\n */\nexport function selectSessions(\n sessions: WorktreeSession[],\n filter: FleetFilter,\n): WorktreeSession[] {\n return sessions.filter((s) => {\n if (filter.target && s.target !== filter.target) return false;\n if (filter.branch && s.branch !== filter.branch) return false;\n return true;\n });\n}\n\nexport interface RunUnit {\n session: WorktreeSession;\n /** A single worktree directory to run the command in. */\n path: string;\n}\n\n/**\n * Flatten selected sessions into one unit of work per worktree path. Group\n * sessions contribute one unit per path. Order is stable (session order,\n * then path order) so sequential runs are deterministic.\n */\nexport function expandRunUnits(sessions: WorktreeSession[]): RunUnit[] {\n const units: RunUnit[] = [];\n for (const session of sessions) {\n for (const p of session.paths) {\n units.push({ session, path: p });\n }\n }\n return units;\n}\n\nexport interface RunResult extends RunUnit {\n /** Exit code; null when the process was killed by a signal. */\n code: number | null;\n ok: boolean;\n}\n\n/** True when any unit failed (non-zero / signalled). */\nexport function anyFailed(results: RunResult[]): boolean {\n return results.some((r) => !r.ok);\n}\n","import fs from 'node:fs';\nimport chalk from 'chalk';\nimport type { CommandModule } from 'yargs';\nimport { loadHistory } from '../core/history.js';\nimport { broadcastPrompt } from '../core/broadcast.js';\n\n/** Read the whole of stdin synchronously (for `broadcast -`). */\nfunction readStdin(): string {\n try {\n return fs.readFileSync(0, 'utf-8');\n } catch {\n return '';\n }\n}\n\nexport const broadcastCommand: CommandModule = {\n command: 'broadcast <prompt>',\n describe:\n 'Queue a prompt to every live session; delivered on each session\\'s next turn',\n builder: (yargs) =>\n yargs\n .positional('prompt', {\n describe: 'Prompt to send, or \"-\" to read from stdin',\n type: 'string',\n })\n .option('target', {\n describe: 'Only sessions for this project/group alias',\n type: 'string',\n })\n .option('branch', {\n describe: 'With --target, only this branch',\n type: 'string',\n })\n .option('all', {\n describe: 'Required to broadcast to every session when no --target',\n type: 'boolean',\n default: false,\n }),\n handler: async (argv) => {\n let prompt = (argv.prompt as string | undefined) ?? '';\n if (prompt === '-') {\n prompt = readStdin();\n }\n prompt = prompt.trim();\n if (!prompt) {\n console.error(chalk.red('Empty prompt. Usage: work broadcast <prompt>'));\n process.exitCode = 1;\n return;\n }\n\n const target = argv.target as string | undefined;\n const branch = argv.branch as string | undefined;\n const all = argv.all as boolean;\n if (branch && !target) {\n console.error(chalk.red('--branch requires --target.'));\n process.exitCode = 1;\n return;\n }\n // Guardrail: broadcasting to every session is a wide blast radius, so\n // require an explicit --all (or a narrowing --target) rather than letting\n // a bare `work broadcast \"...\"` fan out to the whole fleet by accident.\n if (!target && !all) {\n console.error(\n chalk.red(\n 'Refusing to broadcast to every session. Pass --all to confirm, or --target <alias> to narrow.',\n ),\n );\n process.exitCode = 1;\n return;\n }\n\n const queued = await broadcastPrompt(loadHistory(), { target, branch }, prompt);\n\n if (queued.length === 0) {\n console.log(chalk.yellow('No matching sessions to broadcast to.'));\n return;\n }\n\n console.log(\n chalk.cyan(\n `Queued prompt to ${queued.length} session${queued.length === 1 ? '' : 's'}:`,\n ),\n );\n for (const t of queued) {\n console.log(\n ` ${chalk.cyan(`[${t.session.target}/${t.session.branch}]`)} ${chalk.gray(t.sessionId)}`,\n );\n }\n console.log('');\n console.log(\n chalk.gray(\n 'Delivery is lazy: each session picks this up on its next prompt (UserPromptSubmit hook).',\n ),\n );\n },\n};\n","/**\n * Core for `work broadcast`: queue a prompt to every (filtered) live session\n * via the existing pending-delivery mechanism. We do NOT touch PTYs — those\n * are per-process and unreachable from a separate CLI. Instead we post a\n * `published`, `author:'user'` comment to each session's comment file store;\n * `work hook prompt-submit` surfaces it on that session's next turn (see\n * pending-delivery.ts `readPendingForSession`, which selects exactly those).\n *\n * Because broadcast writes `~/.work` state from a SEPARATE process while\n * `work web` may be writing the same comment file, the write goes through\n * `withFileLock` + `atomicWriteFile` (§5.2): we lock, re-read the file from\n * disk, append, and atomically write back — so a concurrent server write\n * can't be lost or corrupted.\n */\n\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport {\n clearCommentStoreCache,\n commentsDir,\n commentsFileFor,\n} from './comment-file-store.js';\nimport { ensureFile, withFileLock, atomicWriteFile } from './fs-safe.js';\nimport { sessionIdFor } from './web-state.js';\nimport { selectSessions, type FleetFilter } from './fleet.js';\nimport type { WorktreeSession } from './history.js';\nimport type { Comment } from './comment-types.js';\n\nexport interface BroadcastTarget {\n session: WorktreeSession;\n sessionId: string;\n commentId: string;\n}\n\nfunction readComments(file: string): Comment[] {\n try {\n const parsed = JSON.parse(fs.readFileSync(file, 'utf-8'));\n return Array.isArray(parsed) ? (parsed as Comment[]) : [];\n } catch {\n return [];\n }\n}\n\n/** Append one published user comment to a session's comment file under an\n * advisory lock, so concurrent `work web` writes to the same file can't\n * clobber each other. Returns the new comment's id. */\nasync function appendLocked(sessionId: string, body: string): Promise<string> {\n const file = commentsFileFor(sessionId);\n fs.mkdirSync(commentsDir(), { recursive: true });\n ensureFile(file, '[]');\n const comment: Comment = {\n id: crypto.randomBytes(8).toString('hex'),\n repo: '',\n file: '',\n line: 0,\n side: 'general',\n body,\n createdAt: new Date().toISOString(),\n author: 'user',\n status: 'published',\n };\n await withFileLock(file, () => {\n const existing = readComments(file);\n existing.push(comment);\n atomicWriteFile(file, JSON.stringify(existing, null, 2));\n });\n return comment.id;\n}\n\n/**\n * Post `prompt` as a published user comment (side 'general') to every session\n * matching `filter`. Returns one entry per session queued.\n */\nexport async function broadcastPrompt(\n sessions: WorktreeSession[],\n filter: FleetFilter,\n prompt: string,\n): Promise<BroadcastTarget[]> {\n const body = prompt.trim();\n if (!body) throw new Error('broadcast prompt is empty');\n\n const selected = selectSessions(sessions, filter);\n const out: BroadcastTarget[] = [];\n for (const session of selected) {\n const sessionId = sessionIdFor(session);\n const commentId = await appendLocked(sessionId, body);\n out.push({ session, sessionId, commentId });\n }\n // Any in-process cache for these files is now stale (we wrote underneath it\n // via the lock path) — drop it so subsequent reads see the on-disk truth.\n if (out.length > 0) clearCommentStoreCache();\n return out;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { loadConfig } from '../core/config.js';\nimport { parseWorktreeList, getCurrentBranch } from '../core/git.js';\nimport { resolveProjectTarget } from '../core/resolve.js';\n\ntype Done = (completions: string[]) => void;\n\n/**\n * Dynamic completion handler for yargs.\n * Called when the user presses TAB (via --get-yargs-completions).\n *\n * argv._ = ['<scriptName>', '<command>', '<arg1>', ...., '<current>']\n * We skip the script name and the trailing current word to get the \"completed\" args.\n */\nexport function completionHandler(\n current: string,\n argv: Record<string, unknown>,\n done: Done,\n): void {\n const rawArgs = argv._ as string[];\n\n // Skip the script name (first element) and the trailing current word (last element)\n const args = rawArgs.slice(1, -1);\n const command = args[0] as string | undefined;\n\n const config = loadConfig();\n if (!config) {\n done([]);\n return;\n }\n\n if (!command) {\n done(\n ['tree', 't', 'remove', 'list', 'status', 'recent', 'resume', 'prune', 'dash', 'todo', 'hydrate', 'diff', 'web', 'init', 'config', 'completion'].filter(\n (c) => c.startsWith(current),\n ),\n );\n return;\n }\n\n switch (command) {\n case 'config':\n completeConfig(args, current, config, done);\n return;\n\n case 'tree':\n case 't':\n case 'remove':\n case 'list':\n case 'status':\n completeTreeRemoveList(command, args, current, config, done);\n return;\n\n default:\n done([]);\n }\n}\n\nfunction completeConfig(\n args: string[],\n current: string,\n config: NonNullable<ReturnType<typeof loadConfig>>,\n done: Done,\n): void {\n const subAction = args[1] as string | undefined;\n\n if (!subAction) {\n const actions = [\n 'add',\n 'remove',\n 'list',\n 'group',\n 'show',\n 'edit',\n ];\n done(actions.filter((a) => a.startsWith(current)));\n return;\n }\n\n if (subAction === 'remove' && args.length === 2) {\n const aliases = Object.keys(config.repos);\n done(aliases.filter((a) => a.startsWith(current)));\n return;\n }\n\n if (subAction === 'group') {\n const groupSub = args[2] as string | undefined;\n\n if (!groupSub) {\n const subs = ['add', 'remove', 'regen'];\n done(subs.filter((s) => s.startsWith(current)));\n return;\n }\n\n if (\n (groupSub === 'remove' || groupSub === 'regen') &&\n args.length === 3\n ) {\n const groups = Object.keys(config.groups);\n done(groups.filter((g) => g.startsWith(current)));\n return;\n }\n\n if (groupSub === 'add' && args.length >= 4) {\n const alreadyUsed = args.slice(3);\n const aliases = Object.keys(config.repos).filter(\n (a) => !alreadyUsed.includes(a),\n );\n done(aliases.filter((a) => a.startsWith(current)));\n return;\n }\n\n done([]);\n return;\n }\n\n done([]);\n}\n\nfunction completeTreeRemoveList(\n command: string,\n args: string[],\n current: string,\n config: NonNullable<ReturnType<typeof loadConfig>>,\n done: Done,\n): void {\n const targetName = args[1] as string | undefined;\n\n if (!targetName) {\n const names = [\n ...Object.keys(config.repos),\n ...Object.keys(config.groups),\n ];\n done(names.filter((n) => n.startsWith(current)));\n return;\n }\n\n if (args.length === 2) {\n const target = resolveProjectTarget(targetName, config);\n if (!target) {\n done([]);\n return;\n }\n\n if (target.isGroup) {\n completeGroupBranches(target.name, target.repoAliases, current, config, done);\n } else {\n completeRepoBranches(targetName, current, config, done);\n }\n return;\n }\n\n done([]);\n}\n\nfunction completeRepoBranches(\n alias: string,\n current: string,\n config: NonNullable<ReturnType<typeof loadConfig>>,\n done: Done,\n): void {\n const repoPath = config.repos[alias];\n if (!repoPath || !fs.existsSync(repoPath)) {\n done([]);\n return;\n }\n\n const worktrees = parseWorktreeList(repoPath);\n const branches = worktrees\n .map((wt) => wt.branch)\n .filter((b): b is string => !!b && b.startsWith(current));\n done(branches);\n}\n\nfunction completeGroupBranches(\n groupName: string,\n repoAliases: string[],\n current: string,\n config: NonNullable<ReturnType<typeof loadConfig>>,\n done: Done,\n): void {\n const groupDir = path.join(config.worktreesRoot, groupName);\n if (!fs.existsSync(groupDir)) {\n done([]);\n return;\n }\n\n try {\n const branchDirs = fs\n .readdirSync(groupDir, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => {\n const bdPath = path.join(groupDir, d.name);\n for (const alias of repoAliases) {\n const repoPath = config.repos[alias];\n if (!repoPath) continue;\n const subPath = path.join(bdPath, path.basename(repoPath));\n if (fs.existsSync(subPath)) {\n const branch = getCurrentBranch(subPath);\n if (branch) return branch;\n }\n }\n return d.name;\n });\n\n done(branchDirs.filter((b) => b.startsWith(current)));\n } catch {\n done([]);\n }\n}\n","declare const __WORK2_VERSION__: string;\n\n/**\n * Inlined at build time from package.json#version. When running via `tsx`\n * (dev mode) the define isn't applied, so we fall back to a placeholder.\n */\nexport const VERSION: string =\n typeof __WORK2_VERSION__ !== 'undefined' ? __WORK2_VERSION__ : 'dev';\n"],"mappings":"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","names":["fs","path","chalk","chalk","fs","path","chalk","path","fs","path","fs","path","path","fs","path","spawn","path","fs","content","spawn","spawn","fs","spawn","yargs","fs","chalk","path","path","os","chalk","fs","path","os","chalk","spawn","label","chalk","path","os","fs","chalk","fs","path","path","fs","fs","path","chalk","fs","path","fs","fs","os","path","fs","input","path","fs","fs","path","fs","chalk","path","yargs","fs","chalk","chalk","yargs","chalk","fs","path","chalk","yargs","chalk","fs","path","fs","chalk","yargs","chalk","fs","fs","path","chalk","select","yargs","chalk","fs","select","path","fs","path","chalk","select","yargs","fs","chalk","select","path","chalk","fs","path","chalk","fetch","fs","chalk","path","label","yargs","chalk","fs","chalk","yargs","fs","chalk","fs","path","Box","fs","os","path","execFile","spawn","execFile","execFile","execAsync","fs","path","path","fs","path","fs","path","os","fs","path","path","spawn","sessionKey","spawn","sessionKey","label","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","jsx","jsxs","path","os","fs","pty","execFile","sessionKey","sessions","spawn","jsxs","Box","jsx","jsx","label","fs","path","yargs","chalk","yargs","chalk","chalk","yargs","chalk","chalk","fs","path","path","fs","yargs","chalk","fs","os","path","chalk","fs","os","path","spawn","input","fs","path","path","fs","path","spawn","realRoot","fs","os","run","files","result","fs","path","os","crypto","path","chalk","path","chalk","Hono","crypto","findParent","input","chalk","path","chalk","fs","path","path","fs","fs","path","info","chalk","Hono","input","fs","path","path","fs","fs","logPath","chalk","path","os","info","yargs","fs","os","path","chalk","fs","os","path","chalk","Hono","streamSSE","path","crypto","chokidar","crypto","findSession","chokidar","path","findSession","pty","fs","path","os","path","os","fs","input","fs","path","pathFor","path","fs","writeAtomic","zValidator","findSession","zValidator","pty","zValidator","z","z","zValidator","path","spawn","zValidator","z","z","zValidator","findSession","fetch","path","spawn","zValidator","z","path","spawn","fs","os","path","crypto","spawn","path","os","fs","crypto","run","spawn","label","path","crypto","crypto","path","label","streamSSE","spawn","zValidator","z","label","repos","path","pty","path","os","fs","Hono","findSession","streamSSE","chalk","info","path","os","isPidAlive","readPid","fs","chalk","yargs","input","chalk","spawn","spawn","tag","chalk","yargs","fs","chalk","crypto","fs","fs","crypto","fs","yargs","chalk","fs","path","fs","path","chalk","yargs","chalk","fs","path"]}
1
+ {"version":3,"sources":["../src/bin.ts","../src/cli.ts","../src/commands/config.ts","../src/core/config.ts","../src/core/git.ts","../src/core/logger.ts","../src/core/claude-md.ts","../src/utils/platform.ts","../src/core/ai-launcher.ts","../src/commands/init.ts","../src/core/setup-completions.ts","../src/commands/tree.ts","../src/core/resolve.ts","../src/core/worktree.ts","../src/core/history.ts","../src/core/fs-safe.ts","../src/core/claude-activity.ts","../src/core/port-allocator.ts","../src/core/copy-files.ts","../src/core/base-spec.ts","../src/commands/remove.ts","../src/commands/list.ts","../src/commands/status.ts","../src/utils/format.ts","../src/commands/recent.ts","../src/commands/resume.ts","../src/commands/prune.ts","../src/core/prunable-scan.ts","../src/commands/sync.ts","../src/tui-ink/index.tsx","../src/tui-ink/App.tsx","../src/core/pr.ts","../src/core/jira.ts","../src/core/tasks.ts","../src/tui/session.ts","../src/core/hook-server.ts","../src/core/settings-editor.ts","../src/core/notifier.ts","../src/core/status-hooks.ts","../src/tui-ink/renderer-lines.ts","../src/tui-ink/Sidebar.tsx","../src/tui-ink/TerminalPane.tsx","../src/tui-ink/StatusBar.tsx","../src/commands/dash.ts","../src/commands/completion.ts","../src/commands/todo.ts","../src/commands/hydrate.ts","../src/core/hydrate.ts","../src/commands/diff.ts","../src/core/diff-pipeline.ts","../src/core/diff-parse.ts","../src/core/lcov.ts","../src/core/git-tree-snapshot.ts","../src/core/repo-spec.ts","../src/core/diff-scope.ts","../src/core/comment-server.ts","../src/core/comment-store.ts","../src/core/diff-server.ts","../src/core/file-context.ts","../src/core/fs-watcher.ts","../src/core/web-static.ts","../src/core/spa-handler.ts","../src/core/comment-schemas.ts","../src/core/review-poll.ts","../src/core/static-renderer.ts","../src/commands/web.ts","../src/core/web-server.ts","../src/core/web-state.ts","../src/core/pty-pool.ts","../src/core/comment-file-store.ts","../src/core/pending-delivery.ts","../src/core/session-meta.ts","../src/core/session-comment-routes.ts","../src/core/panes-routes.ts","../src/core/worktree-routes.ts","../src/core/scope-routes.ts","../src/core/checkpoint.ts","../src/core/scope-manager.ts","../src/core/terminal-routes.ts","../src/core/terminal-ws.ts","../src/core/command-hook-installer.ts","../src/commands/hook.ts","../src/commands/run.ts","../src/core/fleet.ts","../src/commands/broadcast.ts","../src/core/broadcast.ts","../src/completions/index.ts","../src/version.ts"],"sourcesContent":["import fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport chalk from 'chalk';\r\nimport { run } from './cli.js';\r\nimport { installConsoleLogger, debug } from './core/logger.js';\r\nimport { getConfigDir } from './core/config.js';\r\n\r\n// Install debug logging — all console.log/error/warn also write to ~/.work/debug.log\r\ninstallConsoleLogger();\r\ndebug('--- work started', process.argv.slice(2).join(' '), '---');\r\n\r\n// Force color support — this is an interactive CLI, and some Windows terminals\r\n// (e.g. PowerShell via conhost) don't set isTTY on spawned .cmd shims.\r\nif (!process.env.NO_COLOR && chalk.level === 0) {\r\n chalk.level = 1;\r\n}\r\n\r\nfunction handleFatalError(err: unknown): void {\r\n if (err instanceof Error && err.name === 'ExitPromptError') {\r\n console.log('\\nCancelled.');\r\n process.exit(0);\r\n }\r\n // node-pty can throw async errors for already-exited PTYs — non-fatal in dash mode\r\n if (err instanceof Error && err.message?.includes('pty that has already exited')) {\r\n try {\r\n fs.appendFileSync(path.join(getConfigDir(), 'debug.log'),\r\n `${new Date().toISOString()} [WARN] Ignored async node-pty error: ${err.message}\\n`);\r\n } catch { /* */ }\r\n return;\r\n }\r\n try {\r\n const msg = err instanceof Error ? err.stack || err.message : String(err);\r\n fs.appendFileSync(path.join(getConfigDir(), 'debug.log'),\r\n `${new Date().toISOString()} [FATAL] handleFatalError: ${msg}\\n`);\r\n } catch { /* */ }\r\n console.error(err);\r\n process.exit(1);\r\n}\r\n\r\nprocess.on('uncaughtException', handleFatalError);\r\nprocess.on('unhandledRejection', handleFatalError);\r\n\r\nrun(process.argv.slice(2));\r\n","import chalk from 'chalk';\nimport yargs from 'yargs';\nimport { configCommand } from './commands/config.js';\nimport { initCommand } from './commands/init.js';\nimport { treeCommand } from './commands/tree.js';\nimport { removeCommand } from './commands/remove.js';\nimport { listCommand } from './commands/list.js';\nimport { statusCommand } from './commands/status.js';\nimport { recentCommand } from './commands/recent.js';\nimport { resumeCommand } from './commands/resume.js';\nimport { pruneCommand } from './commands/prune.js';\nimport { syncCommand } from './commands/sync.js';\nimport { dashCommand } from './commands/dash.js';\nimport { completionCommand } from './commands/completion.js';\nimport { todoCommand } from './commands/todo.js';\nimport { hydrateCommand } from './commands/hydrate.js';\nimport { diffCommand } from './commands/diff.js';\nimport { webCommand } from './commands/web.js';\nimport { hookCommand } from './commands/hook.js';\nimport { runCommand } from './commands/run.js';\nimport { broadcastCommand } from './commands/broadcast.js';\nimport { completionHandler } from './completions/index.js';\nimport { VERSION } from './version.js';\n\nfunction showHelp() {\n console.log('');\n console.log(chalk.cyan(`Work - Git Worktree Manager ${chalk.gray(`v${VERSION}`)}`));\n console.log(chalk.cyan('============================'));\n console.log('');\n console.log(chalk.green('Usage:'));\n console.log(' work init - Set up configuration');\n console.log(' work config <action> - Manage configuration');\n console.log(' work list [project|group] - List all worktrees');\n console.log(' work tree|t <project|group> <branch> - Create/switch to worktree');\n console.log(' work tree <project|group> <branch> --base <branch> - Create from a specific base branch');\n console.log(' work tree <project|group> <branch> --open - Also open VS Code');\n console.log(' work tree <project|group> <branch> --unsafe - Skip AI tool permission checks');\n console.log(' work remove <project|group> <branch> - Remove worktree');\n console.log(' work remove <project|group> <branch> --force - Force remove worktree');\n console.log(' work status [project|group] [branch] - Show worktree status');\n console.log(' work status --prune - Remove stale entries');\n console.log(' work recent [count] - List recent sessions');\n console.log(' work resume - Resume a recent session');\n console.log(' work dash - Interactive session dashboard');\n console.log(' work web - Browser dashboard (one tab, every session)');\n console.log(' work prune - Remove merged worktrees');\n console.log(' work prune --force - Remove all merged (no prompt)');\n console.log(' work sync - Fetch all repos and prune merged (non-interactive)');\n console.log(' work sync --dry-run - Show what sync would prune, remove nothing');\n console.log(' work sync --force - Also remove worktrees with uncommitted/unpushed changes');\n console.log(' work sync --include-squash - Also prune squash-merged branches (lower confidence)');\n console.log(' work hydrate - Seed history from worktrees on disk');\n console.log(' work diff [base] - Open a GitHub-PR-style diff in your browser');\n console.log(' work run <cmd...> - Run a command in every worktree');\n console.log(' work run <cmd...> --target <alias> --parallel - Filter + run concurrently');\n console.log(' work broadcast <prompt> - Queue a prompt to every live session');\n console.log(' work todo - List tasks');\n console.log(' work todo add <text> - Add a task');\n console.log(' work todo done <id> - Mark task complete');\n console.log(' work todo rm <id> - Remove a task');\n console.log(' work completion --install - Install shell completions');\n console.log('');\n console.log(chalk.green('Config Actions:'));\n console.log(' work config add <alias> <path> - Add a repository');\n console.log(' work config remove <alias> - Remove a repository');\n console.log(' work config list - List repos and groups');\n console.log(' work config group add <name> <alias1> <alias2> ... - Create a repository group');\n console.log(' work config group remove <name> - Remove a repository group');\n console.log(' work config group regen <name> - Regenerate group CLAUDE.md');\n console.log('');\n console.log(chalk.green('Examples:'));\n console.log(' work init');\n console.log(' work list');\n console.log(' work list ai');\n console.log(' work tree ai feature/login');\n console.log(' work tree frontend feature/login --open');\n console.log(' work tree ai feature/hotfix --unsafe');\n console.log(' work remove ai feature/login');\n console.log('');\n console.log(chalk.gray(' # Groups (multi-repo worktrees):'));\n console.log(' work config group add fullstack api frontend');\n console.log(' work tree fullstack feature/login');\n console.log(' work remove fullstack feature/login');\n console.log('');\n}\n\nexport function run(argv: string[]) {\n // Show custom colored help when no args given\n if (argv.length === 0) {\n showHelp();\n return;\n }\n\n const cli = yargs(argv)\n .scriptName('work')\n .usage('$0 <command> [options]')\n .command(initCommand)\n .command(configCommand)\n .command(treeCommand)\n .command(removeCommand)\n .command(listCommand)\n .command(statusCommand)\n .command(recentCommand)\n .command(resumeCommand)\n .command(pruneCommand)\n .command(syncCommand)\n .command(dashCommand)\n .command(todoCommand)\n .command(hydrateCommand)\n .command(diffCommand)\n .command(webCommand)\n .command(hookCommand)\n .command(runCommand)\n .command(broadcastCommand)\n .command(completionCommand)\n // Hidden: yargs uses this internally for --get-yargs-completions\n .completion('__completions', false as any, completionHandler)\n .demandCommand(1, 'You need to specify a command. Run work --help for usage.')\n .strict()\n .fail((msg, err, yargs) => {\n if (err?.name === 'ExitPromptError') {\n console.log('\\nCancelled.');\n process.exit(0);\n }\n if (msg) {\n yargs.showHelp();\n console.error('\\n' + msg);\n }\n if (err) console.error(err);\n process.exit(1);\n })\n .help()\n .alias('h', 'help')\n .version(VERSION)\n .alias('v', 'version')\n .wrap(Math.min(100, process.stdout.columns || 80));\n\n cli.parse();\n}\n","import fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport chalk from 'chalk';\r\nimport type { ArgumentsCamelCase, CommandModule } from 'yargs';\r\nimport {\r\n loadConfig,\r\n saveConfig,\r\n getConfigPath,\r\n getConfigDir,\r\n ensureConfig,\r\n} from '../core/config.js';\r\nimport { isGitRepo } from '../core/git.js';\r\nimport { generateGroupClaudeMd } from '../core/claude-md.js';\r\nimport { openInEditor } from '../utils/platform.js';\r\n\r\nexport const configCommand: CommandModule = {\r\n command: 'config <action>',\r\n describe: 'Manage configuration',\r\n builder: (yargs) =>\r\n yargs\r\n .showHelpOnFail(true)\r\n .positional('action', {\r\n describe: 'Config action to perform',\r\n choices: [\r\n 'add',\r\n 'remove',\r\n 'list',\r\n 'group',\r\n 'show',\r\n 'edit',\r\n ] as const,\r\n type: 'string',\r\n demandOption: true,\r\n })\r\n .option('args', {\r\n type: 'array',\r\n string: true,\r\n hidden: true,\r\n })\r\n .strict(false),\r\n handler: (argv) => {\r\n const action = argv.action as string;\r\n // Collect all extra positional args after the action\r\n const extra = (argv._ as string[]).slice(1); // slice off 'config'\r\n\r\n switch (action) {\r\n case 'add':\r\n handleAdd(extra);\r\n break;\r\n case 'remove':\r\n handleRemove(extra);\r\n break;\r\n case 'list':\r\n handleList();\r\n break;\r\n case 'group':\r\n handleGroup(extra);\r\n break;\r\n case 'show':\r\n handleShow();\r\n break;\r\n case 'edit':\r\n handleEdit();\r\n break;\r\n default:\r\n showConfigHelp();\r\n }\r\n },\r\n};\r\n\r\nfunction handleAdd(args: string[]): void {\r\n const [alias, repoPath] = args;\r\n if (!alias || !repoPath) {\r\n console.error('Usage: work config add <alias> <path>');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n if (!fs.existsSync(repoPath)) {\r\n console.error(`Path does not exist: ${repoPath}`);\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n if (!isGitRepo(repoPath)) {\r\n console.error(`Path is not a git repository: ${repoPath}`);\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n const config = ensureConfig();\r\n config.repos[alias] = repoPath;\r\n saveConfig(config);\r\n console.log(chalk.green(`Added: ${alias} -> ${repoPath}`));\r\n}\r\n\r\nfunction handleRemove(args: string[]): void {\r\n const [alias] = args;\r\n if (!alias) {\r\n console.error('Usage: work config remove <alias>');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n const config = ensureConfig();\r\n\r\n if (!(alias in config.repos)) {\r\n console.error(`Repository alias not found: ${alias}`);\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n delete config.repos[alias];\r\n saveConfig(config);\r\n console.log(chalk.green(`Removed: ${alias}`));\r\n}\r\n\r\nfunction handleList(): void {\r\n const config = loadConfig();\r\n if (!config) {\r\n console.log(\r\n chalk.yellow(\r\n 'No configuration found. Run \"work init\" to set up.',\r\n ),\r\n );\r\n return;\r\n }\r\n\r\n console.log('');\r\n console.log(chalk.cyan('Work Configuration'));\r\n console.log(chalk.cyan('=================='));\r\n console.log(chalk.green(`Worktrees Root: ${config.worktreesRoot}`));\r\n console.log('');\r\n console.log(chalk.green('Repositories:'));\r\n\r\n const repoKeys = Object.keys(config.repos);\r\n if (repoKeys.length === 0) {\r\n console.log(chalk.gray(' (none configured)'));\r\n } else {\r\n for (const key of repoKeys) {\r\n console.log(` ${key} -> ${config.repos[key]}`);\r\n }\r\n }\r\n console.log('');\r\n\r\n console.log(chalk.green('Groups:'));\r\n const groupKeys = Object.keys(config.groups);\r\n if (groupKeys.length === 0) {\r\n console.log(chalk.gray(' (none configured)'));\r\n } else {\r\n for (const key of groupKeys) {\r\n const aliases = config.groups[key].join(', ');\r\n console.log(` ${key} -> [${aliases}]`);\r\n }\r\n }\r\n console.log('');\r\n}\r\n\r\nfunction handleGroup(args: string[]): void {\r\n const [subAction, ...rest] = args;\r\n switch (subAction) {\r\n case 'add':\r\n handleAddGroup(rest);\r\n break;\r\n case 'remove':\r\n handleRemoveGroup(rest);\r\n break;\r\n case 'regen':\r\n handleRegenGroup(rest);\r\n break;\r\n default:\r\n showGroupHelp();\r\n }\r\n}\r\n\r\nfunction handleAddGroup(args: string[]): void {\r\n const [groupName, ...repoAliases] = args;\r\n if (!groupName) {\r\n console.error(\r\n 'Usage: work config group add <name> <alias1> <alias2> [alias3...]',\r\n );\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n if (repoAliases.length < 2) {\r\n console.error('A group must contain at least 2 repository aliases.');\r\n console.log(\r\n chalk.yellow(\r\n 'Usage: work config group add <name> <alias1> <alias2> [alias3...]',\r\n ),\r\n );\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n const config = ensureConfig();\r\n\r\n // Validate: all aliases exist in repos\r\n for (const alias of repoAliases) {\r\n if (!(alias in config.repos)) {\r\n console.error(`Repository alias not found: ${alias}`);\r\n console.log(\r\n chalk.yellow(\r\n `Available aliases: ${Object.keys(config.repos).join(', ')}`,\r\n ),\r\n );\r\n process.exitCode = 1;\r\n return;\r\n }\r\n }\r\n\r\n // Validate: group name doesn't collide with repo aliases\r\n if (groupName in config.repos) {\r\n console.error(\r\n `Group name '${groupName}' conflicts with an existing repository alias.`,\r\n );\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n // Validate: group name doesn't collide with repo folder names\r\n const repoFolderNames = Object.values(config.repos).map((p) =>\r\n path.basename(p),\r\n );\r\n if (repoFolderNames.includes(groupName)) {\r\n console.error(\r\n `Group name '${groupName}' conflicts with a repository folder name.`,\r\n );\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n config.groups[groupName] = repoAliases;\r\n saveConfig(config);\r\n console.log(\r\n chalk.green(\r\n `Added group: ${groupName} -> [${repoAliases.join(', ')}]`,\r\n ),\r\n );\r\n\r\n // Generate combined CLAUDE.md\r\n generateGroupClaudeMd(groupName, repoAliases, config);\r\n}\r\n\r\nfunction handleRemoveGroup(args: string[]): void {\r\n const [groupName] = args;\r\n if (!groupName) {\r\n console.error('Usage: work config group remove <name>');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n const config = ensureConfig();\r\n\r\n if (!(groupName in config.groups)) {\r\n console.error(`Group not found: ${groupName}`);\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n delete config.groups[groupName];\r\n saveConfig(config);\r\n\r\n // Delete the .claude.md file\r\n const claudeMdPath = path.join(getConfigDir(), `${groupName}.claude.md`);\r\n if (fs.existsSync(claudeMdPath)) {\r\n fs.unlinkSync(claudeMdPath);\r\n console.log(`Deleted: ${claudeMdPath}`);\r\n }\r\n\r\n console.log(chalk.green(`Removed group: ${groupName}`));\r\n}\r\n\r\nfunction handleRegenGroup(args: string[]): void {\r\n const [groupName] = args;\r\n if (!groupName) {\r\n console.error('Usage: work config group regen <name>');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n const config = ensureConfig();\r\n\r\n if (!(groupName in config.groups)) {\r\n console.error(`Group not found: ${groupName}`);\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n const repoAliases = config.groups[groupName];\r\n generateGroupClaudeMd(groupName, repoAliases, config);\r\n}\r\n\r\nfunction handleShow(): void {\r\n const configPath = getConfigPath();\r\n if (fs.existsSync(configPath)) {\r\n console.log(chalk.green(`Config file: ${configPath}`));\r\n console.log('');\r\n const content = fs.readFileSync(configPath, 'utf-8');\r\n console.log(content);\r\n } else {\r\n console.log(\r\n chalk.yellow(\r\n 'No configuration file found. Run \"work init\" to set up.',\r\n ),\r\n );\r\n }\r\n}\r\n\r\nfunction handleEdit(): void {\r\n const configPath = getConfigPath();\r\n if (!fs.existsSync(configPath)) {\r\n console.error(\r\n 'No configuration file found. Run \"work init\" first.',\r\n );\r\n process.exitCode = 1;\r\n return;\r\n }\r\n openInEditor(configPath);\r\n}\r\n\r\nfunction showConfigHelp(): void {\r\n console.log(chalk.yellow('Usage: work config <action>'));\r\n console.log('');\r\n console.log(chalk.green('Actions:'));\r\n console.log(\r\n ' add <alias> <path> - Add a repository',\r\n );\r\n console.log(\r\n ' remove <alias> - Remove a repository',\r\n );\r\n console.log(\r\n ' list - List all configured repositories and groups',\r\n );\r\n console.log(\r\n ' group <sub> - Manage groups (add, remove, regen)',\r\n );\r\n console.log(\r\n ' show - Show configuration file contents',\r\n );\r\n console.log(\r\n ' edit - Open configuration file in editor',\r\n );\r\n}\r\n\r\nfunction showGroupHelp(): void {\r\n console.log(chalk.yellow('Usage: work config group <action>'));\r\n console.log('');\r\n console.log(chalk.green('Actions:'));\r\n console.log(\r\n ' add <name> <alias1> <alias2> [...] - Create a repository group',\r\n );\r\n console.log(\r\n ' remove <name> - Remove a repository group',\r\n );\r\n console.log(\r\n ' regen <name> - Regenerate group CLAUDE.md',\r\n );\r\n}\r\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\n\n/**\n * An opt-in shell command that runs when a session changes status. The command\n * runs with the session's directory as its cwd (passed as the spawn `cwd`\n * option, never interpolated into the command string).\n */\nexport interface StatusHook {\n on: 'idle' | 'needs_input';\n command: string;\n}\n\nexport interface WorkConfig {\n worktreesRoot: string;\n repos: Record<string, string>;\n groups: Record<string, string[]>;\n copyFiles: string[];\n /**\n * AI tool command to launch in worktrees. May include extra args, e.g.\n * \"claude\" (default), \"gemini\", \"codex\", or \"my-tool --some-flag\".\n */\n aiCommand?: string;\n /**\n * Per-tool flag overrides. Defaults come from the preset matching the\n * binary in `aiCommand` (see AI_TOOL_PRESETS in core/ai-launcher.ts).\n * Set any value to an empty string to disable that flag for the configured tool.\n */\n aiCommandFlags?: {\n /** Flag for skipping permission checks. */\n unsafe?: string;\n /** Flag for resuming the most recent session. */\n resume?: string;\n /** Flag for passing an initial prompt as a file path. */\n promptFile?: string;\n /** Flag for passing an inline prompt; empty string = positional arg. */\n prompt?: string;\n };\n /** Editor command for opening worktrees. Default: \"code\" */\n editor?: string;\n /**\n * Range of dev-server ports to allocate to worktrees (inclusive).\n * Each worktree gets a stable port exposed as $PORT to the launched process.\n * Default when unset: { start: 3000, end: 3099 }.\n */\n portRange?: { start: number; end: number };\n /**\n * Opt-in desktop notifications. When true, the dashboard fires an OS\n * notification when a session goes idle or needs input. Default: off.\n */\n notifications?: boolean;\n /**\n * Opt-in shell commands run when a session changes status (idle /\n * needs_input). Each command runs with the session dir as its cwd.\n * Generalizes `notifications`; both paths work independently. Default: none.\n */\n statusHooks?: StatusHook[];\n}\n\n/** Lowest port we allow to be configured (avoid privileged ports < 1024). */\nconst MIN_PORT = 1024;\n/** Highest valid TCP port. */\nconst MAX_PORT = 65535;\n\n/**\n * Validate a configured port range. Returns the normalized range when it is a\n * pair of integers with `MIN_PORT <= start <= end <= MAX_PORT`, otherwise\n * undefined (callers then fall back to the default range). Rejects non-integers,\n * privileged/out-of-bounds ports, and reversed ranges.\n */\nexport function validatePortRange(\n value: unknown,\n): { start: number; end: number } | undefined {\n if (!value || typeof value !== 'object') return undefined;\n const { start, end } = value as { start?: unknown; end?: unknown };\n if (typeof start !== 'number' || typeof end !== 'number') return undefined;\n if (!Number.isInteger(start) || !Number.isInteger(end)) return undefined;\n if (start < MIN_PORT || end > MAX_PORT) return undefined;\n if (start > end) return undefined;\n return { start, end };\n}\n\nexport function getConfigDir(): string {\n const dir = path.join(os.homedir(), '.work');\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n return dir;\n}\n\nexport function getConfigPath(): string {\n return path.join(getConfigDir(), 'config.json');\n}\n\nexport function loadConfig(): WorkConfig | null {\n const configPath = getConfigPath();\n if (!fs.existsSync(configPath)) {\n return null;\n }\n\n try {\n const raw = fs.readFileSync(configPath, 'utf-8');\n const parsed = JSON.parse(raw);\n return {\n worktreesRoot: parsed.worktreesRoot ?? '',\n repos: parsed.repos ?? {},\n groups: parsed.groups ?? {},\n copyFiles: parsed.copyFiles ?? [],\n aiCommand: parsed.aiCommand,\n aiCommandFlags: parsed.aiCommandFlags,\n editor: parsed.editor,\n portRange: validatePortRange(parsed.portRange),\n notifications: parsed.notifications === true,\n statusHooks: Array.isArray(parsed.statusHooks) ? parsed.statusHooks : [],\n };\n } catch {\n return null;\n }\n}\n\nexport function saveConfig(config: WorkConfig): void {\n const configPath = getConfigPath();\n fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');\n}\n\nexport function ensureConfig(): WorkConfig {\n const config = loadConfig();\n if (!config) {\n throw new Error('Configuration not found. Run \"work init\" to set up.');\n }\n return config;\n}\n","import spawn from 'cross-spawn';\r\nimport { execFile } from 'node:child_process';\r\nimport path from 'node:path';\r\nimport { debug } from './logger.js';\r\n\r\nexport interface GitResult {\r\n stdout: string;\r\n stderr: string;\r\n exitCode: number;\r\n}\r\n\r\n/** Run a git command synchronously in the given cwd. */\r\nexport function git(args: string[], cwd: string): GitResult {\r\n debug('git', args.join(' '), `cwd=${cwd}`);\r\n const result = spawn.sync('git', args, {\r\n cwd,\r\n encoding: 'utf-8',\r\n stdio: ['pipe', 'pipe', 'pipe'],\r\n // Avoid flashing a console window on Windows for every git call —\r\n // matters most when this runs inside the detached `wd --watch` daemon.\r\n windowsHide: true,\r\n });\r\n\r\n const r = {\r\n stdout: (result.stdout ?? '').toString().trim(),\r\n stderr: (result.stderr ?? '').toString().trim(),\r\n exitCode: result.status ?? 1,\r\n };\r\n if (r.exitCode !== 0) {\r\n debug('git failed', { args, exitCode: r.exitCode, stderr: r.stderr });\r\n }\r\n return r;\r\n}\r\n\r\nexport interface WorktreeEntry {\r\n path: string;\r\n branch: string;\r\n head: string;\r\n}\r\n\r\n/** Parse `git worktree list --porcelain` output into structured entries. */\r\nexport function parseWorktreeList(cwd: string): WorktreeEntry[] {\r\n const result = git(['worktree', 'list', '--porcelain'], cwd);\r\n if (result.exitCode !== 0) return [];\r\n\r\n const entries: WorktreeEntry[] = [];\r\n let current: Partial<WorktreeEntry> = {};\r\n\r\n for (const line of result.stdout.split('\\n')) {\r\n const worktreeMatch = line.match(/^worktree (.+)$/);\r\n if (worktreeMatch) {\r\n if (current.path) {\r\n entries.push({\r\n path: current.path,\r\n branch: current.branch ?? '',\r\n head: current.head ?? '',\r\n });\r\n }\r\n current = { path: worktreeMatch[1] };\r\n continue;\r\n }\r\n\r\n const headMatch = line.match(/^HEAD (.+)$/);\r\n if (headMatch) {\r\n current.head = headMatch[1].substring(0, 7);\r\n continue;\r\n }\r\n\r\n const branchMatch = line.match(/^branch (.+)$/);\r\n if (branchMatch) {\r\n current.branch = branchMatch[1].replace(/^refs\\/heads\\//, '');\r\n }\r\n }\r\n\r\n // Push last entry\r\n if (current.path) {\r\n entries.push({\r\n path: current.path,\r\n branch: current.branch ?? '',\r\n head: current.head ?? '',\r\n });\r\n }\r\n\r\n return entries;\r\n}\r\n\r\n/** Check if a local branch exists. */\r\nexport function localBranchExists(branch: string, cwd: string): boolean {\r\n const result = git(['rev-parse', '--verify', branch], cwd);\r\n return result.exitCode === 0;\r\n}\r\n\r\n/** Check if a remote tracking branch exists. */\r\nexport function remoteBranchExists(branch: string, cwd: string): boolean {\r\n const result = git(['rev-parse', '--verify', `origin/${branch}`], cwd);\r\n return result.exitCode === 0;\r\n}\r\n\r\n/** Check if path is a valid git worktree/repo. */\r\nexport function isGitRepo(cwd: string): boolean {\r\n const result = git(['rev-parse', '--is-inside-work-tree'], cwd);\r\n return result.exitCode === 0 && result.stdout === 'true';\r\n}\r\n\r\n/** Get the absolute toplevel of the worktree containing cwd, or null if not a repo. */\r\nexport function getWorktreeRoot(cwd: string): string | null {\r\n const result = git(['rev-parse', '--show-toplevel'], cwd);\r\n if (result.exitCode !== 0 || !result.stdout) return null;\r\n return result.stdout;\r\n}\r\n\r\n/**\r\n * Get the MAIN repo working directory for a (possibly linked) worktree.\r\n * `--git-common-dir` points at the main repo's `.git` dir; the main repo root\r\n * is its parent when the basename is `.git`, otherwise the path itself.\r\n * Returns null on failure.\r\n */\r\nexport function getMainRepoRoot(cwd: string): string | null {\r\n const result = git(\r\n ['rev-parse', '--path-format=absolute', '--git-common-dir'],\r\n cwd,\r\n );\r\n if (result.exitCode !== 0 || !result.stdout) return null;\r\n const commonDir = result.stdout;\r\n if (path.basename(commonDir) === '.git') {\r\n return path.dirname(commonDir);\r\n }\r\n return commonDir;\r\n}\r\n\r\n/** Get current branch name. */\r\nexport function getCurrentBranch(cwd: string): string {\r\n const result = git(['branch', '--show-current'], cwd);\r\n return result.stdout;\r\n}\r\n\r\n/** Check for uncommitted changes (returns porcelain status). */\r\nexport function getStatus(cwd: string): string {\r\n const result = git(['status', '--porcelain'], cwd);\r\n return result.stdout;\r\n}\r\n\r\n/** Get the default branch from origin/HEAD (e.g. \"main\" or \"master\"). */\r\nexport function getDefaultBranch(cwd: string): string | null {\r\n const result = git(['symbolic-ref', 'refs/remotes/origin/HEAD'], cwd);\r\n if (result.exitCode === 0 && result.stdout) {\r\n // Output is like \"refs/remotes/origin/main\"\r\n return result.stdout.replace(/^refs\\/remotes\\/origin\\//, '');\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * Result of checking a branch against a single base ref.\r\n *\r\n * 'merged' is a high-confidence true merge (the branch tip is reachable from\r\n * base via a merge/fast-forward). 'squash-merged' is the lower-confidence\r\n * heuristic match from {@link checkSquashMerged} — the branch's squashed patch\r\n * already appears in base, but there is no real merge commit linking the two.\r\n * Callers that prune unattended should treat 'squash-merged' as opt-in only.\r\n */\r\ntype MergeCheck = 'merged' | 'squash-merged' | 'stale' | 'unrelated';\r\n\r\n/**\r\n * Check if a branch is truly merged into a base ref (not just a stale branch\r\n * sitting behind base with no unique commits).\r\n *\r\n * Returns 'stale' if the branch tip is on the base's first-parent chain\r\n * (no unique work). Returns 'merged' if the branch has unique commits that\r\n * are all reachable from base. Returns 'squash-merged' if only the squash\r\n * heuristic matched. Returns 'unrelated' otherwise.\r\n */\r\nfunction checkMergeStatus(branch: string, baseRef: string, cwd: string): MergeCheck {\r\n // 1. Branch must be an ancestor of base (all its commits are in base)\r\n const branchInBase = git(['merge-base', '--is-ancestor', branch, baseRef], cwd);\r\n if (branchInBase.exitCode !== 0) {\r\n // Not a regular merge — check for squash merge (low-confidence heuristic).\r\n if (checkSquashMerged(branch, baseRef, cwd)) return 'squash-merged';\r\n return 'unrelated';\r\n }\r\n\r\n // 2. If base is also ancestor of branch, they're at the same commit\r\n // (fast-forward merge or empty branch) — nothing left to merge.\r\n const baseInBranch = git(['merge-base', '--is-ancestor', baseRef, branch], cwd);\r\n if (baseInBranch.exitCode === 0) return 'merged';\r\n\r\n // 3. Branch is ancestor of base — but is it on the main line (stale) or\r\n // only reachable via a merge commit (truly merged)?\r\n // A stale branch's tip sits directly on the first-parent chain.\r\n const tip = git(['rev-parse', branch], cwd);\r\n if (tip.exitCode !== 0) return 'unrelated';\r\n const mainLine = git(['rev-list', '--first-parent', '-n', '500', baseRef], cwd);\r\n if (mainLine.exitCode === 0 && mainLine.stdout.includes(tip.stdout)) {\r\n return 'stale';\r\n }\r\n\r\n return 'merged';\r\n}\r\n\r\n/**\r\n * Detect squash merges by creating a temporary dangling commit that represents\r\n * the branch's tree squashed onto the merge-base, then checking if that patch\r\n * is already present in the base ref via `git cherry`.\r\n */\r\nfunction checkSquashMerged(branch: string, baseRef: string, cwd: string): boolean {\r\n const mb = git(['merge-base', baseRef, branch], cwd);\r\n if (mb.exitCode !== 0) return false;\r\n\r\n const tree = git(['rev-parse', `${branch}^{tree}`], cwd);\r\n if (tree.exitCode !== 0) return false;\r\n\r\n const dangling = git(['commit-tree', tree.stdout, '-p', mb.stdout, '-m', ''], cwd);\r\n if (dangling.exitCode !== 0) return false;\r\n\r\n const cherry = git(['cherry', baseRef, dangling.stdout], cwd);\r\n if (cherry.exitCode !== 0) return false;\r\n\r\n // If the output starts with \"-\", the patch is already in baseRef (squash-merged)\r\n return cherry.stdout.startsWith('-');\r\n}\r\n\r\n/** Confidence level for a positive merge result. */\r\nexport type MergeConfidence = 'merged' | 'squash-merged';\r\n\r\nexport interface MergeCheckResult {\r\n merged: boolean;\r\n /** The base ref it matched against (e.g. \"origin/main\"), or null if not merged. */\r\n into: string | null;\r\n /**\r\n * How the match was determined when `merged` is true: 'merged' is a\r\n * high-confidence true merge; 'squash-merged' is the lower-confidence\r\n * squash heuristic. Null when not merged.\r\n */\r\n confidence: MergeConfidence | null;\r\n}\r\n\r\nexport function isBranchMerged(\r\n branch: string,\r\n cwd: string,\r\n baseBranch?: string,\r\n): MergeCheckResult {\r\n const defaultBranch = getDefaultBranch(cwd);\r\n const bases = baseBranch\r\n ? [baseBranch]\r\n : [defaultBranch, 'main', 'master'].filter(\r\n (b): b is string => b !== null,\r\n );\r\n\r\n // Deduplicate (e.g. default branch is \"main\" which is already in the list)\r\n const uniqueBases = [...new Set(bases)];\r\n\r\n for (const base of uniqueBases) {\r\n // Check remote base branch first (most up-to-date after fetch)\r\n if (remoteBranchExists(base, cwd)) {\r\n const baseRef = `origin/${base}`;\r\n const status = checkMergeStatus(branch, baseRef, cwd);\r\n if (status === 'merged') return { merged: true, into: baseRef, confidence: 'merged' };\r\n if (status === 'squash-merged')\r\n return { merged: true, into: baseRef, confidence: 'squash-merged' };\r\n if (status === 'stale') return { merged: false, into: null, confidence: null };\r\n }\r\n\r\n // Fall back to local base branch\r\n if (localBranchExists(base, cwd)) {\r\n const status = checkMergeStatus(branch, base, cwd);\r\n if (status === 'merged') return { merged: true, into: base, confidence: 'merged' };\r\n if (status === 'squash-merged')\r\n return { merged: true, into: base, confidence: 'squash-merged' };\r\n if (status === 'stale') return { merged: false, into: null, confidence: null };\r\n }\r\n }\r\n\r\n return { merged: false, into: null, confidence: null };\r\n}\r\n\r\n/** Fetch latest remote refs for a repo and ensure origin/HEAD is set. */\r\nexport function fetchRemote(cwd: string): void {\r\n git(['fetch', '--quiet'], cwd);\r\n\r\n // Ensure origin/HEAD is set so getDefaultBranch works\r\n if (!getDefaultBranch(cwd)) {\r\n git(['remote', 'set-head', 'origin', '--auto'], cwd);\r\n }\r\n}\r\n\r\n/** Async version of fetchRemote — non-blocking. */\r\nexport async function fetchRemoteAsync(cwd: string): Promise<void> {\r\n await new Promise<void>((resolve, reject) => {\r\n execFile(\r\n 'git',\r\n ['fetch', '--quiet'],\r\n { cwd, timeout: 30000, windowsHide: true },\r\n (err) => {\r\n if (err) reject(err);\r\n else resolve();\r\n },\r\n );\r\n });\r\n if (!getDefaultBranch(cwd)) {\r\n git(['remote', 'set-head', 'origin', '--auto'], cwd);\r\n }\r\n}\r\n\r\n/** Rebase a branch onto the default branch. Returns error message or null on success. */\r\nexport function rebaseOntoMain(branch: string, cwd: string): string | null {\r\n const defaultBranch = getDefaultBranch(cwd) ?? 'main';\r\n // Fetch latest first\r\n git(['fetch', '--quiet'], cwd);\r\n const base = remoteBranchExists(defaultBranch, cwd) ? `origin/${defaultBranch}` : defaultBranch;\r\n const result = git(['rebase', base, branch], cwd);\r\n if (result.exitCode !== 0) {\r\n // Abort the failed rebase\r\n git(['rebase', '--abort'], cwd);\r\n return result.stderr || 'Rebase failed';\r\n }\r\n return null;\r\n}\r\n\r\n/** Count merge conflicts between a branch and the default branch. Returns 0 if clean. */\r\nexport function countConflicts(branch: string, cwd: string): number {\r\n const defaultBranch = getDefaultBranch(cwd) ?? 'main';\r\n const base = remoteBranchExists(defaultBranch, cwd) ? `origin/${defaultBranch}` : defaultBranch;\r\n\r\n // Use merge-tree to simulate merge without changing worktree\r\n const mergeBase = git(['merge-base', base, branch], cwd);\r\n if (mergeBase.exitCode !== 0) return 0;\r\n\r\n const result = git(['merge-tree', mergeBase.stdout, base, branch], cwd);\r\n // Count conflict markers in merge-tree output\r\n const conflicts = (result.stdout.match(/^<<<<<<< /gm) || []).length;\r\n return conflicts;\r\n}\r\n\r\n/** Check for unpushed commits. Returns the log output or empty string. */\r\nexport function getUnpushedCommits(cwd: string): string {\r\n const branch = getCurrentBranch(cwd);\r\n if (!branch) return '';\r\n\r\n const upstreamResult = git(\r\n ['rev-parse', '--abbrev-ref', `${branch}@{upstream}`],\r\n cwd,\r\n );\r\n if (upstreamResult.exitCode !== 0) return '';\r\n\r\n const upstream = upstreamResult.stdout;\r\n const logResult = git(['log', '--oneline', `${upstream}..HEAD`], cwd);\r\n return logResult.stdout;\r\n}\r\n","import fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport { getConfigDir } from './config.js';\r\n\r\nconst MAX_LOG_SIZE = 5 * 1024 * 1024; // 5MB\r\n\r\nlet logPath: string | null = null;\r\nlet logStream: fs.WriteStream | null = null;\r\n\r\nfunction ensureLogStream(): fs.WriteStream | null {\r\n if (logStream) return logStream;\r\n try {\r\n const dir = getConfigDir();\r\n // Make sure the parent directory exists before opening a stream into\r\n // it — under tests (mocked homedir) or first-run conditions the .work\r\n // dir may not have been created yet, and createWriteStream's failure\r\n // is async (emits 'error') which would otherwise become an unhandled\r\n // exception in the host process.\r\n fs.mkdirSync(dir, { recursive: true });\r\n logPath = path.join(dir, 'debug.log');\r\n\r\n // Rotate if too large\r\n try {\r\n const stat = fs.statSync(logPath);\r\n if (stat.size > MAX_LOG_SIZE) {\r\n const prev = logPath + '.1';\r\n try { fs.unlinkSync(prev); } catch { /* */ }\r\n fs.renameSync(logPath, prev);\r\n }\r\n } catch { /* file doesn't exist yet */ }\r\n\r\n const stream = fs.createWriteStream(logPath, { flags: 'a' });\r\n // Best-effort logging — async stream errors must not crash the host.\r\n stream.on('error', () => { /* */ });\r\n logStream = stream;\r\n return logStream;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nfunction timestamp(): string {\r\n return new Date().toISOString();\r\n}\r\n\r\n// Strip ANSI escape codes for clean log output\r\nfunction stripAnsi(str: string): string {\r\n return str.replace(/\\x1b\\[[0-9;]*m/g, '');\r\n}\r\n\r\nexport function debugLog(level: 'INFO' | 'ERROR' | 'DEBUG' | 'WARN', ...args: unknown[]): void {\r\n const stream = ensureLogStream();\r\n if (!stream) return;\r\n const msg = args.map((a) => typeof a === 'string' ? stripAnsi(a) : JSON.stringify(a)).join(' ');\r\n stream.write(`${timestamp()} [${level}] ${msg}\\n`);\r\n}\r\n\r\n/**\r\n * Patch console.log and console.error to also write to the debug log.\r\n * Call once at startup.\r\n */\r\nexport function installConsoleLogger(): void {\r\n const origLog = console.log.bind(console);\r\n const origError = console.error.bind(console);\r\n const origWarn = console.warn.bind(console);\r\n\r\n console.log = (...args: unknown[]) => {\r\n origLog(...args);\r\n debugLog('INFO', ...args);\r\n };\r\n\r\n console.error = (...args: unknown[]) => {\r\n origError(...args);\r\n debugLog('ERROR', ...args);\r\n };\r\n\r\n console.warn = (...args: unknown[]) => {\r\n origWarn(...args);\r\n debugLog('WARN', ...args);\r\n };\r\n}\r\n\r\n/** Write a debug-only message (not shown to user). */\r\nexport function debug(...args: unknown[]): void {\r\n debugLog('DEBUG', ...args);\r\n}\r\n\r\n/** Get the log file path. */\r\nexport function getLogPath(): string {\r\n ensureLogStream();\r\n return logPath ?? path.join(getConfigDir(), 'debug.log');\r\n}\r\n\r\n/** Flush and close the log stream. */\r\nexport function closeLog(): void {\r\n if (logStream) {\r\n logStream.end();\r\n logStream = null;\r\n }\r\n}\r\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport spawn from 'cross-spawn';\nimport chalk from 'chalk';\nimport type { WorkConfig } from './config.js';\nimport { getConfigDir } from './config.js';\n\n/**\n * Generate a combined CLAUDE.md for a group using `claude -p`.\n * Falls back to a concatenated template if the Claude CLI call fails.\n */\nexport function generateGroupClaudeMd(\n groupName: string,\n repoAliases: string[],\n config: WorkConfig,\n): void {\n const outputPath = path.join(getConfigDir(), `${groupName}.claude.md`);\n\n // Build prompt with each repo's CLAUDE.md\n const promptParts: string[] = [];\n promptParts.push(\n 'You are generating a CLAUDE.md file for a multi-repository workspace.',\n );\n promptParts.push(\n 'The workspace contains the following repositories as subdirectories:',\n );\n promptParts.push('');\n\n for (const alias of repoAliases) {\n const repoPath = config.repos[alias];\n const repoName = path.basename(repoPath);\n const claudeMdPath = path.join(repoPath, 'CLAUDE.md');\n\n promptParts.push(`## Repository: ${repoName}/ (alias: ${alias})`);\n\n if (fs.existsSync(claudeMdPath)) {\n const content = fs.readFileSync(claudeMdPath, 'utf-8');\n promptParts.push('### CLAUDE.md contents:');\n promptParts.push('```');\n promptParts.push(content);\n promptParts.push('```');\n } else {\n promptParts.push('(no CLAUDE.md found)');\n }\n promptParts.push('');\n }\n\n promptParts.push('Generate a combined CLAUDE.md for this workspace that:');\n promptParts.push(\n '1. Explains the workspace structure (which subdirectories contain which repos)',\n );\n promptParts.push(\n \"2. Merges and synthesizes the instructions from all repos' CLAUDE.md files\",\n );\n promptParts.push(\n '3. Notes any cross-repo relationships or considerations',\n );\n promptParts.push(\n '4. Keeps all specific technical instructions (build commands, test commands, etc.) organized by repository',\n );\n promptParts.push('');\n promptParts.push(\n 'Output ONLY the markdown content for the combined CLAUDE.md, with no additional commentary.',\n );\n\n const prompt = promptParts.join('\\n');\n\n console.log(\n chalk.cyan(\n `Generating combined CLAUDE.md for group '${groupName}'...`,\n ),\n );\n console.log(\n chalk.gray('(This will call Claude to generate the combined file)'),\n );\n\n // Try claude -p\n const result = spawn.sync('claude', ['-p'], {\n input: prompt,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n windowsHide: true,\n });\n\n let content: string;\n\n if (result.status !== 0 || !result.stdout?.trim()) {\n console.log(\n chalk.yellow(\n 'Failed to generate CLAUDE.md via Claude. Creating a basic template instead.',\n ),\n );\n content = buildFallbackTemplate(groupName, repoAliases, config);\n } else {\n content = result.stdout.trim();\n }\n\n fs.writeFileSync(outputPath, content, 'utf-8');\n console.log(chalk.green(`Saved: ${outputPath}`));\n}\n\nfunction buildFallbackTemplate(\n groupName: string,\n repoAliases: string[],\n config: WorkConfig,\n): string {\n const parts: string[] = [];\n parts.push(`# Multi-Repository Workspace: ${groupName}`);\n parts.push('');\n parts.push('This workspace contains the following repositories:');\n parts.push('');\n\n for (const alias of repoAliases) {\n const repoPath = config.repos[alias];\n const repoName = path.basename(repoPath);\n parts.push(`- **${repoName}/** (alias: ${alias})`);\n }\n\n parts.push('');\n parts.push('## Per-Repository Instructions');\n parts.push('');\n\n for (const alias of repoAliases) {\n const repoPath = config.repos[alias];\n const repoName = path.basename(repoPath);\n const claudeMdPath = path.join(repoPath, 'CLAUDE.md');\n\n parts.push(`### ${repoName}`);\n\n if (fs.existsSync(claudeMdPath)) {\n const content = fs.readFileSync(claudeMdPath, 'utf-8');\n parts.push(content);\n } else {\n parts.push('(no CLAUDE.md found)');\n }\n parts.push('');\n }\n\n return parts.join('\\n');\n}\n","import spawn from 'cross-spawn';\nimport { buildAiLaunchArgs, type AiLaunchOpts, type AiToolSpec } from '../core/ai-launcher.js';\n\n/** Get the platform-appropriate default editor. */\nexport function getEditor(): string {\n return (\n process.env.EDITOR ??\n process.env.VISUAL ??\n (process.platform === 'win32' ? 'notepad' : 'vi')\n );\n}\n\n/** Open a file in the user's editor. */\nexport function openInEditor(filePath: string): void {\n const editor = getEditor();\n spawn.sync(editor, [filePath], { stdio: 'inherit' });\n}\n\n/** Open a URL in the default browser. */\nexport function openUrl(url: string): void {\n const cmd = process.platform === 'win32' ? 'cmd' : process.platform === 'darwin' ? 'open' : 'xdg-open';\n const args = process.platform === 'win32' ? ['/c', 'start', '', url] : [url];\n spawn(cmd, args, { stdio: 'ignore', detached: true }).unref();\n}\n\n/** Open VS Code in the given directory. */\nexport function openVSCode(dir: string): void {\n spawn.sync('code', ['.'], { cwd: dir, stdio: 'inherit' });\n}\n\n/**\n * Launch the configured AI tool in the given directory.\n *\n * When `port` is provided it is injected as `$PORT` into the launched process\n * so dev servers started by parallel agent sessions don't collide.\n */\nexport function launchAi(\n cwd: string,\n tool: AiToolSpec,\n opts: AiLaunchOpts = {},\n port?: number,\n): void {\n const { cmd, args } = buildAiLaunchArgs(tool, opts);\n const spawnOpts: Parameters<typeof spawn.sync>[2] =\n port !== undefined\n ? { cwd, stdio: 'inherit', env: { ...process.env, PORT: String(port) } }\n : { cwd, stdio: 'inherit' };\n spawn.sync(cmd, args, spawnOpts);\n}\n","import fs from 'node:fs';\nimport type { WorkConfig } from './config.js';\n\n/** Resolved AI tool spec ready for spawning. */\nexport interface AiToolSpec {\n /** Executable name. */\n cmd: string;\n /** Arguments always prepended (from any extra tokens in `aiCommand`). */\n baseArgs: string[];\n /** Flag injected when `--unsafe` is used. Empty string disables. */\n unsafeFlag: string;\n /** Flag injected when resuming a prior session. Empty string disables. */\n resumeFlag: string;\n /**\n * Flag used to pass an initial prompt as a file (followed by the file path).\n * Empty string disables — prompt-file content will be inlined via `promptFlag`\n * instead, if available.\n */\n promptFileFlag: string;\n /**\n * Flag used to pass an inline prompt (followed by the prompt string).\n * Empty string means the prompt is appended as a positional argument\n * (Claude's behavior).\n */\n promptFlag: string;\n}\n\nexport interface AiLaunchOpts {\n unsafe?: boolean;\n resume?: boolean;\n promptFile?: string;\n /** Inline prompt (used by CLI `--prompt`). */\n initialPrompt?: string;\n}\n\n/**\n * Per-tool flag presets. Looked up by the binary in `aiCommand`. Users can\n * override any individual flag via `config.aiCommandFlags`.\n */\nexport const AI_TOOL_PRESETS: Record<string, {\n unsafe: string;\n resume: string;\n promptFile: string;\n prompt: string;\n}> = {\n claude: {\n unsafe: '--dangerously-skip-permissions',\n resume: '--continue',\n promptFile: '--prompt-file',\n prompt: '', // positional\n },\n opencode: {\n unsafe: '', // no equivalent — permissions are configured elsewhere\n resume: '--continue',\n promptFile: '', // no file flag — content gets inlined via promptFlag\n prompt: '--prompt',\n },\n};\n\n/** Display names for the init picker. */\nexport const KNOWN_TOOLS = [\n { name: 'Claude Code (claude)', value: 'claude' },\n { name: 'OpenCode (opencode)', value: 'opencode' },\n] as const;\n\n/**\n * Resolve the configured AI tool. Falls back to the `claude` preset so default\n * behavior is preserved when no `aiCommand` is set.\n */\nexport function getAiTool(config: Pick<WorkConfig, 'aiCommand' | 'aiCommandFlags'>): AiToolSpec {\n const command = (config.aiCommand ?? 'claude').trim();\n const parts = command.split(/\\s+/).filter(Boolean);\n const cmd = parts[0] || 'claude';\n const baseArgs = parts.slice(1);\n const preset = AI_TOOL_PRESETS[cmd] ?? AI_TOOL_PRESETS.claude;\n const overrides = config.aiCommandFlags ?? {};\n return {\n cmd,\n baseArgs,\n unsafeFlag: overrides.unsafe ?? preset.unsafe,\n resumeFlag: overrides.resume ?? preset.resume,\n promptFileFlag: overrides.promptFile ?? preset.promptFile,\n promptFlag: overrides.prompt ?? preset.prompt,\n };\n}\n\n/** Build the final `{ cmd, args }` to spawn for the given launch options. */\nexport function buildAiLaunchArgs(tool: AiToolSpec, opts: AiLaunchOpts = {}): { cmd: string; args: string[] } {\n const args = [...tool.baseArgs];\n if (opts.unsafe && tool.unsafeFlag) args.push(tool.unsafeFlag);\n if (opts.resume && tool.resumeFlag) args.push(tool.resumeFlag);\n\n let inlinePrompt = opts.initialPrompt;\n if (opts.promptFile) {\n if (tool.promptFileFlag) {\n args.push(tool.promptFileFlag, opts.promptFile);\n } else if (tool.promptFlag) {\n // Tool has no prompt-file flag — read the file and pass inline instead.\n try { inlinePrompt = fs.readFileSync(opts.promptFile, 'utf-8'); }\n catch { /* leave inlinePrompt as-is */ }\n }\n }\n if (inlinePrompt) {\n if (tool.promptFlag) args.push(tool.promptFlag, inlinePrompt);\n else args.push(inlinePrompt);\n }\n return { cmd: tool.cmd, args };\n}\n","import path from 'node:path';\nimport os from 'node:os';\nimport chalk from 'chalk';\nimport { input, confirm, select } from '@inquirer/prompts';\nimport type { CommandModule } from 'yargs';\nimport {\n loadConfig,\n saveConfig,\n getConfigPath,\n type WorkConfig,\n} from '../core/config.js';\nimport { isGitRepo } from '../core/git.js';\nimport { KNOWN_TOOLS } from '../core/ai-launcher.js';\nimport {\n setupCompletions,\n printCompletionResults,\n printManualInstructions,\n} from '../core/setup-completions.js';\n\nexport const initCommand: CommandModule = {\n command: 'init',\n describe: 'Set up work configuration interactively',\n handler: async () => {\n console.log('');\n console.log(chalk.cyan('Welcome to Work - Git Worktree Manager'));\n console.log(chalk.cyan('======================================'));\n console.log('');\n\n const configPath = getConfigPath();\n let config = loadConfig();\n\n if (config) {\n console.log(\n chalk.yellow(\n `Configuration file already exists at: ${configPath}`,\n ),\n );\n const overwrite = await confirm({\n message:\n 'Do you want to reconfigure? This will keep existing repos.',\n default: false,\n });\n\n if (!overwrite) {\n console.log('Initialization cancelled.');\n return;\n }\n }\n\n if (!config) {\n config = {\n worktreesRoot: '',\n repos: {},\n groups: {},\n copyFiles: [\n '*.Development.json',\n '*.Local.json',\n '.claude/settings.local.json',\n ],\n };\n }\n\n // Configure worktrees root\n console.log(chalk.green('Where should all worktrees be created?'));\n const defaultRoot =\n config.worktreesRoot ||\n path.join(path.dirname(os.homedir()), 'worktrees');\n\n const worktreesInput = await input({\n message: 'Worktrees root directory',\n default: defaultRoot,\n });\n\n config.worktreesRoot = worktreesInput || defaultRoot;\n\n console.log('');\n console.log(\n chalk.green(\n `Great! Worktrees will be created in: ${config.worktreesRoot}`,\n ),\n );\n console.log('');\n\n // AI tool selection\n console.log(chalk.green('Which AI tool should be launched in worktrees?'));\n const currentTool = (config.aiCommand ?? 'claude').trim().split(/\\s+/)[0];\n const knownChoice = KNOWN_TOOLS.find((t) => t.value === currentTool);\n const toolChoice = await select<string>({\n message: 'AI tool',\n choices: [\n ...KNOWN_TOOLS.map((t) => ({ name: t.name, value: t.value })),\n { name: 'Custom (enter command manually)', value: '__custom__' },\n ],\n default: knownChoice ? knownChoice.value : '__custom__',\n });\n\n if (toolChoice === '__custom__') {\n const customCmd = await input({\n message: 'Command to launch (with any base args)',\n default: config.aiCommand ?? 'claude',\n });\n config.aiCommand = customCmd.trim() || 'claude';\n } else {\n config.aiCommand = toolChoice;\n }\n console.log(chalk.green(`AI tool set to: ${config.aiCommand}`));\n console.log('');\n\n // Add repositories\n console.log(chalk.green('Now let\\'s add your repositories.'));\n console.log(\n chalk.gray('(You can add more later with: work config add <alias> <path>)'),\n );\n console.log('');\n\n let addMore = true;\n let repoCount = 1;\n\n while (addMore) {\n console.log(chalk.yellow(`Repository #${repoCount}:`));\n\n const alias = await input({\n message: \" Alias (short name, e.g., 'ai', 'frontend')\",\n });\n\n if (!alias.trim()) {\n console.log(chalk.red('Alias cannot be empty. Skipping.'));\n continue;\n }\n\n const repoPath = await input({\n message: ' Repository path',\n });\n\n if (!repoPath.trim()) {\n console.log(chalk.red('Repository path cannot be empty. Skipping.'));\n continue;\n }\n\n // Validate path is a git repo\n if (!isGitRepo(repoPath)) {\n console.log(\n chalk.red(\n `Path is not a git repository (or does not exist): ${repoPath}`,\n ),\n );\n continue;\n }\n\n config.repos[alias.trim()] = repoPath.trim();\n console.log(\n chalk.green(` Added: ${alias.trim()} -> ${repoPath.trim()}`),\n );\n console.log('');\n\n repoCount++;\n\n addMore = await confirm({\n message: 'Add another repository?',\n default: false,\n });\n }\n\n // Save configuration\n saveConfig(config);\n\n console.log('');\n console.log(chalk.green(`Configuration saved to: ${configPath}`));\n\n // Tab completions\n console.log('');\n console.log(chalk.green('Tab Completions'));\n const installCompletions = await confirm({\n message: 'Set up tab completions?',\n default: true,\n });\n\n if (installCompletions) {\n const results = setupCompletions();\n if (results.length > 0) {\n printCompletionResults(results);\n console.log('');\n console.log(\n chalk.gray(' Restart your shell for completions to take effect.'),\n );\n } else {\n printManualInstructions();\n }\n }\n\n console.log('');\n console.log(\n chalk.cyan('You\\'re all set! Try: work tree <project> <branch>'),\n );\n console.log('');\n },\n};\n","import fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport os from 'node:os';\r\nimport chalk from 'chalk';\r\nimport spawn from 'cross-spawn';\r\n\r\nexport interface ShellProfile {\r\n shell: string;\r\n profilePath: string;\r\n completionLine: string;\r\n}\r\n\r\nexport type InstallResult =\r\n | 'installed'\r\n | 'created-file'\r\n | 'already-exists'\r\n | 'error';\r\n\r\nexport interface CompletionResult {\r\n profile: ShellProfile;\r\n status: InstallResult;\r\n error?: string;\r\n}\r\n\r\nconst PS_COMPLETION_LINE =\r\n 'work completion --shell powershell | Out-String | Invoke-Expression';\r\nconst BASH_COMPLETION_LINE = 'eval \"$(work completion)\"';\r\nconst ZSH_COMPLETION_LINE = 'eval \"$(work completion --shell zsh)\"';\r\nconst FISH_COMPLETION_SCRIPT = `# work tab completions\r\nfunction __work_complete\r\n set -l cmd (commandline -opc)\r\n set -l cur (commandline -ct)\r\n work --get-yargs-completions $cmd $cur 2>/dev/null\r\nend\r\n\r\ncomplete -c work -f -a '(__work_complete)'`;\r\nconst MARKER = '# work tab completions';\r\nconst LEGACY_MARKER = '# work2 tab completions';\r\n\r\n/** Get the Windows Documents folder, handling OneDrive redirection. */\r\nfunction getWindowsDocumentsFolder(): string | null {\r\n // Try pwsh first, fall back to powershell.exe\r\n for (const exe of ['pwsh', 'powershell']) {\r\n const result = spawn.sync(\r\n exe,\r\n ['-NoProfile', '-Command', \"[Environment]::GetFolderPath('MyDocuments')\"],\r\n {\r\n encoding: 'utf-8',\r\n stdio: ['pipe', 'pipe', 'pipe'],\r\n windowsHide: true,\r\n },\r\n );\r\n if (result.status === 0 && result.stdout) {\r\n const dir = result.stdout.toString().trim();\r\n if (dir) return dir;\r\n }\r\n }\r\n return null;\r\n}\r\n\r\n/** Check if an executable exists on PATH. */\r\nfunction executableExists(name: string): boolean {\r\n const cmd = process.platform === 'win32' ? 'where' : 'which';\r\n const result = spawn.sync(cmd, [name], {\r\n encoding: 'utf-8',\r\n stdio: ['pipe', 'pipe', 'pipe'],\r\n windowsHide: true,\r\n });\r\n return result.status === 0;\r\n}\r\n\r\n/** Detect available shell profiles for completion installation. */\r\nexport function detectShellProfiles(): ShellProfile[] {\r\n const profiles: ShellProfile[] = [];\r\n\r\n if (process.platform === 'win32') {\r\n const docs = getWindowsDocumentsFolder();\r\n if (!docs) return profiles;\r\n\r\n if (executableExists('pwsh')) {\r\n profiles.push({\r\n shell: 'PowerShell 7',\r\n profilePath: path.join(\r\n docs,\r\n 'PowerShell',\r\n 'Microsoft.PowerShell_profile.ps1',\r\n ),\r\n completionLine: PS_COMPLETION_LINE,\r\n });\r\n }\r\n\r\n if (executableExists('powershell')) {\r\n profiles.push({\r\n shell: 'PowerShell 5.1',\r\n profilePath: path.join(\r\n docs,\r\n 'WindowsPowerShell',\r\n 'Microsoft.PowerShell_profile.ps1',\r\n ),\r\n completionLine: PS_COMPLETION_LINE,\r\n });\r\n }\r\n } else {\r\n const shell = process.env.SHELL ?? '';\r\n const home = os.homedir();\r\n\r\n if (shell.includes('fish')) {\r\n // Fish uses a dedicated completions directory\r\n const fishConfigDir = process.env.XDG_CONFIG_HOME\r\n ? path.join(process.env.XDG_CONFIG_HOME, 'fish')\r\n : path.join(home, '.config', 'fish');\r\n profiles.push({\r\n shell: 'Fish',\r\n profilePath: path.join(fishConfigDir, 'completions', 'work.fish'),\r\n completionLine: FISH_COMPLETION_SCRIPT,\r\n });\r\n } else if (shell.includes('zsh')) {\r\n profiles.push({\r\n shell: 'Zsh',\r\n profilePath: path.join(home, '.zshrc'),\r\n completionLine: ZSH_COMPLETION_LINE,\r\n });\r\n } else if (shell.includes('bash')) {\r\n profiles.push({\r\n shell: 'Bash',\r\n profilePath: path.join(home, '.bashrc'),\r\n completionLine: BASH_COMPLETION_LINE,\r\n });\r\n }\r\n }\r\n\r\n return profiles;\r\n}\r\n\r\n/**\r\n * Strip a legacy `work2` completion block from a non-fish profile. Matches the\r\n * marker line and the next line (the install command). Returns content unchanged\r\n * if no legacy block is present.\r\n */\r\nfunction stripLegacyBlock(content: string): string {\r\n if (!content.includes(LEGACY_MARKER)) return content;\r\n const lines = content.split(/\\r?\\n/);\r\n const out: string[] = [];\r\n for (let i = 0; i < lines.length; i++) {\r\n if (lines[i].trim() === LEGACY_MARKER) {\r\n // Skip marker line and the following install line (if present)\r\n if (i + 1 < lines.length && /work2\\b/.test(lines[i + 1])) i++;\r\n continue;\r\n }\r\n out.push(lines[i]);\r\n }\r\n return out.join('\\n').replace(/\\n{3,}/g, '\\n\\n');\r\n}\r\n\r\n/** Idempotently install the completion line into a shell profile. */\r\nexport function installCompletionLine(profile: ShellProfile): CompletionResult {\r\n try {\r\n let createdFile = false;\r\n const isFish = profile.shell === 'Fish';\r\n\r\n let existingContent = '';\r\n if (fs.existsSync(profile.profilePath)) {\r\n existingContent = fs.readFileSync(profile.profilePath, 'utf-8');\r\n if (existingContent.includes(MARKER)) {\r\n return { profile, status: 'already-exists' };\r\n }\r\n } else {\r\n createdFile = true;\r\n }\r\n\r\n // Ensure parent directory exists\r\n fs.mkdirSync(path.dirname(profile.profilePath), { recursive: true });\r\n\r\n if (isFish) {\r\n // Fish: write the complete completion script to its own file\r\n fs.writeFileSync(profile.profilePath, profile.completionLine + '\\n');\r\n } else {\r\n // Bash/Zsh/PowerShell: strip any legacy `work2` block, then append new one.\r\n const cleaned = stripLegacyBlock(existingContent);\r\n const snippet = `\\n${MARKER}\\n${profile.completionLine}\\n`;\r\n fs.writeFileSync(profile.profilePath, cleaned + snippet);\r\n }\r\n\r\n return { profile, status: createdFile ? 'created-file' : 'installed' };\r\n } catch (err) {\r\n return {\r\n profile,\r\n status: 'error',\r\n error: err instanceof Error ? err.message : String(err),\r\n };\r\n }\r\n}\r\n\r\n/** Detect shells and install completions into each profile. */\r\nexport function setupCompletions(): CompletionResult[] {\r\n const profiles = detectShellProfiles();\r\n return profiles.map((p) => installCompletionLine(p));\r\n}\r\n\r\n/** Print colored results for each completion installation. */\r\nexport function printCompletionResults(results: CompletionResult[]): void {\r\n for (const r of results) {\r\n const label = `${r.profile.shell} (${r.profile.profilePath})`;\r\n\r\n switch (r.status) {\r\n case 'installed':\r\n console.log(chalk.green(` ✓ ${label} — completions added`));\r\n break;\r\n case 'created-file':\r\n console.log(\r\n chalk.green(` ✓ ${label} — profile created with completions`),\r\n );\r\n break;\r\n case 'already-exists':\r\n console.log(chalk.gray(` · ${label} — already has completions`));\r\n break;\r\n case 'error':\r\n console.log(chalk.red(` ✗ ${label} — ${r.error}`));\r\n break;\r\n }\r\n }\r\n}\r\n\r\n/** Print manual instructions when no shells are detected. */\r\nexport function printManualInstructions(): void {\r\n console.log(\r\n chalk.yellow(\r\n ' Could not detect shell profiles. Add completions manually:',\r\n ),\r\n );\r\n console.log('');\r\n console.log(chalk.gray(' PowerShell — add to $PROFILE:'));\r\n console.log(` ${PS_COMPLETION_LINE}`);\r\n console.log('');\r\n console.log(chalk.gray(' Bash/Zsh — add to ~/.bashrc or ~/.zshrc:'));\r\n console.log(` ${BASH_COMPLETION_LINE}`);\r\n console.log('');\r\n console.log(chalk.gray(' Fish — run:'));\r\n console.log(` work completion --shell fish > ~/.config/fish/completions/work.fish`);\r\n}\r\n","import fs from 'node:fs';\nimport chalk from 'chalk';\nimport type { CommandModule } from 'yargs';\nimport { ensureConfig } from '../core/config.js';\nimport { resolveProjectTarget, getAllTargetNames, resolveFromCwd } from '../core/resolve.js';\nimport { setupWorktree } from '../core/worktree.js';\nimport { getAiTool } from '../core/ai-launcher.js';\nimport { getCurrentBranch } from '../core/git.js';\nimport { upsertSession } from '../core/history.js';\nimport { openVSCode, launchAi } from '../utils/platform.js';\nimport { parseBaseSpec, isEmptyBaseSpec, BaseSpecError } from '../core/base-spec.js';\n\nexport const treeCommand: CommandModule = {\n command: ['tree [target] [branch]', 't [target] [branch]'],\n describe: 'Create or switch to a worktree and launch the configured AI tool',\n builder: (yargs) =>\n yargs\n .showHelpOnFail(true)\n .positional('target', {\n describe: 'Project alias or group name',\n type: 'string',\n })\n .positional('branch', {\n describe: 'Branch name (e.g., feature/login). Omit to work on the base repo.',\n type: 'string',\n })\n .option('here', {\n describe: 'Infer target and branch from the current worktree directory',\n type: 'boolean',\n default: false,\n })\n .option('open', {\n describe: 'Open VS Code in the worktree after creation',\n type: 'boolean',\n default: false,\n })\n .option('unsafe', {\n describe: 'Launch the AI tool with its skip-permissions flag (default: --dangerously-skip-permissions)',\n type: 'boolean',\n default: false,\n })\n .option('base', {\n describe:\n 'Base branch to fork from instead of HEAD. Repeatable. Use a bare branch (--base dev) for all repos, or alias=branch (--base backend=dev --base frontend=feat/x) for per-repo bases in a group.',\n type: 'string',\n })\n .option('prompt', {\n describe: 'Initial prompt to send to the AI tool on startup',\n type: 'string',\n })\n .option('prompt-file', {\n describe: 'File containing the initial prompt (deleted after reading)',\n type: 'string',\n })\n .option('jira-key', {\n describe: 'Link a Jira issue key to this worktree session',\n type: 'string',\n hidden: true,\n })\n .option('setup-only', {\n describe: 'Create worktree without launching the AI tool (used by dashboard)',\n type: 'boolean',\n default: false,\n hidden: true,\n }),\n handler: async (argv) => {\n let targetName = argv.target as string | undefined;\n let branchName = argv.branch as string | undefined;\n const here = argv.here as boolean;\n const open = argv.open as boolean;\n const unsafe = argv.unsafe as boolean;\n const setupOnly = argv['setup-only'] as boolean;\n let baseSpec;\n try {\n baseSpec = parseBaseSpec(argv.base as string | string[] | undefined);\n } catch (err) {\n if (err instanceof BaseSpecError) {\n console.error(err.message);\n process.exitCode = 1;\n return;\n }\n throw err;\n }\n const jiraKey = argv['jira-key'] as string | undefined;\n const promptFile = argv['prompt-file'] as string | undefined;\n let initialPrompt = argv.prompt as string | undefined;\n\n // --prompt-file takes precedence: read and delete the temp file\n if (promptFile) {\n try {\n initialPrompt = fs.readFileSync(promptFile, 'utf-8');\n fs.unlinkSync(promptFile);\n } catch {\n // ignore — fall through to --prompt or no prompt\n }\n }\n\n const config = ensureConfig();\n\n // --here: infer target and branch from the current worktree directory\n if (here) {\n if (targetName) {\n console.error('Cannot combine --here with an explicit target.');\n process.exitCode = 1;\n return;\n }\n const inferred = resolveFromCwd(config, process.cwd());\n if ('error' in inferred) {\n console.error(inferred.error);\n process.exitCode = 1;\n return;\n }\n targetName = inferred.target;\n branchName = inferred.isBaseRepo ? undefined : inferred.branch;\n console.log(\n chalk.cyan(\n `Resolved from current directory: ${targetName}${branchName ? ' @ ' + branchName : ' (base repo)'}`,\n ),\n );\n } else if (!targetName) {\n console.error(\n 'Specify a target, or use --here to infer it from the current directory.',\n );\n process.exitCode = 1;\n return;\n }\n\n // --base requires a branch name\n if (!isEmptyBaseSpec(baseSpec) && !branchName) {\n console.error('--base requires a branch name');\n console.log(\n chalk.yellow(`Usage: work tree ${targetName} <branch> --base <base>`),\n );\n process.exitCode = 1;\n return;\n }\n\n // Resolve project target (for validation and base-repo handling)\n const target = resolveProjectTarget(targetName, config);\n if (!target) {\n const allNames = getAllTargetNames(config);\n console.error(`Project or group not found: ${targetName}`);\n console.log(chalk.yellow(`Available: ${allNames.join(', ')}`));\n console.log(\n chalk.yellow(\n 'Add a new project with: work config add <alias> <path>',\n ),\n );\n process.exitCode = 1;\n return;\n }\n\n // No branch specified — work directly on the base repo\n if (!branchName) {\n if (target.isGroup) {\n console.error('Branch is required for group targets.');\n console.log(chalk.yellow(`Usage: work tree ${targetName} <branch>`));\n process.exitCode = 1;\n return;\n }\n\n const repoPath = config.repos[targetName];\n if (!repoPath) {\n console.error(`Repository path not configured for: ${targetName}`);\n process.exitCode = 1;\n return;\n }\n if (!fs.existsSync(repoPath)) {\n console.error(`Repository path does not exist: ${repoPath}`);\n process.exitCode = 1;\n return;\n }\n\n console.log(chalk.cyan(`Working on base repo: ${targetName}`));\n console.log(`Repo path: ${repoPath}`);\n\n const currentBranch = getCurrentBranch(repoPath) ?? '(detached)';\n await upsertSession(targetName, false, currentBranch, [repoPath], jiraKey);\n\n if (open) openVSCode(repoPath);\n if (!setupOnly) {\n const tool = getAiTool(config);\n console.log(`Starting ${tool.cmd}...`);\n launchAi(repoPath, tool, { unsafe, initialPrompt });\n }\n return;\n }\n\n // Create/switch worktree via shared core logic\n const result = await setupWorktree(targetName, branchName, config, baseSpec, jiraKey);\n if (!result) {\n process.exitCode = 1;\n return;\n }\n\n if (open) {\n for (const p of result.paths) {\n openVSCode(p);\n }\n }\n\n console.log(`Worktree path: ${result.launchDir}`);\n if (!setupOnly) {\n const tool = getAiTool(config);\n console.log(`Starting ${tool.cmd}...`);\n launchAi(result.launchDir, tool, { unsafe, initialPrompt }, result.port);\n }\n },\n};\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { WorkConfig } from './config.js';\nimport {\n getWorktreeRoot,\n getMainRepoRoot,\n getCurrentBranch,\n} from './git.js';\n\nexport interface ProjectTarget {\n isGroup: boolean;\n name: string;\n repoAliases: string[];\n}\n\n/**\n * Resolve whether a name is a group or single repo.\n * Returns null if the name is not found in either.\n */\nexport function resolveProjectTarget(\n name: string,\n config: WorkConfig,\n): ProjectTarget | null {\n // Check if it's a group\n if (name in config.groups) {\n return {\n isGroup: true,\n name,\n repoAliases: [...config.groups[name]],\n };\n }\n\n // Check if it's a repo\n if (name in config.repos) {\n return {\n isGroup: false,\n name,\n repoAliases: [name],\n };\n }\n\n return null;\n}\n\n/** Get all available project/group names. */\nexport function getAllTargetNames(config: WorkConfig): string[] {\n return [\n ...Object.keys(config.repos),\n ...Object.keys(config.groups),\n ];\n}\n\n/**\n * Match a worktree path against the configured worktrees root to determine the\n * target. The path must be strictly under `worktreesRoot`. The first path\n * segment is either a group name (checked first) or a repo folder basename.\n *\n * Both paths are canonicalized via `realpath` before comparison: git's\n * `--show-toplevel` returns a fully symlink-resolved path, while\n * `config.worktreesRoot` may contain symlinked components (e.g. macOS\n * `/tmp` → `/private/tmp`). Without this they would mismatch. `realpath` is\n * injectable for testability and defaults to fs.realpathSync (falling back to\n * path.resolve if it throws). Returns null if no match.\n */\nexport function matchTargetByWorktreePath(\n config: WorkConfig,\n absWorktreePath: string,\n realpath: (p: string) => string = safeRealpath,\n): { target: string; isGroup: boolean } | null {\n const root = realpath(config.worktreesRoot);\n const wt = realpath(absWorktreePath);\n\n const rel = path.relative(root, wt);\n // Must be strictly under root (not the root itself, not outside it).\n if (!rel || rel === '' || rel.startsWith('..') || path.isAbsolute(rel)) {\n return null;\n }\n\n const segments = rel.split(path.sep).filter(Boolean);\n if (segments.length === 0) return null;\n const firstSeg = segments[0];\n\n // Groups checked first.\n if (firstSeg in config.groups) {\n return { target: firstSeg, isGroup: true };\n }\n\n for (const alias of Object.keys(config.repos)) {\n if (path.basename(config.repos[alias]) === firstSeg) {\n return { target: alias, isGroup: false };\n }\n }\n\n return null;\n}\n\n/**\n * Reverse-map a main-repo root path to a single-repo alias by comparing\n * canonicalized paths. `realpath` is injectable for testability and defaults\n * to fs.realpathSync (falling back to path.resolve if it throws).\n * Cannot resolve a group from git alone. Returns null if no match.\n */\nexport function matchTargetByRepoRoot(\n config: WorkConfig,\n repoRoot: string,\n realpath: (p: string) => string = (p) => {\n try {\n return fs.realpathSync(p);\n } catch {\n return path.resolve(p);\n }\n },\n): { target: string; isGroup: false } | null {\n const target = realpath(repoRoot);\n for (const alias of Object.keys(config.repos)) {\n if (realpath(config.repos[alias]) === target) {\n return { target: alias, isGroup: false };\n }\n }\n return null;\n}\n\n/** Canonicalize a path via fs.realpathSync, falling back to path.resolve. */\nfunction safeRealpath(p: string): string {\n try {\n return fs.realpathSync(p);\n } catch {\n return path.resolve(p);\n }\n}\n\n/**\n * Resolve the target, branch, and base-repo status from the current working\n * directory's git worktree. Uses git. Returns an `{ error }` object on failure.\n */\nexport function resolveFromCwd(\n config: WorkConfig,\n cwd: string,\n):\n | { target: string; isGroup: boolean; branch: string; isBaseRepo: boolean }\n | { error: string } {\n const worktreeRoot = getWorktreeRoot(cwd);\n if (!worktreeRoot) {\n return { error: 'Not inside a git repository.' };\n }\n\n const branch = getCurrentBranch(worktreeRoot);\n if (!branch) {\n return {\n error: 'Current worktree is in a detached HEAD state; cannot infer branch.',\n };\n }\n\n let match = matchTargetByWorktreePath(config, worktreeRoot);\n\n if (!match) {\n const mainRoot = getMainRepoRoot(worktreeRoot);\n if (mainRoot) {\n match = matchTargetByRepoRoot(config, mainRoot);\n }\n }\n\n if (!match) {\n const aliases = Object.keys(config.repos).join(', ');\n return {\n error: `Current directory does not belong to any configured work repo or group. Configured repos: ${aliases}`,\n };\n }\n\n let isBaseRepo = false;\n if (!match.isGroup) {\n const repoPath = config.repos[match.target];\n if (repoPath && safeRealpath(repoPath) === safeRealpath(worktreeRoot)) {\n isBaseRepo = true;\n }\n }\n\n return {\n target: match.target,\n isGroup: match.isGroup,\n branch,\n isBaseRepo,\n };\n}\n","import fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport chalk from 'chalk';\r\nimport { debug } from './logger.js';\r\nimport type { WorkConfig } from './config.js';\r\nimport { getConfigDir } from './config.js';\r\nimport { resolveProjectTarget } from './resolve.js';\r\nimport { upsertSessionWithPort } from './history.js';\r\nimport {\r\n git,\r\n parseWorktreeList,\r\n localBranchExists,\r\n remoteBranchExists,\r\n isGitRepo,\r\n getCurrentBranch,\r\n getStatus,\r\n getUnpushedCommits,\r\n} from './git.js';\r\nimport { copyConfigFiles } from './copy-files.js';\r\nimport {\r\n type BaseSpec,\r\n baseForAlias,\r\n baseSpecOverrideAliases,\r\n isEmptyBaseSpec,\r\n toBaseSpec,\r\n} from './base-spec.js';\r\n\r\n/**\r\n * Create a single git worktree for one repo.\r\n * Returns true on success, false on failure.\r\n *\r\n * When `baseBranch` is provided, the new branch is created from that base\r\n * instead of HEAD. Only valid for new branches — errors if the target branch\r\n * already exists locally or on remote.\r\n */\r\nexport function createSingleWorktree(\r\n repoPath: string,\r\n worktreePath: string,\r\n branchName: string,\r\n config: WorkConfig,\r\n baseBranch?: string,\r\n): boolean {\r\n debug('createSingleWorktree', { repoPath, worktreePath, branchName, baseBranch });\r\n\r\n // Check if the worktree already exists at the target path (idempotent re-run)\r\n if (fs.existsSync(worktreePath)) {\r\n if (isGitRepo(worktreePath)) {\r\n const currentBranch = getCurrentBranch(worktreePath);\r\n if (currentBranch === branchName) {\r\n console.log(\r\n chalk.yellow(` Worktree already exists at: ${worktreePath}`),\r\n );\r\n return true;\r\n }\r\n }\r\n }\r\n\r\n // Check if the branch is already checked out in another worktree\r\n const worktrees = parseWorktreeList(repoPath);\r\n const existingForBranch = worktrees.find(\r\n (wt) => wt.branch === branchName && wt.path !== worktreePath,\r\n );\r\n\r\n if (existingForBranch) {\r\n console.log(\r\n chalk.red(\r\n ` Branch '${branchName}' is already checked out in a worktree at: ${existingForBranch.path}`,\r\n ),\r\n );\r\n console.log(\r\n chalk.red(' Remove that worktree first, or use the existing one.'),\r\n );\r\n return false;\r\n }\r\n\r\n // Create parent directory\r\n const parentDir = path.dirname(worktreePath);\r\n fs.mkdirSync(parentDir, { recursive: true });\r\n\r\n // Fetch remote refs first so origin/* is up to date even if pull fails below\r\n git(['fetch', '--quiet'], repoPath);\r\n\r\n // Pull latest changes for current branch in main repo\r\n const baseRepoBranch = getCurrentBranch(repoPath);\r\n const baseBranchLabel = baseRepoBranch ?? '(detached HEAD)';\r\n console.log(` Pulling latest changes for main repo (on ${baseBranchLabel})...`);\r\n if (baseRepoBranch && !['master', 'main', 'dev'].includes(baseRepoBranch)) {\r\n console.log(\r\n chalk.yellow(\r\n ` ⚠ Warning: base repo is on '${baseRepoBranch}', not master/main/dev`,\r\n ),\r\n );\r\n }\r\n const baseRepoPull = git(['pull', '--quiet'], repoPath);\r\n const baseRepoPullFailed = baseRepoPull.exitCode !== 0;\r\n if (baseRepoPullFailed) {\r\n console.log(\r\n chalk.yellow(\r\n ` ⚠ Could not pull '${baseBranchLabel}' (uncommitted changes, conflicts, or no upstream).`,\r\n ),\r\n );\r\n const firstErrLine = baseRepoPull.stderr.split('\\n')[0];\r\n if (firstErrLine) console.log(chalk.gray(` ${firstErrLine}`));\r\n }\r\n\r\n const hasLocal = localBranchExists(branchName, repoPath);\r\n const hasRemote = remoteBranchExists(branchName, repoPath);\r\n\r\n // --base requires a brand-new branch\r\n if (baseBranch && (hasLocal || hasRemote)) {\r\n console.log(\r\n chalk.red(\r\n ` Cannot use --base: branch '${branchName}' already exists ${hasLocal ? 'locally' : 'on remote'}`,\r\n ),\r\n );\r\n return false;\r\n }\r\n\r\n // Pull latest changes if branch exists locally\r\n if (hasLocal) {\r\n console.log(` Pulling latest changes for ${branchName}...`);\r\n const prevBranch = getCurrentBranch(repoPath);\r\n git(['checkout', branchName, '--quiet'], repoPath);\r\n const branchPull = git(['pull', '--quiet'], repoPath);\r\n if (branchPull.exitCode !== 0) {\r\n console.log(\r\n chalk.yellow(\r\n ` ⚠ Could not pull '${branchName}'. Worktree may be behind origin.`,\r\n ),\r\n );\r\n const firstErrLine = branchPull.stderr.split('\\n')[0];\r\n if (firstErrLine) console.log(chalk.gray(` ${firstErrLine}`));\r\n }\r\n if (prevBranch) {\r\n git(['checkout', prevBranch, '--quiet'], repoPath);\r\n }\r\n }\r\n\r\n // Create worktree\r\n let result;\r\n let branchSource: 'local' | 'remote' | 'new' = 'new';\r\n if (hasLocal || hasRemote) {\r\n if (hasRemote && !hasLocal) {\r\n branchSource = 'remote';\r\n result = git(\r\n [\r\n 'worktree',\r\n 'add',\r\n worktreePath,\r\n '-b',\r\n branchName,\r\n '--track',\r\n `origin/${branchName}`,\r\n ],\r\n repoPath,\r\n );\r\n } else {\r\n branchSource = 'local';\r\n result = git(\r\n ['worktree', 'add', worktreePath, branchName],\r\n repoPath,\r\n );\r\n }\r\n } else if (baseBranch) {\r\n // Validate the base branch exists\r\n const baseLocal = localBranchExists(baseBranch, repoPath);\r\n const baseRemote = remoteBranchExists(baseBranch, repoPath);\r\n\r\n if (!baseLocal && !baseRemote) {\r\n console.log(\r\n chalk.red(\r\n ` Base branch '${baseBranch}' does not exist locally or on remote`,\r\n ),\r\n );\r\n return false;\r\n }\r\n\r\n const baseRef = baseLocal ? baseBranch : `origin/${baseBranch}`;\r\n result = git(\r\n ['worktree', 'add', worktreePath, '-b', branchName, baseRef],\r\n repoPath,\r\n );\r\n } else {\r\n // If pulling the base repo branch failed, use origin/<baseRepoBranch> as the\r\n // source so the new branch isn't created from a stale local HEAD.\r\n const fallbackToRemote =\r\n baseRepoPullFailed &&\r\n !!baseRepoBranch &&\r\n remoteBranchExists(baseRepoBranch, repoPath);\r\n if (fallbackToRemote) {\r\n console.log(\r\n chalk.cyan(` Using origin/${baseRepoBranch} as base (local '${baseRepoBranch}' is stale)`),\r\n );\r\n result = git(\r\n ['worktree', 'add', worktreePath, '-b', branchName, `origin/${baseRepoBranch}`],\r\n repoPath,\r\n );\r\n } else {\r\n result = git(\r\n ['worktree', 'add', worktreePath, '-b', branchName],\r\n repoPath,\r\n );\r\n }\r\n }\r\n\r\n if (result.exitCode !== 0) {\r\n debug('git worktree add failed', { exitCode: result.exitCode, stdout: result.stdout, stderr: result.stderr });\r\n console.log(chalk.red(' Failed to create worktree'));\r\n if (result.stderr) {\r\n console.log(chalk.red(` ${result.stderr}`));\r\n }\r\n return false;\r\n }\r\n\r\n // Copy configuration files from main repo\r\n if (config.copyFiles && config.copyFiles.length > 0) {\r\n copyConfigFiles(repoPath, worktreePath, config.copyFiles);\r\n }\r\n\r\n if (branchSource === 'remote') {\r\n console.log(chalk.cyan(` Tracking remote branch origin/${branchName}`));\r\n } else if (branchSource === 'local') {\r\n console.log(chalk.cyan(` Using existing local branch ${branchName}`));\r\n } else if (baseBranch) {\r\n console.log(chalk.cyan(` Created new branch ${branchName} from ${baseBranch}`));\r\n } else {\r\n console.log(chalk.cyan(` Created new branch ${branchName}`));\r\n }\r\n\r\n console.log(chalk.green(` Created worktree: ${worktreePath}`));\r\n return true;\r\n}\r\n\r\n/**\r\n * Remove a single git worktree.\r\n * Returns true on success, false if blocked (uncommitted/unpushed changes).\r\n */\r\nexport function removeSingleWorktree(\r\n repoPath: string,\r\n worktreePath: string,\r\n branchName: string,\r\n force: boolean,\r\n): boolean {\r\n if (!fs.existsSync(worktreePath)) {\r\n console.log(\r\n chalk.yellow(` Worktree does not exist at: ${worktreePath}`),\r\n );\r\n return true; // Nothing to remove is success\r\n }\r\n\r\n // Check if it's a valid git worktree\r\n if (!isGitRepo(worktreePath)) {\r\n fs.rmSync(worktreePath, { recursive: true, force: true });\r\n git(['worktree', 'prune'], repoPath);\r\n console.log(` Removed invalid worktree directory: ${worktreePath}`);\r\n return true;\r\n }\r\n\r\n if (!force) {\r\n // Check for uncommitted changes\r\n const status = getStatus(worktreePath);\r\n if (status) {\r\n console.log(\r\n chalk.yellow(` Uncommitted changes in: ${worktreePath}`),\r\n );\r\n console.log(status);\r\n return false;\r\n }\r\n\r\n // Check for unpushed commits\r\n const unpushed = getUnpushedCommits(worktreePath);\r\n if (unpushed) {\r\n console.log(\r\n chalk.yellow(` Unpushed commits in: ${worktreePath}`),\r\n );\r\n console.log(unpushed);\r\n return false;\r\n }\r\n }\r\n\r\n const args = force\r\n ? ['worktree', 'remove', worktreePath, '--force']\r\n : ['worktree', 'remove', worktreePath];\r\n\r\n const result = git(args, repoPath);\r\n\r\n if (result.exitCode === 0) {\r\n console.log(chalk.green(` Removed worktree: ${worktreePath}`));\r\n return true;\r\n } else {\r\n console.log(chalk.red(` Failed to remove worktree: ${worktreePath}`));\r\n if (result.stderr) {\r\n console.log(chalk.red(` ${result.stderr}`));\r\n }\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Result of setupWorktree — everything needed to launch an AI session.\r\n */\r\nexport interface WorktreeSetupResult {\r\n /** Directory to launch the AI tool in. */\r\n launchDir: string;\r\n /** All worktree paths created/found (for session tracking). */\r\n paths: string[];\r\n /** Whether the target is a group. */\r\n isGroup: boolean;\r\n /** Stable dev-server port allocated to this worktree, if allocation succeeded. */\r\n port?: number;\r\n}\r\n\r\n/**\r\n * High-level worktree setup: resolve target, create worktree(s), copy group\r\n * CLAUDE.md, and record the session. Used by both the CLI command and the TUI.\r\n *\r\n * Returns the setup result on success, or null on failure.\r\n */\r\nexport async function setupWorktree(\r\n targetName: string,\r\n branchName: string,\r\n config: WorkConfig,\r\n base?: string | BaseSpec,\r\n jiraKey?: string,\r\n): Promise<WorktreeSetupResult | null> {\r\n const spec = toBaseSpec(base);\r\n debug('setupWorktree', { targetName, branchName, spec, jiraKey });\r\n const target = resolveProjectTarget(targetName, config);\r\n if (!target) { debug('setupWorktree: target not found', targetName); return null; }\r\n\r\n const workTreeDirName = branchName.replace(/\\//g, '-');\r\n\r\n if (target.isGroup) {\r\n return setupGroupWorktree(target.name, target.repoAliases, branchName, workTreeDirName, config, spec, jiraKey);\r\n } else {\r\n return setupSingleWorktree(targetName, branchName, workTreeDirName, config, spec, jiraKey);\r\n }\r\n}\r\n\r\nasync function setupGroupWorktree(\r\n groupName: string,\r\n repoAliases: string[],\r\n branchName: string,\r\n workTreeDirName: string,\r\n config: WorkConfig,\r\n spec: BaseSpec,\r\n jiraKey?: string,\r\n): Promise<WorktreeSetupResult | null> {\r\n const groupWorktreePath = path.join(config.worktreesRoot, groupName, workTreeDirName);\r\n\r\n // Pre-validate --base across all repos before creating anything. Each repo\r\n // resolves its own base: a per-repo override (`alias=branch`) if present,\r\n // otherwise the bare default.\r\n if (!isEmptyBaseSpec(spec)) {\r\n const unknownAliases = baseSpecOverrideAliases(spec).filter(\r\n (a) => !repoAliases.includes(a),\r\n );\r\n if (unknownAliases.length > 0) {\r\n console.error(\r\n `--base names repo(s) not in group '${groupName}': ${unknownAliases.join(', ')}. Group repos: ${repoAliases.join(', ')}`,\r\n );\r\n return null;\r\n }\r\n\r\n const missingBase: string[] = [];\r\n const branchExists: string[] = [];\r\n\r\n for (const alias of repoAliases) {\r\n const repoPath = config.repos[alias];\r\n const repoBase = baseForAlias(spec, alias);\r\n if (!repoBase) continue; // no base applied to this repo → forks HEAD\r\n if (!localBranchExists(repoBase, repoPath) && !remoteBranchExists(repoBase, repoPath)) {\r\n missingBase.push(`${alias} (${repoBase})`);\r\n }\r\n if (localBranchExists(branchName, repoPath) || remoteBranchExists(branchName, repoPath)) {\r\n branchExists.push(alias);\r\n }\r\n }\r\n\r\n if (missingBase.length > 0) {\r\n console.error(`Base branch not found in: ${missingBase.join(', ')}`);\r\n return null;\r\n }\r\n if (branchExists.length > 0) {\r\n console.error(`Cannot use --base: branch '${branchName}' already exists in: ${branchExists.join(', ')}`);\r\n return null;\r\n }\r\n }\r\n\r\n console.log(chalk.cyan(`Creating group worktree: ${groupName}/${branchName}`));\r\n console.log(chalk.gray(`Directory: ${groupWorktreePath}`));\r\n console.log('');\r\n\r\n fs.mkdirSync(groupWorktreePath, { recursive: true });\r\n\r\n const createdWorktrees: Array<{ repoPath: string; worktreePath: string }> = [];\r\n // Per-repo fork point, keyed by worktree path (matches the session `paths`).\r\n const baseBranches: Record<string, string> = {};\r\n\r\n for (const alias of repoAliases) {\r\n const repoPath = config.repos[alias];\r\n const repoName = path.basename(repoPath);\r\n const subWorktreePath = path.join(groupWorktreePath, repoName);\r\n const repoBase = baseForAlias(spec, alias);\r\n\r\n console.log(chalk.cyan(`[${alias}] (${repoName}):`));\r\n const success = createSingleWorktree(repoPath, subWorktreePath, branchName, config, repoBase);\r\n\r\n if (success) {\r\n createdWorktrees.push({ repoPath, worktreePath: subWorktreePath });\r\n if (repoBase) baseBranches[subWorktreePath] = repoBase;\r\n } else {\r\n // Rollback\r\n console.log('');\r\n console.log(chalk.yellow('Rolling back created worktrees due to failure...'));\r\n for (const wt of createdWorktrees) {\r\n removeSingleWorktree(wt.repoPath, wt.worktreePath, branchName, true);\r\n }\r\n try {\r\n if (fs.readdirSync(groupWorktreePath).length === 0) {\r\n fs.rmSync(groupWorktreePath, { recursive: true, force: true });\r\n }\r\n } catch { /* */ }\r\n console.error('Failed to create group worktree. Changes have been rolled back.');\r\n return null;\r\n }\r\n }\r\n\r\n // Copy group CLAUDE.md\r\n const configDir = getConfigDir();\r\n const claudeMdSrc = path.join(configDir, `${groupName}.claude.md`);\r\n const claudeMdDest = path.join(groupWorktreePath, 'CLAUDE.md');\r\n\r\n if (fs.existsSync(claudeMdSrc)) {\r\n fs.copyFileSync(claudeMdSrc, claudeMdDest);\r\n console.log('');\r\n console.log(chalk.green('Copied group CLAUDE.md to worktree root'));\r\n } else {\r\n console.log('');\r\n console.log(chalk.yellow(`Warning: Group CLAUDE.md not found at ${claudeMdSrc}`));\r\n console.log(chalk.yellow(`Run 'work config regengroup ${groupName}' to generate it.`));\r\n }\r\n\r\n const allPaths = createdWorktrees.map((wt) => wt.worktreePath);\r\n // Representative base for the single-line \"vs X\" badge: the explicit\r\n // default, else a per-repo value only when every repo shares it.\r\n const distinctBases = [...new Set(Object.values(baseBranches))];\r\n const representativeBase =\r\n spec.default ?? (distinctBases.length === 1 ? distinctBases[0] : undefined);\r\n const { port } = await upsertSessionWithPort(\r\n groupName,\r\n true,\r\n branchName,\r\n allPaths,\r\n config,\r\n jiraKey,\r\n representativeBase,\r\n baseBranches,\r\n );\r\n\r\n console.log('');\r\n console.log(`Branch: ${branchName}`);\r\n if (port !== undefined) console.log(chalk.gray(`Dev-server port: ${port}`));\r\n\r\n return { launchDir: groupWorktreePath, paths: allPaths, isGroup: true, port };\r\n}\r\n\r\nasync function setupSingleWorktree(\r\n targetName: string,\r\n branchName: string,\r\n workTreeDirName: string,\r\n config: WorkConfig,\r\n spec: BaseSpec,\r\n jiraKey?: string,\r\n): Promise<WorktreeSetupResult | null> {\r\n const repoPath = config.repos[targetName];\r\n const repoName = path.basename(repoPath);\r\n let workTreePath = path.join(config.worktreesRoot, repoName, workTreeDirName);\r\n\r\n // For a single repo, the only valid per-repo override alias is the target.\r\n const unknownAliases = baseSpecOverrideAliases(spec).filter((a) => a !== targetName);\r\n if (unknownAliases.length > 0) {\r\n console.error(\r\n `--base names repo(s) other than '${targetName}': ${unknownAliases.join(', ')}`,\r\n );\r\n return null;\r\n }\r\n const baseBranch = baseForAlias(spec, targetName);\r\n\r\n if (!fs.existsSync(repoPath)) {\r\n console.error(`Repository path does not exist: ${repoPath}`);\r\n return null;\r\n }\r\n\r\n // Check for existing worktree at any path\r\n const worktrees = parseWorktreeList(repoPath);\r\n const existing = worktrees.find(\r\n (wt) =>\r\n wt.branch === branchName &&\r\n path.resolve(wt.path) !== path.resolve(repoPath),\r\n );\r\n\r\n if (existing) {\r\n if (baseBranch) {\r\n console.error(`Cannot use --base: worktree for '${branchName}' already exists at ${existing.path}`);\r\n return null;\r\n }\r\n console.log(`Worktree already exists at: ${existing.path}`);\r\n workTreePath = existing.path;\r\n } else {\r\n const success = createSingleWorktree(repoPath, workTreePath, branchName, config, baseBranch);\r\n if (!success) return null;\r\n }\r\n\r\n const { port } = await upsertSessionWithPort(\r\n targetName,\r\n false,\r\n branchName,\r\n [workTreePath],\r\n config,\r\n jiraKey,\r\n baseBranch,\r\n baseBranch ? { [workTreePath]: baseBranch } : undefined,\r\n );\r\n\r\n console.log(`Branch: ${branchName}`);\r\n if (port !== undefined) console.log(chalk.gray(`Dev-server port: ${port}`));\r\n\r\n return { launchDir: workTreePath, paths: [workTreePath], isGroup: false, port };\r\n}\r\n\r\n/**\r\n * Remove all worktrees for a session and clean up.\r\n * Returns true if all worktrees were successfully removed.\r\n * When force=false, stops on uncommitted/unpushed changes.\r\n */\r\nexport function teardownWorktree(\r\n target: string,\r\n isGroup: boolean,\r\n branch: string,\r\n config: WorkConfig,\r\n force: boolean = true,\r\n): boolean {\r\n const workTreeDirName = branch.replace(/\\//g, '-');\r\n\r\n if (isGroup && config.groups[target]) {\r\n const aliases = config.groups[target];\r\n const groupWorktreePath = path.join(config.worktreesRoot, target, workTreeDirName);\r\n let allRemoved = true;\r\n\r\n for (const alias of aliases) {\r\n const repoPath = config.repos[alias];\r\n if (!repoPath) continue;\r\n const repoName = path.basename(repoPath);\r\n const subWorktreePath = path.join(groupWorktreePath, repoName);\r\n console.log(chalk.cyan(`[${alias}] (${repoName}):`));\r\n if (!removeSingleWorktree(repoPath, subWorktreePath, branch, force)) {\r\n allRemoved = false;\r\n }\r\n }\r\n\r\n // Clean up group CLAUDE.md and empty parent dir\r\n const claudeMd = path.join(groupWorktreePath, 'CLAUDE.md');\r\n try { if (fs.existsSync(claudeMd)) fs.unlinkSync(claudeMd); } catch { /* */ }\r\n try {\r\n if (fs.existsSync(groupWorktreePath) && fs.readdirSync(groupWorktreePath).length === 0) {\r\n fs.rmSync(groupWorktreePath, { recursive: true, force: true });\r\n console.log(chalk.green(`Cleaned up group directory: ${groupWorktreePath}`));\r\n }\r\n } catch { /* */ }\r\n\r\n return allRemoved;\r\n } else {\r\n const repoPath = config.repos[target];\r\n if (repoPath) {\r\n const worktrees = parseWorktreeList(repoPath);\r\n const wt = worktrees.find((w) => w.branch === branch);\r\n if (wt) {\r\n return removeSingleWorktree(repoPath, wt.path, branch, force);\r\n }\r\n }\r\n console.log(chalk.yellow(`No worktree found for branch '${branch}' in '${target}'.`));\r\n return false;\r\n }\r\n}\r\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { WorkConfig } from './config.js';\nimport { getConfigDir } from './config.js';\nimport { atomicWriteFile, ensureFile, withFileLock } from './fs-safe.js';\nimport { effectiveLastAccessedAt } from './claude-activity.js';\nimport { allocateFreePort } from './port-allocator.js';\n\nexport interface WorktreeSession {\n target: string;\n isGroup: boolean;\n branch: string;\n paths: string[];\n createdAt: string;\n lastAccessedAt: string;\n jiraKey?: string;\n /** Branch this worktree was forked from. Recorded when known at creation time.\n * For groups with a single shared base this is that base; with per-repo bases\n * it's the representative/default (see `baseBranches` for the per-repo map). */\n baseBranch?: string;\n /** Per-repo fork point, keyed by worktree path (same strings as `paths`).\n * Set when `work tree --base alias=branch` gives repos different bases.\n * Diff routes prefer this over `baseBranch` for a given repo. */\n baseBranches?: Record<string, string>;\n /** Stable dev-server port allocated to this worktree, exposed as $PORT. */\n port?: number;\n}\n\nexport function getHistoryPath(): string {\n return path.join(getConfigDir(), 'history.json');\n}\n\n/**\n * Back up a corrupt history file so the user can recover manually\n * instead of having it silently overwritten.\n */\nfunction backupCorruptFile(historyPath: string, reason: string): void {\n try {\n const stamp = new Date().toISOString().replace(/[:.]/g, '-');\n const backupPath = `${historyPath}.bad-${stamp}`;\n fs.copyFileSync(historyPath, backupPath);\n console.error(\n `[work] history.json was unreadable (${reason}). Corrupt copy saved to ${backupPath}`,\n );\n } catch {\n // best-effort\n }\n}\n\nexport function loadHistory(): WorktreeSession[] {\n const historyPath = getHistoryPath();\n if (!fs.existsSync(historyPath)) {\n return [];\n }\n\n let raw: string;\n try {\n raw = fs.readFileSync(historyPath, 'utf-8');\n } catch (err) {\n backupCorruptFile(historyPath, `read error: ${(err as Error).message}`);\n return [];\n }\n\n try {\n const parsed = JSON.parse(raw);\n if (!Array.isArray(parsed)) {\n backupCorruptFile(historyPath, 'not an array');\n return [];\n }\n return parsed.filter(\n (s) =>\n s &&\n typeof s.target === 'string' &&\n typeof s.branch === 'string' &&\n Array.isArray(s.paths),\n );\n } catch (err) {\n backupCorruptFile(historyPath, `parse error: ${(err as Error).message}`);\n return [];\n }\n}\n\nexport function saveHistory(sessions: WorktreeSession[]): void {\n atomicWriteFile(getHistoryPath(), JSON.stringify(sessions, null, 2));\n}\n\n/**\n * Serialize a read-modify-write sequence against other work processes.\n * Concurrent `work tree`/`remove` calls would otherwise clobber each\n * other's writes (this is the bug that wiped 60+ sessions).\n */\nasync function withHistoryLock<T>(fn: () => T | Promise<T>): Promise<T> {\n const historyPath = getHistoryPath();\n ensureFile(historyPath, '[]');\n return withFileLock(historyPath, fn);\n}\n\nfunction sessionKey(target: string, branch: string): string {\n return `${target}:${branch}`;\n}\n\nexport function findSession(\n sessions: WorktreeSession[],\n target: string,\n branch: string,\n): WorktreeSession | undefined {\n return sessions.find(\n (s) => s.target === target && s.branch === branch,\n );\n}\n\nexport async function upsertSession(\n target: string,\n isGroup: boolean,\n branch: string,\n paths: string[],\n jiraKey?: string,\n baseBranch?: string,\n port?: number,\n): Promise<void> {\n await withHistoryLock(() => {\n const sessions = loadHistory();\n const existing = findSession(sessions, target, branch);\n const now = new Date().toISOString();\n\n if (existing) {\n existing.paths = paths;\n existing.lastAccessedAt = now;\n if (jiraKey) existing.jiraKey = jiraKey;\n if (baseBranch && !existing.baseBranch) existing.baseBranch = baseBranch;\n if (port !== undefined) existing.port = port;\n } else {\n const session: WorktreeSession = {\n target,\n isGroup,\n branch,\n paths,\n createdAt: now,\n lastAccessedAt: now,\n };\n if (jiraKey) session.jiraKey = jiraKey;\n if (baseBranch) session.baseBranch = baseBranch;\n if (port !== undefined) session.port = port;\n sessions.push(session);\n }\n\n saveHistory(sessions);\n });\n}\n\n/**\n * Atomically allocate a stable dev-server port AND persist the session in a\n * single locked critical section. Allocating over `loadHistory()` and then\n * writing in a separate `upsertSession` call is a TOCTOU race: two concurrent\n * `work tree` runs would both read the same history snapshot and pick the same\n * port. Doing load + allocate + save under one `withHistoryLock` serializes\n * concurrent allocations so each gets a distinct port.\n *\n * Port allocation is best-effort: if it fails (range exhausted, etc.) the\n * session is still persisted, just without a port, and `port` comes back\n * undefined. An already-allocated port on an existing session is preserved\n * (idempotent re-runs keep their port).\n *\n * The allocation seed is `target:branch` (unique per worktree) so two repos\n * that share a branch name don't collide on the same deterministic base offset.\n */\nexport async function upsertSessionWithPort(\n target: string,\n isGroup: boolean,\n branch: string,\n paths: string[],\n config: Pick<WorkConfig, 'portRange'>,\n jiraKey?: string,\n baseBranch?: string,\n baseBranches?: Record<string, string>,\n): Promise<{ port?: number }> {\n return withHistoryLock(async () => {\n const sessions = loadHistory();\n const existing = findSession(sessions, target, branch);\n const now = new Date().toISOString();\n\n const hasPerRepo = baseBranches && Object.keys(baseBranches).length > 0;\n\n // Keep an already-assigned port; otherwise allocate against the current\n // (locked) snapshot so concurrent callers can't pick the same one.\n let port = existing?.port;\n if (port === undefined) {\n const seedKey = sessionKey(target, branch);\n try {\n port = await allocateFreePort(seedKey, config, sessions);\n } catch {\n port = undefined;\n }\n }\n\n if (existing) {\n existing.paths = paths;\n existing.lastAccessedAt = now;\n if (jiraKey) existing.jiraKey = jiraKey;\n if (baseBranch && !existing.baseBranch) existing.baseBranch = baseBranch;\n if (hasPerRepo && !existing.baseBranches) existing.baseBranches = baseBranches;\n if (port !== undefined) existing.port = port;\n } else {\n const session: WorktreeSession = {\n target,\n isGroup,\n branch,\n paths,\n createdAt: now,\n lastAccessedAt: now,\n };\n if (jiraKey) session.jiraKey = jiraKey;\n if (baseBranch) session.baseBranch = baseBranch;\n if (hasPerRepo) session.baseBranches = baseBranches;\n if (port !== undefined) session.port = port;\n sessions.push(session);\n }\n\n saveHistory(sessions);\n return { port };\n });\n}\n\nexport async function removeSession(target: string, branch: string): Promise<void> {\n await withHistoryLock(() => {\n const sessions = loadHistory();\n const filtered = sessions.filter(\n (s) => !(s.target === target && s.branch === branch),\n );\n\n if (filtered.length !== sessions.length) {\n saveHistory(filtered);\n }\n });\n}\n\nexport function getSessionsForTarget(\n sessions: WorktreeSession[],\n target: string,\n): WorktreeSession[] {\n return sessions.filter((s) => s.target === target);\n}\n\nexport function getRecentSessions(\n sessions: WorktreeSession[],\n count: number,\n): WorktreeSession[] {\n return [...sessions]\n .sort(\n (a, b) =>\n new Date(effectiveLastAccessedAt(b)).getTime() -\n new Date(effectiveLastAccessedAt(a)).getTime(),\n )\n .slice(0, count);\n}\n\nexport function pruneStaleEntries(sessions: WorktreeSession[]): {\n kept: WorktreeSession[];\n pruned: number;\n} {\n const kept: WorktreeSession[] = [];\n let pruned = 0;\n\n for (const session of sessions) {\n const anyPathExists = session.paths.some((p) => fs.existsSync(p));\n if (anyPathExists) {\n kept.push(session);\n } else {\n pruned++;\n }\n }\n\n return { kept, pruned };\n}\n\n/** Locked variant of prune for `status --prune` callers. */\nexport async function prunePersistedStaleEntries(): Promise<{ pruned: number }> {\n return withHistoryLock(() => {\n const sessions = loadHistory();\n const { kept, pruned } = pruneStaleEntries(sessions);\n if (pruned > 0) saveHistory(kept);\n return { pruned };\n });\n}\n\n/**\n * Merge hydrated sessions into history without clobbering existing entries.\n * For each incoming session: if a matching target+branch exists, refresh its\n * paths (if different) but keep original timestamps. Otherwise insert.\n */\nexport async function mergeHydratedSessions(\n incoming: WorktreeSession[],\n): Promise<{ added: number; updated: number }> {\n return withHistoryLock(() => {\n const sessions = loadHistory();\n let added = 0;\n let updated = 0;\n\n for (const inc of incoming) {\n const existing = findSession(sessions, inc.target, inc.branch);\n if (existing) {\n const sortedA = [...existing.paths].sort();\n const sortedB = [...inc.paths].sort();\n const same =\n sortedA.length === sortedB.length &&\n sortedA.every((p, i) => p === sortedB[i]);\n if (!same) {\n existing.paths = inc.paths;\n updated++;\n }\n } else {\n sessions.push(inc);\n added++;\n }\n }\n\n if (added > 0 || updated > 0) {\n saveHistory(sessions);\n }\n return { added, updated };\n });\n}\n","import fs from 'node:fs';\nimport lockfile from 'proper-lockfile';\n\n/**\n * Write a file atomically. Writes to a sibling tmp file and renames\n * over the target, so a crash mid-write can't leave a truncated file.\n */\nexport function atomicWriteFile(filePath: string, content: string): void {\n const tmpPath = `${filePath}.tmp-${process.pid}`;\n fs.writeFileSync(tmpPath, content, 'utf-8');\n fs.renameSync(tmpPath, filePath);\n}\n\n/**\n * Ensure a file exists (creating with the given initial content if missing).\n * Required before `withFileLock` can acquire a lock on it — proper-lockfile\n * resolves the target file's realpath before creating the sibling .lock dir.\n */\nexport function ensureFile(filePath: string, initialContent: string): void {\n if (!fs.existsSync(filePath)) {\n fs.writeFileSync(filePath, initialContent, 'utf-8');\n }\n}\n\n/**\n * Serialize a read-modify-write sequence across processes using the same\n * advisory-lock mechanism as npm/yarn/pnpm. Caller is responsible for\n * ensuring the target file exists first (see `ensureFile`).\n */\nexport async function withFileLock<T>(\n filePath: string,\n fn: () => T | Promise<T>,\n): Promise<T> {\n const release = await lockfile.lock(filePath, {\n retries: { retries: 20, minTimeout: 25, maxTimeout: 500, factor: 2 },\n stale: 10_000,\n });\n try {\n return await fn();\n } finally {\n await release();\n }\n}\n\n/**\n * Synchronous sibling of {@link withFileLock}. Serializes a read-modify-write\n * across processes using the same advisory-lock mechanism, but without an\n * `await` so it can be called from synchronous code paths (e.g. the\n * file-backed comment store, whose API is sync and consumed by sync Hono\n * route handlers). Caller is responsible for ensuring the target file exists\n * first (see `ensureFile`).\n */\nexport function withFileLockSync<T>(filePath: string, fn: () => T): T {\n // proper-lockfile's sync API forbids its own retry config (it requires an\n // async flow), so we hand-roll a bounded retry: try to acquire, and on a\n // contended lock (ELOCKED) sleep synchronously and try again.\n const maxAttempts = 30;\n let release: (() => void) | undefined;\n for (let attempt = 0; ; attempt++) {\n try {\n release = lockfile.lockSync(filePath, { stale: 10_000 });\n break;\n } catch (err) {\n const code = (err as { code?: string }).code;\n if (code !== 'ELOCKED' || attempt >= maxAttempts) throw err;\n sleepSync(Math.min(25 * 2 ** Math.min(attempt, 4), 500));\n }\n }\n try {\n return fn();\n } finally {\n release();\n }\n}\n\n/** Block the current thread for `ms` milliseconds without spinning the CPU.\n * Used only by `withFileLockSync`'s contention backoff. */\nfunction sleepSync(ms: number): void {\n const shared = new Int32Array(new SharedArrayBuffer(4));\n Atomics.wait(shared, 0, 0, ms);\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport type { WorktreeSession } from './history.js';\n\n/**\n * Claude Code writes each session as JSONL under\n * `~/.claude/projects/<encoded-cwd>/`, where the encoded path replaces every\n * non-alphanumeric character with `-`. These files get rewritten on every\n * message, so their mtimes reflect actual conversation activity — which is\n * what the user cares about, not when `work tree` was last invoked.\n */\nfunction encodeProjectDir(p: string): string {\n return path.resolve(p).replace(/[^A-Za-z0-9]/g, '-');\n}\n\nfunction latestJsonlMtimeMs(projectDir: string): number {\n let entries: string[];\n try {\n entries = fs.readdirSync(projectDir);\n } catch {\n return 0;\n }\n let latest = 0;\n for (const name of entries) {\n if (!name.endsWith('.jsonl')) continue;\n try {\n const stat = fs.statSync(path.join(projectDir, name));\n if (stat.mtimeMs > latest) latest = stat.mtimeMs;\n } catch {\n /* ignore unreadable entries */\n }\n }\n return latest;\n}\n\nexport function getClaudeActivityMs(launchPath: string): number {\n const dir = path.join(\n os.homedir(),\n '.claude',\n 'projects',\n encodeProjectDir(launchPath),\n );\n return latestJsonlMtimeMs(dir);\n}\n\nfunction getLaunchPaths(session: WorktreeSession): string[] {\n if (!session.isGroup) return [...session.paths];\n // Groups launch Claude in the parent (group root), not a repo subfolder.\n const parents = new Set<string>();\n for (const p of session.paths) parents.add(path.dirname(p));\n return [...parents];\n}\n\n/**\n * Returns the most recent of the session's persisted `lastAccessedAt` and\n * the mtime of Claude's session logs for this worktree's launch path(s).\n */\nexport function effectiveLastAccessedAt(session: WorktreeSession): string {\n let bestMs = new Date(session.lastAccessedAt).getTime();\n if (!Number.isFinite(bestMs)) bestMs = 0;\n for (const p of getLaunchPaths(session)) {\n const ms = getClaudeActivityMs(p);\n if (ms > bestMs) bestMs = ms;\n }\n return new Date(bestMs).toISOString();\n}\n\nexport type ActivityState = 'active' | 'open' | 'stale';\n\n/** Active: Claude wrote a turn in the last 30 s — currently thinking. */\nconst ACTIVE_MS = 30_000;\n/** Open: Claude touched the transcript within 5 min — terminal still attached,\n * just idle. After this we consider the session stale. */\nconst OPEN_MS = 5 * 60_000;\n\nexport interface SessionActivity {\n /** ms since epoch of the most recent Claude write across this session's\n * launch path(s), or null if no transcript exists. */\n lastActivity: number | null;\n state: ActivityState;\n}\n\n/**\n * Derive an activity state for a session by looking at Claude's transcript\n * mtime. Works whether the user launched Claude via `work tree`, `work dash`,\n * a manual terminal, or `work web`'s own PTY — they all write to the same\n * `~/.claude/projects/<encoded-cwd>/` directory.\n *\n * If Claude's on-disk layout ever changes, the function falls back to\n * \"stale\" gracefully and the dashboard's other status signals (e.g. our own\n * PTY pool) keep working.\n */\nexport function readSessionActivity(session: WorktreeSession): SessionActivity {\n let latest = 0;\n for (const p of getLaunchPaths(session)) {\n const ms = getClaudeActivityMs(p);\n if (ms > latest) latest = ms;\n }\n if (latest === 0) return { lastActivity: null, state: 'stale' };\n const age = Date.now() - latest;\n const state: ActivityState =\n age <= ACTIVE_MS ? 'active' : age <= OPEN_MS ? 'open' : 'stale';\n return { lastActivity: latest, state };\n}\n\n/** Watch root: `~/.claude/projects/`. The web server subscribes to mtime\n * changes here and re-broadcasts `sessions-changed` so the sidebar\n * badges refresh without polling. */\nexport function claudeProjectsRoot(): string {\n return path.join(os.homedir(), '.claude', 'projects');\n}\n","import fs from 'node:fs';\nimport net from 'node:net';\nimport type { WorkConfig } from './config.js';\nimport type { WorktreeSession } from './history.js';\n\n/** Default dev-server port range when none is configured. */\nexport const DEFAULT_PORT_RANGE = { start: 3000, end: 3099 };\n\n/**\n * Thrown when every port in the configured range is occupied. Distinct class so\n * callers can tell \"range exhausted\" apart from any other allocation failure.\n */\nexport class PortRangeExhaustedError extends Error {\n readonly start: number;\n readonly end: number;\n constructor(start: number, end: number) {\n super(\n `No free dev-server port available in range ${start}-${end} ` +\n `(all ${end - start + 1} in use). Widen \"portRange\" in ~/.work/config.json.`,\n );\n this.name = 'PortRangeExhaustedError';\n this.start = start;\n this.end = end;\n }\n}\n\n/**\n * Deterministic, stable string hash (FNV-1a 32-bit). Same input always yields\n * the same non-negative integer, so a worktree's seed key maps to the same base\n * offset across processes and machines.\n */\nfunction hashString(input: string): number {\n let hash = 0x811c9dc5;\n for (let i = 0; i < input.length; i++) {\n hash ^= input.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193);\n }\n return hash >>> 0;\n}\n\nfunction normalizeRange(config: Pick<WorkConfig, 'portRange'>): {\n start: number;\n end: number;\n rangeSize: number;\n} {\n const range = config.portRange ?? DEFAULT_PORT_RANGE;\n const start = Math.min(range.start, range.end);\n const end = Math.max(range.start, range.end);\n const rangeSize = end - start + 1;\n if (rangeSize <= 0) {\n throw new Error(`Invalid port range: ${range.start}-${range.end}`);\n }\n return { start, end, rangeSize };\n}\n\nfunction portsHeldByActiveSessions(sessions: WorktreeSession[]): Set<number> {\n const inUse = new Set<number>();\n for (const session of sessions) {\n if (session.port === undefined) continue;\n const active = session.paths.some((p) => fs.existsSync(p));\n if (active) inUse.add(session.port);\n }\n return inUse;\n}\n\n/**\n * Allocate a stable dev-server port for a worktree.\n *\n * Pure and testable: the caller passes the current `sessions` array (e.g.\n * `loadHistory()`) rather than this function reading state itself.\n *\n * - The range comes from `config.portRange` (falling back to 3000–3099).\n * - A deterministic base offset is derived from `hash(seedKey) % rangeSize`,\n * so the same worktree prefers the same port. The seed key MUST be unique per\n * worktree (e.g. `target:branch` or the full worktree path) — using only a\n * branch name would let two repos sharing a branch collide deterministically.\n * - Ports already held by *active* sessions (at least one path still exists)\n * are considered in use and skipped.\n * - Walks forward from the base offset (wrapping around the range) to the first\n * free port. Throws `PortRangeExhaustedError` if every port is occupied.\n *\n * NOTE: this checks only our own history, not host liveness. Use\n * `allocateFreePort` when you also want to skip ports occupied by unrelated\n * processes on the machine.\n */\nexport function allocatePort(\n seedKey: string,\n config: Pick<WorkConfig, 'portRange'>,\n sessions: WorktreeSession[],\n): number {\n const { start, end, rangeSize } = normalizeRange(config);\n const inUse = portsHeldByActiveSessions(sessions);\n const baseOffset = hashString(seedKey) % rangeSize;\n\n for (let i = 0; i < rangeSize; i++) {\n const candidate = start + ((baseOffset + i) % rangeSize);\n if (!inUse.has(candidate)) {\n return candidate;\n }\n }\n\n throw new PortRangeExhaustedError(start, end);\n}\n\n/**\n * Probe whether a TCP port is free to bind on localhost. Resolves false on\n * EADDRINUSE (something is already listening), true otherwise. Best-effort: any\n * non-EADDRINUSE error resolves true so a quirky host can't block allocation.\n */\nexport function isPortFree(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const server = net.createServer();\n server.once('error', (err: NodeJS.ErrnoException) => {\n resolve(err.code !== 'EADDRINUSE');\n });\n server.once('listening', () => {\n server.close(() => resolve(true));\n });\n server.listen(port, '127.0.0.1');\n });\n}\n\n/**\n * Like `allocatePort`, but additionally probes host liveness: candidates that\n * are already bound by some other process on localhost (EADDRINUSE) are skipped\n * even when our history doesn't know about them. This is what actually prevents\n * collisions with non-work processes.\n *\n * Walks the same deterministic order as `allocatePort` and returns the first\n * candidate that is both free in history and bindable on the host. Throws\n * `PortRangeExhaustedError` if every candidate is taken.\n */\nexport async function allocateFreePort(\n seedKey: string,\n config: Pick<WorkConfig, 'portRange'>,\n sessions: WorktreeSession[],\n probe: (port: number) => Promise<boolean> = isPortFree,\n): Promise<number> {\n const { start, end, rangeSize } = normalizeRange(config);\n const inUse = portsHeldByActiveSessions(sessions);\n const baseOffset = hashString(seedKey) % rangeSize;\n\n for (let i = 0; i < rangeSize; i++) {\n const candidate = start + ((baseOffset + i) % rangeSize);\n if (inUse.has(candidate)) continue;\n if (await probe(candidate)) {\n return candidate;\n }\n }\n\n throw new PortRangeExhaustedError(start, end);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { globSync } from 'glob';\nimport chalk from 'chalk';\n\n/** Directories to exclude when copying files. */\nconst EXCLUDED_DIRS = ['bin', 'obj', 'node_modules', '.git'];\n\n/**\n * Copy files matching configured patterns from a source repo into a worktree.\n * Handles .claude directory specially (direct path match).\n * Other patterns use recursive glob with exclusion filtering.\n */\nexport function copyConfigFiles(\n repoPath: string,\n worktreePath: string,\n patterns: string[],\n): void {\n for (const pattern of patterns) {\n // Normalize pattern separators to forward slashes for glob\n const normalized = pattern.replace(/\\\\/g, '/');\n\n // Handle .claude directory specially (direct path match)\n if (normalized.startsWith('.claude/')) {\n const relativePath = normalized;\n const sourcePath = path.join(repoPath, relativePath);\n\n if (fs.existsSync(sourcePath)) {\n const destPath = path.join(worktreePath, relativePath);\n const destDir = path.dirname(destPath);\n fs.mkdirSync(destDir, { recursive: true });\n fs.copyFileSync(sourcePath, destPath);\n console.log(` Copied: ${relativePath}`);\n }\n continue;\n }\n\n // Use glob for pattern matching (recursive)\n const matches = globSync(`**/${normalized}`, {\n cwd: repoPath,\n nodir: true,\n posix: true,\n dot: true,\n });\n\n for (const match of matches) {\n // Check exclusion directories\n const parts = match.split('/');\n if (parts.some((p) => EXCLUDED_DIRS.includes(p))) {\n continue;\n }\n\n const sourcePath = path.join(repoPath, match);\n const destPath = path.join(worktreePath, match);\n const destDir = path.dirname(destPath);\n\n fs.mkdirSync(destDir, { recursive: true });\n fs.copyFileSync(sourcePath, destPath);\n console.log(` Copied: ${match}`);\n }\n }\n}\n","/**\n * Parsing for `work tree --base`. A base can be specified two ways, and\n * they compose:\n *\n * --base dev → default base for every repo\n * --base backend=dev --base front=x → per-repo overrides\n * --base dev --base front=x → dev for all, front overridden to x\n *\n * yargs collects repeated `--base` flags into an array (a single flag stays\n * a string), so this accepts `string | string[] | undefined`.\n */\n\nexport interface BaseSpec {\n /** Base applied to any repo without a per-repo override. */\n default?: string;\n /** alias → base branch overrides. */\n perRepo: Record<string, string>;\n}\n\n/** Thrown by {@link parseBaseSpec} on malformed or conflicting input. */\nexport class BaseSpecError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'BaseSpecError';\n }\n}\n\n/**\n * Parse raw `--base` values into a {@link BaseSpec}. A value containing `=`\n * is treated as an `alias=branch` override; anything else is a bare default.\n * Throws {@link BaseSpecError} on empty halves or conflicting duplicates.\n */\nexport function parseBaseSpec(raw: string | string[] | undefined): BaseSpec {\n const spec: BaseSpec = { perRepo: {} };\n if (raw === undefined) return spec;\n const values = Array.isArray(raw) ? raw : [raw];\n\n for (const value of values) {\n const v = value.trim();\n if (!v) continue;\n const eq = v.indexOf('=');\n if (eq === -1) {\n if (spec.default !== undefined && spec.default !== v) {\n throw new BaseSpecError(\n `Conflicting default --base values: '${spec.default}' and '${v}'`,\n );\n }\n spec.default = v;\n } else {\n const alias = v.slice(0, eq).trim();\n const branch = v.slice(eq + 1).trim();\n if (!alias || !branch) {\n throw new BaseSpecError(\n `Invalid --base '${value}'. Use 'alias=branch' or a bare 'branch'.`,\n );\n }\n const prior = spec.perRepo[alias];\n if (prior !== undefined && prior !== branch) {\n throw new BaseSpecError(\n `Conflicting --base for '${alias}': '${prior}' and '${branch}'`,\n );\n }\n spec.perRepo[alias] = branch;\n }\n }\n\n return spec;\n}\n\n/** The resolved base for one repo alias: its override, else the default. */\nexport function baseForAlias(spec: BaseSpec, alias: string): string | undefined {\n return spec.perRepo[alias] ?? spec.default;\n}\n\n/** True when no base was requested at all (plain HEAD-fork behavior). */\nexport function isEmptyBaseSpec(spec: BaseSpec): boolean {\n return spec.default === undefined && Object.keys(spec.perRepo).length === 0;\n}\n\n/** The alias keys carrying a per-repo override. */\nexport function baseSpecOverrideAliases(spec: BaseSpec): string[] {\n return Object.keys(spec.perRepo);\n}\n\n/** Normalize the loose `setupWorktree` parameter into a {@link BaseSpec}. */\nexport function toBaseSpec(base: string | BaseSpec | undefined): BaseSpec {\n if (base === undefined) return { perRepo: {} };\n if (typeof base === 'string') return { default: base, perRepo: {} };\n return base;\n}\n","import chalk from 'chalk';\r\nimport type { CommandModule } from 'yargs';\r\nimport { ensureConfig } from '../core/config.js';\r\nimport { resolveProjectTarget, getAllTargetNames } from '../core/resolve.js';\r\nimport { teardownWorktree } from '../core/worktree.js';\r\nimport { removeSession } from '../core/history.js';\r\n\r\nexport const removeCommand: CommandModule = {\r\n command: 'remove <target> <branch>',\r\n describe: 'Remove a worktree',\r\n builder: (yargs) =>\r\n yargs\r\n .showHelpOnFail(true)\r\n .positional('target', {\r\n describe: 'Project alias or group name',\r\n type: 'string',\r\n demandOption: true,\r\n })\r\n .positional('branch', {\r\n describe: 'Branch name (e.g., feature/login)',\r\n type: 'string',\r\n demandOption: true,\r\n })\r\n .option('force', {\r\n describe: 'Force remove even with uncommitted/unpushed changes',\r\n type: 'boolean',\r\n default: false,\r\n }),\r\n handler: async (argv) => {\r\n const targetName = argv.target as string;\r\n const branchName = argv.branch as string;\r\n const force = argv.force as boolean;\r\n\r\n const config = ensureConfig();\r\n\r\n const target = resolveProjectTarget(targetName, config);\r\n if (!target) {\r\n const allNames = getAllTargetNames(config);\r\n console.error(`Project or group not found: ${targetName}`);\r\n console.log(chalk.yellow(`Available: ${allNames.join(', ')}`));\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n console.log(chalk.cyan(`Removing worktree: ${targetName}/${branchName}`));\r\n console.log('');\r\n\r\n const allRemoved = teardownWorktree(targetName, target.isGroup, branchName, config, force);\r\n\r\n if (allRemoved === true) {\r\n await removeSession(targetName, branchName);\r\n } else {\r\n process.exitCode = 1;\r\n console.log('');\r\n console.log(\r\n chalk.yellow(\r\n 'Some worktrees could not be removed due to uncommitted/unpushed changes.',\r\n ),\r\n );\r\n console.log(\r\n chalk.yellow(\r\n `Use 'work remove ${targetName} ${branchName} --force' to force remove.`,\r\n ),\r\n );\r\n }\r\n },\r\n};\r\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport type { CommandModule } from 'yargs';\nimport { ensureConfig } from '../core/config.js';\nimport { parseWorktreeList, getCurrentBranch } from '../core/git.js';\nimport { resolveProjectTarget, getAllTargetNames } from '../core/resolve.js';\n\nexport const listCommand: CommandModule = {\n command: 'list [target]',\n describe: 'List worktrees',\n builder: (yargs) =>\n yargs.positional('target', {\n describe: 'Project alias or group name (omit to list all)',\n type: 'string',\n }),\n handler: (argv) => {\n const targetName = argv.target as string | undefined;\n\n const config = ensureConfig();\n const worktreesRoot = config.worktreesRoot;\n\n console.log('');\n console.log(chalk.cyan('Worktrees'));\n console.log(chalk.cyan('========='));\n console.log('');\n\n let showRepos: string[] = [];\n let showGroups: string[] = [];\n\n if (targetName) {\n const target = resolveProjectTarget(targetName, config);\n if (!target) {\n const allNames = getAllTargetNames(config);\n console.error(`Project or group not found: ${targetName}`);\n console.log(chalk.yellow(`Available: ${allNames.join(', ')}`));\n process.exitCode = 1;\n return;\n }\n if (target.isGroup) {\n showGroups = [targetName];\n } else {\n showRepos = [targetName];\n }\n } else {\n showRepos = Object.keys(config.repos);\n showGroups = Object.keys(config.groups);\n }\n\n let foundAny = false;\n\n // Show per-repo worktrees\n for (const proj of showRepos) {\n const repoPath = config.repos[proj];\n\n if (!fs.existsSync(repoPath)) {\n console.log(\n chalk.red(\n `${proj} -> Repository path not found: ${repoPath}`,\n ),\n );\n continue;\n }\n\n const worktreeList = parseWorktreeList(repoPath).filter(\n (wt) => wt.path !== repoPath,\n );\n\n if (worktreeList.length > 0) {\n foundAny = true;\n const plural = worktreeList.length !== 1 ? 's' : '';\n console.log(\n chalk.green(\n `${proj} (${worktreeList.length} worktree${plural}):`,\n ),\n );\n\n for (const wt of worktreeList) {\n const branchDisplay = wt.branch || `detached at ${wt.head}`;\n console.log(` ${branchDisplay}`);\n console.log(chalk.gray(` ${wt.path}`));\n }\n console.log('');\n }\n }\n\n // Show group worktrees\n for (const groupName of showGroups) {\n const groupDir = path.join(worktreesRoot, groupName);\n if (!fs.existsSync(groupDir)) continue;\n\n let branchDirs: string[];\n try {\n branchDirs = fs\n .readdirSync(groupDir, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name);\n } catch {\n continue;\n }\n\n if (branchDirs.length === 0) continue;\n\n foundAny = true;\n const plural = branchDirs.length !== 1 ? 's' : '';\n console.log(\n chalk.magenta(\n `${groupName} [group] (${branchDirs.length} worktree${plural}):`,\n ),\n );\n\n const repoAliases = config.groups[groupName] ?? [];\n\n for (const bdName of branchDirs) {\n const bdPath = path.join(groupDir, bdName);\n\n // Determine the actual branch name from the first sub-worktree\n let actualBranch: string | null = null;\n for (const alias of repoAliases) {\n const repoPath = config.repos[alias];\n if (!repoPath) continue;\n const rdPath = path.join(bdPath, path.basename(repoPath));\n if (fs.existsSync(rdPath)) {\n const branch = getCurrentBranch(rdPath);\n if (branch) {\n actualBranch = branch;\n break;\n }\n }\n }\n\n const displayBranch = actualBranch || bdName;\n console.log(` ${displayBranch}`);\n console.log(chalk.gray(` ${bdPath}`));\n if (repoAliases.length > 0) {\n console.log(\n chalk.gray(` Repos: ${repoAliases.join(', ')}`),\n );\n }\n }\n console.log('');\n }\n\n if (!foundAny) {\n if (targetName) {\n console.log(\n chalk.yellow(`No worktrees found for: ${targetName}`),\n );\n } else {\n console.log(\n chalk.yellow(\n 'No worktrees found for any project or group',\n ),\n );\n }\n console.log('');\n }\n },\n};\n","import fs from 'node:fs';\r\nimport chalk from 'chalk';\r\nimport type { CommandModule } from 'yargs';\r\nimport { ensureConfig } from '../core/config.js';\r\nimport {\r\n loadHistory,\r\n getSessionsForTarget,\r\n prunePersistedStaleEntries,\r\n type WorktreeSession,\r\n} from '../core/history.js';\r\nimport { effectiveLastAccessedAt } from '../core/claude-activity.js';\r\nimport {\r\n getStatus,\r\n getUnpushedCommits,\r\n isBranchMerged,\r\n getCurrentBranch,\r\n} from '../core/git.js';\r\nimport { timeAgo, shortDateTime } from '../utils/format.js';\r\n\r\nexport const statusCommand: CommandModule = {\r\n command: 'status [target] [branch]',\r\n describe: 'Show status of tracked worktrees',\r\n builder: (yargs) =>\r\n yargs\r\n .positional('target', {\r\n describe: 'Filter by project alias or group name',\r\n type: 'string',\r\n })\r\n .positional('branch', {\r\n describe: 'Filter by branch name',\r\n type: 'string',\r\n })\r\n .option('prune', {\r\n describe: 'Remove entries whose worktree paths no longer exist',\r\n type: 'boolean',\r\n default: false,\r\n }),\r\n handler: async (argv) => {\r\n const target = argv.target as string | undefined;\r\n const branch = argv.branch as string | undefined;\r\n const prune = argv.prune as boolean;\r\n\r\n ensureConfig();\r\n\r\n if (prune) {\r\n const { pruned } = await prunePersistedStaleEntries();\r\n if (pruned > 0) {\r\n console.log(chalk.green(`Pruned ${pruned} stale session(s).`));\r\n } else {\r\n console.log('No stale sessions to prune.');\r\n }\r\n }\r\n\r\n let sessions = loadHistory();\r\n\r\n if (target) {\r\n sessions = getSessionsForTarget(sessions, target);\r\n }\r\n if (branch) {\r\n sessions = sessions.filter((s) => s.branch === branch);\r\n }\r\n\r\n if (sessions.length === 0) {\r\n console.log(chalk.yellow('No tracked worktree sessions found.'));\r\n return;\r\n }\r\n\r\n console.log('');\r\n console.log(chalk.cyan('Worktree Status'));\r\n console.log(chalk.cyan('==============='));\r\n console.log('');\r\n\r\n for (const session of sessions) {\r\n printSessionStatus(session);\r\n }\r\n },\r\n};\r\n\r\nfunction printSessionStatus(session: WorktreeSession): void {\r\n const typeLabel = session.isGroup\r\n ? chalk.magenta('[group]')\r\n : chalk.blue('[repo]');\r\n console.log(\r\n `${typeLabel} ${chalk.green(session.target)} ${chalk.white(session.branch)}`,\r\n );\r\n\r\n // Check if paths exist\r\n const existingPaths = session.paths.filter((p) => fs.existsSync(p));\r\n if (existingPaths.length === 0) {\r\n console.log(chalk.red(' Path(s) no longer exist on disk'));\r\n console.log(\r\n chalk.gray(\r\n ` Created: ${shortDateTime(session.createdAt)} Last used: ${timeAgo(effectiveLastAccessedAt(session))}`,\r\n ),\r\n );\r\n console.log('');\r\n return;\r\n }\r\n\r\n // For each existing path, gather git info\r\n for (const wtPath of existingPaths) {\r\n const branch = getCurrentBranch(wtPath);\r\n if (!branch) continue;\r\n\r\n const { merged } = isBranchMerged(branch, wtPath);\r\n const changes = getStatus(wtPath);\r\n const unpushed = getUnpushedCommits(wtPath);\r\n\r\n const changeCount = changes\r\n ? changes.split('\\n').filter((l) => l.trim()).length\r\n : 0;\r\n const unpushedCount = unpushed\r\n ? unpushed.split('\\n').filter((l) => l.trim()).length\r\n : 0;\r\n\r\n const mergedTag = merged\r\n ? chalk.green(' [merged]')\r\n : '';\r\n\r\n const parts: string[] = [];\r\n if (changeCount > 0) {\r\n parts.push(chalk.yellow(`${changeCount} uncommitted`));\r\n }\r\n if (unpushedCount > 0) {\r\n parts.push(chalk.yellow(`${unpushedCount} unpushed`));\r\n }\r\n\r\n const statusLine = parts.length > 0 ? parts.join(', ') : chalk.green('clean');\r\n\r\n if (existingPaths.length > 1) {\r\n console.log(chalk.gray(` ${wtPath}`));\r\n console.log(` ${statusLine}${mergedTag}`);\r\n } else {\r\n console.log(` ${statusLine}${mergedTag}`);\r\n console.log(chalk.gray(` ${wtPath}`));\r\n }\r\n }\r\n\r\n console.log(\r\n chalk.gray(\r\n ` Created: ${shortDateTime(session.createdAt)} Last used: ${timeAgo(effectiveLastAccessedAt(session))}`,\r\n ),\r\n );\r\n console.log('');\r\n}\r\n","const MINUTE = 60_000;\nconst HOUR = 60 * MINUTE;\nconst DAY = 24 * HOUR;\n\nexport function timeAgo(isoDate: string): string {\n const diff = Date.now() - new Date(isoDate).getTime();\n\n if (diff < MINUTE) return 'just now';\n if (diff < HOUR) {\n const mins = Math.floor(diff / MINUTE);\n return `${mins}m ago`;\n }\n if (diff < DAY) {\n const hours = Math.floor(diff / HOUR);\n return `${hours}h ago`;\n }\n const days = Math.floor(diff / DAY);\n return `${days}d ago`;\n}\n\nexport function shortDateTime(isoDate: string): string {\n const d = new Date(isoDate);\n const month = String(d.getMonth() + 1).padStart(2, '0');\n const day = String(d.getDate()).padStart(2, '0');\n const hours = String(d.getHours()).padStart(2, '0');\n const minutes = String(d.getMinutes()).padStart(2, '0');\n return `${month}-${day} ${hours}:${minutes}`;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport type { CommandModule } from 'yargs';\nimport { select } from '@inquirer/prompts';\nimport { ensureConfig } from '../core/config.js';\nimport { loadHistory, getRecentSessions, upsertSession } from '../core/history.js';\nimport { effectiveLastAccessedAt } from '../core/claude-activity.js';\nimport { getAiTool } from '../core/ai-launcher.js';\nimport { launchAi } from '../utils/platform.js';\nimport { timeAgo } from '../utils/format.js';\n\nexport const recentCommand: CommandModule = {\n command: 'recent [count]',\n describe: 'List recent worktree sessions',\n builder: (yargs) =>\n yargs\n .positional('count', {\n describe: 'Number of recent sessions to show',\n type: 'number',\n default: 10,\n })\n .option('resume', {\n describe: 'Interactively pick a session and launch the configured AI tool',\n type: 'boolean',\n default: false,\n })\n .option('unsafe', {\n describe: 'Launch the AI tool with its skip-permissions flag (used with --resume)',\n type: 'boolean',\n default: false,\n }),\n handler: async (argv) => {\n const count = argv.count as number;\n const resume = argv.resume as boolean;\n const unsafe = argv.unsafe as boolean;\n\n const config = ensureConfig();\n\n const sessions = loadHistory();\n const recent = getRecentSessions(sessions, count);\n\n if (recent.length === 0) {\n console.log(chalk.yellow('No recent worktree sessions found.'));\n return;\n }\n\n if (resume) {\n // Filter to sessions that still exist on disk\n const valid = recent.filter((s) =>\n s.paths.some((p) => fs.existsSync(p)),\n );\n\n if (valid.length === 0) {\n console.log(\n chalk.yellow('No resumable sessions (all paths removed from disk).'),\n );\n return;\n }\n\n const choices = valid.map((s) => {\n const typeTag = s.isGroup ? '[group]' : '[repo]';\n return {\n name: `${typeTag} ${s.target} ${s.branch} (${timeAgo(effectiveLastAccessedAt(s))})`,\n value: s,\n };\n });\n\n const choice = await select({\n message: 'Select a session to resume:',\n choices,\n pageSize: 15,\n });\n\n // Find first existing path\n const firstExisting = choice.paths.find((p) => fs.existsSync(p));\n if (!firstExisting) {\n console.error('Session path no longer exists.');\n process.exitCode = 1;\n return;\n }\n\n // For groups, launch in the parent directory (group root), not a single repo subfolder\n const launchPath = choice.isGroup ? path.dirname(firstExisting) : firstExisting;\n\n await upsertSession(choice.target, choice.isGroup, choice.branch, choice.paths);\n\n const tool = getAiTool(config);\n console.log(chalk.cyan(`Resuming in: ${launchPath}`));\n console.log(`Starting ${tool.cmd}...`);\n launchAi(launchPath, tool, { unsafe, resume: true }, choice.port);\n return;\n }\n\n // Just list recent sessions\n console.log('');\n console.log(chalk.cyan('Recent Sessions'));\n console.log(chalk.cyan('==============='));\n console.log('');\n\n for (const session of recent) {\n const typeLabel = session.isGroup\n ? chalk.magenta('[group]')\n : chalk.blue('[repo]');\n const exists = session.paths.some((p) => fs.existsSync(p));\n const existsTag = exists ? '' : chalk.red(' [removed]');\n\n console.log(\n `${typeLabel} ${chalk.green(session.target)} ${chalk.white(session.branch)}${existsTag}`,\n );\n console.log(\n chalk.gray(` Last used: ${timeAgo(effectiveLastAccessedAt(session))}`),\n );\n }\n\n console.log('');\n console.log(\n chalk.gray('Use work resume to interactively resume a session.'),\n );\n },\n};\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport type { CommandModule } from 'yargs';\nimport { select } from '@inquirer/prompts';\nimport { ensureConfig } from '../core/config.js';\nimport { loadHistory, getRecentSessions, upsertSession } from '../core/history.js';\nimport { effectiveLastAccessedAt } from '../core/claude-activity.js';\nimport { getAiTool } from '../core/ai-launcher.js';\nimport { launchAi } from '../utils/platform.js';\nimport { timeAgo } from '../utils/format.js';\n\nexport const resumeCommand: CommandModule = {\n command: 'resume',\n describe: 'Resume a recent worktree session',\n builder: (yargs) =>\n yargs.option('unsafe', {\n describe: 'Launch the AI tool with its skip-permissions flag',\n type: 'boolean',\n default: false,\n }),\n handler: async (argv) => {\n const unsafe = argv.unsafe as boolean;\n\n const config = ensureConfig();\n\n const sessions = loadHistory();\n const recent = getRecentSessions(sessions, sessions.length);\n\n // Filter to sessions that still exist on disk\n const valid = recent.filter((s) =>\n s.paths.some((p) => fs.existsSync(p)),\n );\n\n if (valid.length === 0) {\n console.log(\n chalk.yellow('No resumable sessions found.'),\n );\n return;\n }\n\n const choices = valid.map((s) => {\n const typeTag = s.isGroup ? '[group]' : '[repo]';\n return {\n name: `${typeTag} ${s.target} ${s.branch} (${timeAgo(effectiveLastAccessedAt(s))})`,\n value: s,\n };\n });\n\n const choice = await select({\n message: 'Select a session to resume:',\n choices,\n pageSize: 15,\n });\n\n // Find first existing path\n const firstExisting = choice.paths.find((p) => fs.existsSync(p));\n if (!firstExisting) {\n console.error('Session path no longer exists.');\n process.exitCode = 1;\n return;\n }\n\n // For groups, launch in the parent directory (group root), not a single repo subfolder\n const launchPath = choice.isGroup ? path.dirname(firstExisting) : firstExisting;\n\n await upsertSession(choice.target, choice.isGroup, choice.branch, choice.paths);\n\n const tool = getAiTool(config);\n console.log(chalk.cyan(`Resuming in: ${launchPath}`));\n console.log(`Starting ${tool.cmd}...`);\n launchAi(launchPath, tool, { unsafe, resume: true }, choice.port);\n },\n};\n","import chalk from 'chalk';\nimport { checkbox } from '@inquirer/prompts';\nimport type { CommandModule } from 'yargs';\nimport { ensureConfig } from '../core/config.js';\nimport {\n type PrunableEntry,\n collectPrunable,\n removeSingleEntry,\n removeGroupEntry,\n} from '../core/prunable-scan.js';\n\nexport const pruneCommand: CommandModule = {\n command: 'prune',\n describe: 'Remove worktrees for merged branches',\n builder: (yargs) =>\n yargs.option('force', {\n describe: 'Skip interactive picker and remove all merged worktrees',\n type: 'boolean',\n default: false,\n }),\n handler: async (argv) => {\n const force = argv.force as boolean;\n\n const config = ensureConfig();\n console.log(chalk.gray('Scanning worktrees for merged branches...\\n'));\n // Interactive prune is human-confirmed (the user sees the list and picks),\n // so squash-merged matches are safe to surface here. `work sync` stays gated\n // to true-merge confidence by default (opt in via --include-squash).\n const prunable = collectPrunable(config, { includeSquash: true });\n\n if (prunable.length === 0) {\n console.log(chalk.green('No merged worktrees found. Nothing to prune.'));\n return;\n }\n\n console.log(\n chalk.cyan(`Found ${prunable.length} merged worktree(s):\\n`),\n );\n\n let selected: PrunableEntry[];\n\n if (force) {\n selected = prunable;\n for (const entry of selected) {\n const suffix = entry.type === 'group' ? ' [group]' : '';\n console.log(` ${entry.target}: ${entry.branch}${suffix}`);\n }\n console.log('');\n } else {\n const choices = prunable.map((entry) => {\n const suffix = entry.type === 'group' ? ' [group]' : '';\n return {\n name: `${entry.target}: ${entry.branch}${suffix}`,\n value: entry,\n };\n });\n\n selected = await checkbox({\n message: 'Select merged worktrees to remove',\n choices,\n pageSize: choices.length,\n });\n\n if (selected.length === 0) {\n console.log(chalk.yellow('Nothing selected.'));\n return;\n }\n\n console.log('');\n }\n\n for (const entry of selected) {\n if (entry.type === 'single') {\n await removeSingleEntry(entry);\n } else {\n await removeGroupEntry(entry, config);\n }\n }\n\n console.log('');\n console.log(chalk.green(`Pruned ${selected.length} worktree(s).`));\n },\n};\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport { type WorkConfig } from './config.js';\nimport {\n parseWorktreeList,\n isBranchMerged,\n getCurrentBranch,\n getStatus,\n fetchRemote,\n type MergeConfidence,\n} from './git.js';\nimport { removeSingleWorktree } from './worktree.js';\nimport { removeSession } from './history.js';\n\nexport interface PrunableEntry {\n type: 'single' | 'group';\n /** repo alias (single) or group name (group) */\n target: string;\n branch: string;\n /** For single: [{ alias, repoPath, worktreePath }]. For group: one per sub-repo. */\n repos: Array<{\n alias: string;\n repoPath: string;\n worktreePath: string;\n }>;\n /**\n * True if any (sub-)repo in this entry has uncommitted changes. Used by\n * callers to decide whether removal needs the explicit --force escape hatch.\n */\n hasChanges: boolean;\n /**\n * Merge confidence for the whole entry. 'merged' is a high-confidence true\n * merge; 'squash-merged' means at least one (sub-)repo only matched via the\n * lower-confidence squash heuristic. Unattended callers should require an\n * explicit opt-in before pruning 'squash-merged' entries.\n */\n confidence: MergeConfidence;\n}\n\ninterface ScanEntry {\n target: string;\n branch: string;\n merged: boolean;\n into: string | null;\n hasChanges: boolean;\n /**\n * Merge confidence for this row. 'squash-merged' rows only matched the\n * lower-confidence squash heuristic and are gated out of the prunable list\n * by default — the table labels them distinctly so it never claims a row is\n * a plain 'merged' when prune/sync will actually skip it.\n */\n confidence: MergeConfidence | null;\n /** Sub-label for group repos, e.g. \"[straumur]\" */\n subLabel?: string;\n}\n\nexport interface CollectOptions {\n /**\n * When true, fetch each configured repo before scanning so merge checks use\n * up-to-date remote refs. Defaults to true (matches `work prune`). Callers\n * that already fetched (e.g. `work sync`, which fetches in parallel) pass\n * false to avoid redundant serial fetches.\n */\n fetch?: boolean;\n /** When false, suppress the per-target scan-result table. Defaults to true. */\n print?: boolean;\n /**\n * When true, include entries whose only positive signal is the\n * lower-confidence squash-merge heuristic. Defaults to false so unattended\n * callers (e.g. `work sync`) require true-merge confidence and only opt into\n * squash matches explicitly.\n */\n includeSquash?: boolean;\n /**\n * Aliases to skip entirely (e.g. repos whose fetch failed, so their remote\n * refs may be stale and merge checks unreliable). Worktrees in these repos\n * are never reported as prunable.\n */\n skipAliases?: Set<string>;\n}\n\nexport function collectPrunable(\n config: WorkConfig,\n options: CollectOptions = {},\n): PrunableEntry[] {\n const { fetch = true, print = true, includeSquash = false } = options;\n const skipAliases = options.skipAliases ?? new Set<string>();\n const prunable: PrunableEntry[] = [];\n const scanResults: ScanEntry[] = [];\n\n // Fetch all repos upfront so merge checks use up-to-date remote refs\n if (fetch) {\n for (const [alias, repoPath] of Object.entries(config.repos)) {\n if (!fs.existsSync(repoPath)) continue;\n console.log(chalk.gray(` Fetching ${alias}...`));\n fetchRemote(repoPath);\n }\n console.log('');\n }\n\n // Track branches already covered by a group so we don't double-list them\n const groupCoveredKeys = new Set<string>();\n\n // --- Groups ---\n for (const [groupName, aliases] of Object.entries(config.groups)) {\n const groupDir = path.join(config.worktreesRoot, groupName);\n if (!fs.existsSync(groupDir)) continue;\n\n let branchDirs: string[];\n try {\n branchDirs = fs\n .readdirSync(groupDir, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name);\n } catch {\n continue;\n }\n\n for (const branchDir of branchDirs) {\n const branchDirPath = path.join(groupDir, branchDir);\n let branch: string | undefined;\n let allMerged = true;\n let groupHasChanges = false;\n // Whole-group confidence is the weakest across sub-repos: if any sub-repo\n // only matched via the squash heuristic, the group is squash-confidence.\n let groupConfidence: MergeConfidence = 'merged';\n let skipped = false;\n const repos: PrunableEntry['repos'] = [];\n\n for (const alias of aliases) {\n const repoPath = config.repos[alias];\n if (!repoPath) continue;\n // Skip groups touching a repo we couldn't fetch — stale refs make the\n // merge check unreliable, so we must not prune on them.\n if (skipAliases.has(alias)) {\n skipped = true;\n continue;\n }\n const repoName = path.basename(repoPath);\n const subWorktreePath = path.join(branchDirPath, repoName);\n\n if (!fs.existsSync(subWorktreePath)) {\n continue;\n }\n\n const currentBranch = getCurrentBranch(subWorktreePath);\n if (!currentBranch) {\n allMerged = false;\n continue;\n }\n\n if (!branch) branch = currentBranch;\n\n const { merged, into, confidence } = isBranchMerged(currentBranch, repoPath);\n const changes = getStatus(subWorktreePath);\n if (changes) groupHasChanges = true;\n\n scanResults.push({\n target: groupName,\n branch,\n merged,\n into,\n hasChanges: !!changes,\n confidence,\n subLabel: alias,\n });\n\n if (!merged) {\n allMerged = false;\n } else if (confidence === 'squash-merged') {\n groupConfidence = 'squash-merged';\n }\n repos.push({ alias, repoPath, worktreePath: subWorktreePath });\n }\n\n const squashGated = groupConfidence === 'squash-merged' && !includeSquash;\n if (allMerged && !skipped && !squashGated && repos.length > 0 && branch) {\n prunable.push({\n type: 'group',\n target: groupName,\n branch,\n repos,\n hasChanges: groupHasChanges,\n confidence: groupConfidence,\n });\n\n for (const r of repos) {\n groupCoveredKeys.add(`${r.alias}:${branch}`);\n }\n }\n }\n }\n\n // --- Single repos ---\n for (const [alias, repoPath] of Object.entries(config.repos)) {\n if (!fs.existsSync(repoPath)) continue;\n // Skip repos we couldn't fetch — stale refs make merge checks unreliable.\n if (skipAliases.has(alias)) continue;\n\n // `git worktree list --porcelain` always emits the main worktree\n // (the original `.git` repo) first, with linked worktrees after.\n // Drop the head of the list rather than comparing paths — the path\n // comparison is fragile on Windows where `repoPath` may carry a\n // legacy 8.3 short-name segment (e.g. `C:\\Users\\DOMAGO~1\\...`)\n // while git always emits the canonical long-name form. `path.resolve`\n // doesn't bridge that gap, and `realpathSync` on Windows doesn't\n // expand 8.3 names either.\n const worktrees = parseWorktreeList(repoPath).slice(1);\n\n for (const wt of worktrees) {\n if (!wt.branch) continue;\n if (groupCoveredKeys.has(`${alias}:${wt.branch}`)) continue;\n\n const { merged, into, confidence } = isBranchMerged(wt.branch, repoPath);\n const changes = getStatus(wt.path);\n\n scanResults.push({\n target: alias,\n branch: wt.branch,\n merged,\n into,\n hasChanges: !!changes,\n confidence,\n });\n\n const squashGated = confidence === 'squash-merged' && !includeSquash;\n if (merged && confidence && !squashGated) {\n prunable.push({\n type: 'single',\n target: alias,\n branch: wt.branch,\n repos: [{ alias, repoPath, worktreePath: wt.path }],\n hasChanges: !!changes,\n confidence,\n });\n }\n }\n }\n\n // Print scan results grouped by target, merged first\n if (print) {\n printScanResults(scanResults);\n }\n\n return prunable;\n}\n\nexport function printScanResults(results: ScanEntry[]): void {\n // Group by target\n const byTarget = new Map<string, ScanEntry[]>();\n for (const r of results) {\n const entries = byTarget.get(r.target) ?? [];\n entries.push(r);\n byTarget.set(r.target, entries);\n }\n\n for (const [target, entries] of byTarget) {\n // Sort: not merged first, then merged\n entries.sort((a, b) => (a.merged === b.merged ? 0 : a.merged ? 1 : -1));\n\n console.log(chalk.cyan(`${target}:`));\n for (const e of entries) {\n const parts: string[] = [];\n if (!e.merged) {\n parts.push(chalk.red('not merged'));\n } else if (e.confidence === 'squash-merged') {\n // Distinct label: squash matches are skipped by default (sync needs\n // --include-squash), so don't show them as a plain 'merged'.\n parts.push(chalk.yellow(`squash-merged into ${e.into} (skipped unless --include-squash)`));\n } else {\n parts.push(chalk.green(`merged into ${e.into}`));\n }\n if (e.hasChanges) parts.push(chalk.yellow('uncommitted changes'));\n\n const label = e.subLabel\n ? `${e.branch} [${e.subLabel}]`\n : e.branch;\n console.log(` ${chalk.gray(`${label}:`)} ${parts.join(', ')}`);\n }\n console.log('');\n }\n}\n\n/**\n * Remove a single-repo prunable entry. Returns true only if the worktree was\n * actually removed (so callers can tally non-throwing failures). When `force`\n * is false, removeSingleWorktree refuses worktrees with uncommitted changes or\n * unpushed commits and returns false.\n */\nexport async function removeSingleEntry(\n entry: PrunableEntry,\n force = true,\n): Promise<boolean> {\n const { repoPath, worktreePath } = entry.repos[0];\n console.log(chalk.cyan(`Removing ${entry.target}: ${entry.branch}`));\n\n const removed = removeSingleWorktree(repoPath, worktreePath, entry.branch, force);\n if (removed) {\n await removeSession(entry.target, entry.branch);\n }\n return removed;\n}\n\n/**\n * Remove a group prunable entry (one worktree per sub-repo). Returns true only\n * if every sub-repo worktree was removed. When `force` is false, dirty/unpushed\n * sub-repos are refused and the entry is reported as a failure.\n */\nexport async function removeGroupEntry(\n entry: PrunableEntry,\n config: WorkConfig,\n force = true,\n): Promise<boolean> {\n const workTreeDirName = entry.branch.replace(/\\//g, '-');\n const groupWorktreePath = path.join(\n config.worktreesRoot,\n entry.target,\n workTreeDirName,\n );\n\n console.log(\n chalk.cyan(`Removing group ${entry.target}: ${entry.branch}`),\n );\n\n let allRemoved = true;\n\n for (const { alias, repoPath, worktreePath } of entry.repos) {\n const repoName = path.basename(repoPath);\n console.log(chalk.cyan(` [${alias}] (${repoName}):`));\n const removed = removeSingleWorktree(repoPath, worktreePath, entry.branch, force);\n if (!removed) {\n allRemoved = false;\n }\n }\n\n if (allRemoved) {\n await removeSession(entry.target, entry.branch);\n }\n\n // Clean up CLAUDE.md in group worktree\n const claudeMdInWorktree = path.join(groupWorktreePath, 'CLAUDE.md');\n if (fs.existsSync(claudeMdInWorktree)) {\n fs.unlinkSync(claudeMdInWorktree);\n }\n\n // Remove parent dir only if empty\n try {\n if (fs.existsSync(groupWorktreePath)) {\n const contents = fs.readdirSync(groupWorktreePath);\n if (contents.length === 0) {\n fs.rmSync(groupWorktreePath, { recursive: true, force: true });\n console.log(\n chalk.green(\n ` Cleaned up group directory: ${groupWorktreePath}`,\n ),\n );\n }\n }\n } catch {\n // ignore\n }\n\n return allRemoved;\n}\n","import fs from 'node:fs';\nimport chalk from 'chalk';\nimport type { CommandModule } from 'yargs';\nimport { ensureConfig } from '../core/config.js';\nimport { fetchRemoteAsync, getUnpushedCommits } from '../core/git.js';\nimport {\n collectPrunable,\n removeSingleEntry,\n removeGroupEntry,\n} from '../core/prunable-scan.js';\n\nexport const syncCommand: CommandModule = {\n command: 'sync',\n describe: 'Fetch all repos in parallel and prune merged worktrees (non-interactive)',\n builder: (yargs) =>\n yargs\n .option('dry-run', {\n describe: 'Show what would be pruned without removing anything',\n type: 'boolean',\n default: false,\n })\n .option('force', {\n describe:\n 'Force-remove worktrees with uncommitted changes or unpushed commits ' +\n '(default: false — such worktrees are skipped unless --force is given)',\n type: 'boolean',\n default: false,\n })\n .option('include-squash', {\n describe:\n 'Also prune branches detected only via the squash-merge heuristic ' +\n '(default: false — unattended sync requires true-merge confidence)',\n type: 'boolean',\n default: false,\n }),\n handler: async (argv) => {\n const dryRun = argv['dryRun'] as boolean;\n const force = argv['force'] as boolean;\n const includeSquash = argv['includeSquash'] as boolean;\n\n const config = ensureConfig();\n\n // Fetch all configured repos in parallel so merge checks use fresh refs.\n const repoEntries = Object.entries(config.repos).filter(([, repoPath]) =>\n fs.existsSync(repoPath),\n );\n\n // Repos whose fetch failed: their remote refs may be stale, so merge checks\n // are unreliable. Skip pruning any worktree backed by these repos.\n const skipAliases = new Set<string>();\n\n if (repoEntries.length > 0) {\n console.log(chalk.gray(`Fetching ${repoEntries.length} repo(s)...`));\n await Promise.all(\n repoEntries.map(async ([alias, repoPath]) => {\n try {\n await fetchRemoteAsync(repoPath);\n } catch (err) {\n skipAliases.add(alias);\n console.log(\n chalk.yellow(\n ` Warning: fetch failed for ${alias} — skipping prune for this ` +\n `repo (refs may be stale): ${\n err instanceof Error ? err.message : String(err)\n }`,\n ),\n );\n }\n }),\n );\n console.log('');\n }\n\n console.log(chalk.gray('Scanning worktrees for merged branches...\\n'));\n // We already fetched in parallel above; skip the serial fetch in collect.\n // print:false avoids double-printing the scan table — sync prints its own\n // summary below.\n const prunable = collectPrunable(config, {\n fetch: false,\n print: false,\n includeSquash,\n skipAliases,\n });\n\n if (prunable.length === 0) {\n console.log(chalk.green('No merged worktrees found. Everything is in sync.'));\n return;\n }\n\n console.log(chalk.cyan(`Found ${prunable.length} merged worktree(s):\\n`));\n for (const entry of prunable) {\n const suffix = entry.type === 'group' ? ' [group]' : '';\n console.log(` ${entry.target}: ${entry.branch}${suffix}`);\n }\n console.log('');\n\n if (dryRun) {\n console.log(\n chalk.yellow(`Dry run: would prune ${prunable.length} worktree(s). Nothing removed.`),\n );\n return;\n }\n\n let removed = 0;\n let failed = 0;\n let skippedDirty = 0;\n let skippedUnpushed = 0;\n for (const entry of prunable) {\n // Safe by default: never force-remove a worktree with uncommitted\n // changes or unpushed commits unless --force was passed explicitly.\n if (entry.hasChanges && !force) {\n skippedDirty++;\n console.log(\n chalk.yellow(\n ` Skipping ${entry.target}: ${entry.branch} — uncommitted changes ` +\n `(pass --force to remove anyway).`,\n ),\n );\n continue;\n }\n\n // A clean worktree may still hold commits not pushed to its upstream.\n // removeSingleWorktree(force=false) refuses these, which would otherwise\n // surface as a \"Failed to remove\" + exitCode=1. That's not a failure —\n // it's the same safe-by-default skip as uncommitted changes. Detect it\n // here and report it as a benign skip. (entry.hasChanges only covers the\n // working tree, not unpushed history.)\n if (!force) {\n const unpushedRepos = entry.repos.filter(\n (r) => getUnpushedCommits(r.worktreePath) !== '',\n );\n if (unpushedRepos.length > 0) {\n skippedUnpushed++;\n console.log(\n chalk.yellow(\n ` Skipping ${entry.target}: ${entry.branch} — unpushed commits ` +\n `(pass --force to remove anyway).`,\n ),\n );\n continue;\n }\n }\n\n try {\n const ok =\n entry.type === 'single'\n ? await removeSingleEntry(entry, force)\n : await removeGroupEntry(entry, config, force);\n if (ok) {\n removed++;\n } else {\n // Non-throwing failure (e.g. removeSingleWorktree refused or git\n // failed). Count it so process.exitCode is reliable for CI.\n failed++;\n console.log(\n chalk.red(\n ` Failed to remove ${entry.target}: ${entry.branch}`,\n ),\n );\n }\n } catch (err) {\n failed++;\n console.log(\n chalk.red(\n ` Failed to remove ${entry.target}: ${entry.branch} — ${\n err instanceof Error ? err.message : String(err)\n }`,\n ),\n );\n }\n }\n\n console.log('');\n console.log(chalk.green(`Pruned ${removed} worktree(s).`));\n if (skippedDirty > 0) {\n console.log(\n chalk.yellow(\n `Skipped ${skippedDirty} worktree(s) with local changes (use --force).`,\n ),\n );\n }\n if (skippedUnpushed > 0) {\n console.log(\n chalk.yellow(\n `Skipped ${skippedUnpushed} worktree(s) with unpushed commits (use --force).`,\n ),\n );\n }\n if (failed > 0) {\n console.log(chalk.red(`Failed to remove ${failed} worktree(s).`));\n process.exitCode = 1;\n }\n },\n};\n","import React from 'react';\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport { render } from 'ink';\r\nimport { App, runDashboardCleanup } from './App.js';\r\nimport { getConfigDir } from '../core/config.js';\r\n\r\n/** Synchronous crash log — survives hard exits where the async stream doesn't flush. */\r\nfunction crashLog(label: string, err: unknown): void {\r\n try {\r\n const msg = err instanceof Error ? err.stack || err.message : String(err);\r\n const line = `${new Date().toISOString()} [CRASH] ${label}: ${msg}\\n`;\r\n fs.appendFileSync(path.join(getConfigDir(), 'debug.log'), line);\r\n } catch { /* last resort — nothing we can do */ }\r\n}\r\n\r\nexport async function startDashboard(unsafe: boolean): Promise<void> {\r\n if (!process.stdin.isTTY) {\r\n console.error('work dash requires an interactive terminal');\r\n process.exit(1);\r\n }\r\n\r\n // Enter alternate screen buffer and hide cursor\r\n process.stdout.write('\\x1B[?1049h\\x1B[?25l');\r\n process.stdin.setRawMode(true);\r\n process.stdin.resume();\r\n\r\n const restore = () => {\r\n process.stdout.write('\\x1B]0;\\x07\\x1B[?25h\\x1B[?1049l');\r\n };\r\n\r\n try {\r\n const { waitUntilExit, unmount } = render(\r\n <App unsafe={unsafe} onExit={() => {\r\n unmount();\r\n restore();\r\n process.exit(0);\r\n }} />,\r\n {\r\n exitOnCtrlC: false,\r\n patchConsole: false,\r\n },\r\n );\r\n\r\n process.on('exit', restore);\r\n process.on('SIGINT', () => { runDashboardCleanup(); restore(); process.exit(0); });\r\n process.on('SIGTERM', () => { runDashboardCleanup(); restore(); process.exit(0); });\r\n process.on('uncaughtException', (err) => {\r\n // node-pty throws async errors for already-exited PTYs — non-fatal\r\n if (err instanceof Error && err.message?.includes('pty that has already exited')) return;\r\n crashLog('uncaughtException', err);\r\n runDashboardCleanup();\r\n unmount();\r\n restore();\r\n console.error('work dash error:', err);\r\n process.exit(1);\r\n });\r\n\r\n process.on('unhandledRejection', (reason) => {\r\n crashLog('unhandledRejection', reason);\r\n });\r\n\r\n await waitUntilExit();\r\n } catch (err) {\r\n crashLog('catch', err);\r\n restore();\r\n console.error('work dash error:', err);\r\n process.exit(1);\r\n }\r\n}\r\n","import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react';\nimport { Box } from 'ink';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { execFile } from 'node:child_process';\nimport spawn from 'cross-spawn';\nimport {\n loadHistory,\n getRecentSessions,\n upsertSession,\n getHistoryPath,\n type WorktreeSession,\n} from '../core/history.js';\nimport { loadConfig } from '../core/config.js';\nimport { rebaseOntoMain, countConflicts, isBranchMerged, fetchRemoteAsync } from '../core/git.js';\nimport { fetchAllPullRequests, isGhAvailable, type BranchPrMap } from '../core/pr.js';\nimport { fetchMyJiraIssues, isAcliAvailable, type JiraIssue } from '../core/jira.js';\nimport { getTasks, addTask, completeTask, uncompleteTask, removeTask, editTask, getTasksPath_, type Task } from '../core/tasks.js';\nimport { getAiTool, buildAiLaunchArgs } from '../core/ai-launcher.js';\nimport { openUrl } from '../utils/platform.js';\nimport { PtySession, type SessionStatus } from '../tui/session.js';\nimport { debug } from '../core/logger.js';\nimport { HookServer, type HookEvent } from '../core/hook-server.js';\nimport { notifyDesktop, notifyKindForEvent } from '../core/notifier.js';\nimport { runStatusHooks } from '../core/status-hooks.js';\nimport { renderBufferLines } from './renderer-lines.js';\nimport {\n Sidebar, PrPane, JiraPane, TaskPane,\n buildSessionRows, buildProjectRows, buildPrRows, buildJiraRows, buildTaskRows,\n countSelectable, cursorToRow, visualRowToCursor, sessionKey,\n type SidebarRow,\n} from './Sidebar.js';\nimport { TerminalPane } from './TerminalPane.js';\nimport { StatusBar } from './StatusBar.js';\n\nconst DETACH_KEY = '\\x1D'; // Ctrl+]\nconst TAB_KEY = '\\t';\nconst RENDER_INTERVAL_MS = 50;\n\n// SGR mouse: \\x1b[<button;col;rowM or \\x1b[<button;col;rowm\n// Button 64 = scroll up, 65 = scroll down\nconst MOUSE_SGR_RE = /\\x1b\\[<(\\d+);(\\d+);(\\d+)[Mm]/;\n\nenum Focus {\n SESSIONS,\n TASKS,\n PRS,\n JIRA,\n TERMINAL,\n}\n\n/** Top pane can show sessions (default) or projects (when creating new worktree). */\nenum TopPaneMode {\n SESSIONS,\n PROJECTS,\n BRANCH_INPUT,\n}\n\n\n/** Check if Claude has an existing conversation for a directory. */\nfunction hasClaudeConversation(dir: string): boolean {\n try {\n const resolved = path.resolve(dir);\n const projectDir = resolved.replace(/[/\\\\:]/g, '-');\n const claudeProjectPath = path.join(os.homedir(), '.claude', 'projects', projectDir);\n if (!fs.existsSync(claudeProjectPath)) return false;\n const files = fs.readdirSync(claudeProjectPath);\n return files.some((f) => f.endsWith('.jsonl'));\n } catch {\n return false;\n }\n}\n\n/**\n * Stable notification-dedup key for a PTY: its resolved launch directory.\n * findPtyByCwd matches hook cwds by prefix against this, so deriving the key\n * here collapses all sub-repo / group child cwds onto the one PtySession.\n */\nfunction ptyDedupKey(pty: PtySession): string {\n return path.resolve(pty.cwd).toLowerCase();\n}\n\nfunction loadSessions(): WorktreeSession[] {\n const all = loadHistory();\n return getRecentSessions(all, 50).filter((s) =>\n s.paths.some((p) => fs.existsSync(p)),\n );\n}\n\nfunction loadProjects(): Array<{ name: string; isGroup: boolean }> {\n const config = loadConfig();\n if (!config) return [];\n const projects: Array<{ name: string; isGroup: boolean }> = [];\n for (const name of Object.keys(config.groups)) {\n projects.push({ name, isGroup: true });\n }\n for (const name of Object.keys(config.repos)) {\n projects.push({ name, isGroup: false });\n }\n return projects;\n}\n\n/** Find the best project target for a repo alias: prefer group if it belongs to one. */\nfunction repoToProject(repoAlias: string, config: { repos: Record<string, string>; groups: Record<string, string[]> }): string {\n for (const [groupName, aliases] of Object.entries(config.groups)) {\n if (aliases.includes(repoAlias)) return groupName;\n }\n return repoAlias;\n}\n\nfunction mechanicalSlug(summary: string): string {\n return summary\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-|-$/g, '')\n .slice(0, 50);\n}\n\nfunction generateSlug(summary: string): Promise<string> {\n const fallback = mechanicalSlug(summary);\n return new Promise((resolve) => {\n const child = execFile(\n 'claude',\n ['-p', '--model', 'haiku'],\n { encoding: 'utf-8', timeout: 10000, windowsHide: true },\n (err, stdout) => {\n const result = stdout?.trim()\n .toLowerCase()\n .replace(/[^a-z0-9-]/g, '')\n .replace(/^-|-$/g, '');\n resolve(result || fallback);\n },\n );\n child.stdin?.write(\n `Generate a short git branch slug (max 40 chars, lowercase, hyphens only) for: ${summary}\\nOutput ONLY the slug, nothing else.`,\n );\n child.stdin?.end();\n });\n}\n\ninterface AppProps {\n unsafe: boolean;\n onExit: () => void;\n}\n\n/**\n * Module-level cleanup registry. App registers its PTY/HookServer cleanup\n * here so that index.tsx's signal handlers (SIGINT/SIGTERM/uncaughtException)\n * can run it before calling process.exit(), which otherwise leaves orphaned\n * `claude` processes and stale hook entries in ~/.claude/settings.json.\n */\nlet dashboardCleanup: (() => void) | null = null;\nexport function runDashboardCleanup(): void {\n dashboardCleanup?.();\n}\n\nexport function App({ unsafe, onExit }: AppProps) {\n const [focus, setFocus] = useState(Focus.SESSIONS);\n const [sessions, setSessions] = useState<WorktreeSession[]>(loadSessions);\n const [projects] = useState(loadProjects);\n const [config] = useState(() => loadConfig());\n const [sessionCursor, setSessionCursor] = useState(0);\n const [prCursor, setPrCursor] = useState(0);\n const [jiraCursor, setJiraCursor] = useState(0);\n const [taskCursor, setTaskCursor] = useState(0);\n const [tasks, setTasks] = useState<Task[]>(getTasks);\n const [taskInput, setTaskInput] = useState<string | null>(null);\n const [taskEditId, setTaskEditId] = useState<number | null>(null);\n const taskEditIdRef = useRef(taskEditId);\n const [jiraIssues, setJiraIssues] = useState<JiraIssue[]>([]);\n const [acliAvailable, setAcliAvailable] = useState(false);\n const jiraFetching = useRef(false);\n const [conflictCounts, setConflictCounts] = useState<Map<string, number>>(new Map());\n const [mergedSet, setMergedSet] = useState<Set<string>>(new Set());\n const [prMap, setPrMap] = useState<BranchPrMap>(new Map());\n const [ghAvailable, setGhAvailable] = useState(false);\n const [syncing, setSyncing] = useState(false);\n const prFetching = useRef(false);\n const [activeKey, setActiveKey] = useState<string | null>(null);\n const [message, setMessage] = useState('');\n const [termLines, setTermLines] = useState<string[]>([]);\n const termScrollBack = useRef(0);\n const [statusVersion, setStatusVersion] = useState(0);\n const [topPaneMode, setTopPaneMode] = useState(TopPaneMode.SESSIONS);\n const [branchInput, setBranchInput] = useState<{ projectName: string; value: string; isGroup: boolean } | null>(null);\n const [pendingBranch, setPendingBranch] = useState<string | null>(null);\n const [pendingJiraIssue, setPendingJiraIssue] = useState<JiraIssue | null>(null);\n const [pendingTask, setPendingTask] = useState<Task | null>(null);\n const [savedSessionCursor, setSavedSessionCursor] = useState(0);\n\n const localBranches = useMemo(() => new Set(sessions.map((s) => s.branch)), [sessions]);\n const projectRows = useMemo(() => buildProjectRows(projects), [projects]);\n const prRows = useMemo(() => buildPrRows(prMap), [prMap]);\n const jiraRows = useMemo(() => buildJiraRows(jiraIssues), [jiraIssues]);\n const taskRows = useMemo(() => buildTaskRows(tasks), [tasks]);\n\n const ptySessions = useRef(new Map<string, PtySession>());\n const sessionRows = useMemo(() => {\n const sMap = new Map<string, SessionStatus>();\n for (const [key, pty] of ptySessions.current) {\n if (pty.exited) continue;\n sMap.set(key, pty.idle ? 'idle' : 'running');\n }\n return buildSessionRows(sessions, sMap);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [sessions, statusVersion]);\n const topRows = topPaneMode === TopPaneMode.SESSIONS ? sessionRows : projectRows;\n const renderPending = useRef(false);\n const focusRef = useRef(focus);\n const activeKeyRef = useRef(activeKey);\n const sessionCursorRef = useRef(sessionCursor);\n const prCursorRef = useRef(prCursor);\n const jiraCursorRef = useRef(jiraCursor);\n const jiraRowsRef = useRef(jiraRows);\n const taskCursorRef = useRef(taskCursor);\n const taskRowsRef = useRef(taskRows);\n const taskInputRef = useRef(taskInput);\n const sessionsRef = useRef(sessions);\n const topPaneModeRef = useRef(topPaneMode);\n const branchInputRef = useRef(branchInput);\n const topRowsRef = useRef(topRows);\n const prRowsRef = useRef(prRows);\n\n // Keep refs in sync\n focusRef.current = focus;\n activeKeyRef.current = activeKey;\n sessionCursorRef.current = sessionCursor;\n prCursorRef.current = prCursor;\n jiraCursorRef.current = jiraCursor;\n jiraRowsRef.current = jiraRows;\n taskCursorRef.current = taskCursor;\n taskRowsRef.current = taskRows;\n taskInputRef.current = taskInput;\n taskEditIdRef.current = taskEditId;\n sessionsRef.current = sessions;\n topPaneModeRef.current = topPaneMode;\n branchInputRef.current = branchInput;\n const pendingBranchRef = useRef(pendingBranch);\n pendingBranchRef.current = pendingBranch;\n const pendingJiraIssueRef = useRef(pendingJiraIssue);\n pendingJiraIssueRef.current = pendingJiraIssue;\n const pendingTaskRef = useRef(pendingTask);\n pendingTaskRef.current = pendingTask;\n topRowsRef.current = topRows;\n prRowsRef.current = prRows;\n const acliAvailableRef = useRef(acliAvailable);\n acliAvailableRef.current = acliAvailable;\n const hookServerRef = useRef<HookServer | null>(null);\n // Sessions already alerted for their current idle period (notification dedupe).\n const notifiedSessionsRef = useRef<Set<string>>(new Set());\n\n // Layout — reactive to terminal resize\n const [dims, setDims] = useState(() => ({\n cols: process.stdout.columns || 80,\n rows: process.stdout.rows || 24,\n }));\n const cols = dims.cols;\n const rows = dims.rows;\n const sidebarWidth = Math.min(60, Math.floor(cols * 0.525));\n const termWidth = cols - sidebarWidth;\n const termInner = termWidth - 2;\n const contentHeight = rows - 1; // status bar\n // Split left column into 3 panes\n const jiraPaneHeight = acliAvailable ? Math.max(5, Math.floor(contentHeight * 0.20)) : 0;\n const taskPaneHeight = Math.max(5, Math.floor(contentHeight * 0.20));\n const prPaneHeight = Math.max(5, Math.floor((contentHeight - jiraPaneHeight - taskPaneHeight) * 0.45));\n const sessionPaneHeight = contentHeight - prPaneHeight - taskPaneHeight - jiraPaneHeight;\n\n // Update terminal title — icon + count per state, hide if 0\n useEffect(() => {\n let idleCount = 0;\n let runningCount = 0;\n for (const pty of ptySessions.current.values()) {\n if (pty.exited) continue;\n if (pty.idle) idleCount++;\n else runningCount++;\n }\n const parts: string[] = [];\n if (idleCount > 0) parts.push(`🟡 ${idleCount}`);\n if (runningCount > 0) parts.push(`🟢 ${runningCount}`);\n const title = parts.length > 0 ? parts.join(' ') : 'work dash';\n process.stdout.write(`\\x1B]0;${title}\\x07`);\n }, [statusVersion, activeKey]);\n\n const computeGeneration = useRef(0);\n const computeConflictsAndMerged = useCallback((sessionList: WorktreeSession[]) => {\n const generation = ++computeGeneration.current;\n const tick = () => new Promise<void>((r) => setTimeout(r, 0));\n\n (async () => {\n const counts = new Map<string, number>();\n const merged = new Set<string>();\n\n for (const s of sessionList) {\n if (computeGeneration.current !== generation) return; // cancelled\n await tick(); // yield to keep UI responsive (git calls are sync)\n\n const existing = s.paths.find((p) => fs.existsSync(p));\n if (!existing || !s.branch) continue;\n const key = sessionKey(s);\n try {\n const c = countConflicts(s.branch, existing);\n if (c > 0) counts.set(key, c);\n } catch { /* ignore */ }\n try {\n const { merged: isMerged } = isBranchMerged(s.branch, existing);\n if (isMerged) merged.add(key);\n } catch { /* ignore */ }\n }\n\n if (computeGeneration.current === generation) {\n setConflictCounts(counts);\n setMergedSet(merged);\n }\n })();\n }, []);\n\n const refreshPrs = useCallback(() => {\n if (!ghAvailable || !config || prFetching.current) return;\n prFetching.current = true;\n setMessage('Loading PRs...');\n fetchAllPullRequests(config.repos)\n .then((prs) => {\n setPrMap(prs);\n setMessage('');\n })\n .catch(() => {})\n .finally(() => { prFetching.current = false; });\n }, [ghAvailable, config]);\n\n const refreshJira = useCallback(() => {\n if (!acliAvailable || jiraFetching.current) return;\n jiraFetching.current = true;\n fetchMyJiraIssues()\n .then((issues) => setJiraIssues(issues))\n .catch(() => {})\n .finally(() => { jiraFetching.current = false; });\n }, [acliAvailable]);\n\n const refreshTasks = useCallback(() => {\n setTasks(getTasks());\n }, []);\n\n const refreshSessions = useCallback(() => {\n const updated = loadSessions();\n setSessions(updated);\n computeConflictsAndMerged(updated);\n }, [computeConflictsAndMerged]);\n\n const buildStatusMap = useCallback(() => {\n const map = new Map<string, 'stopped' | 'running' | 'idle'>();\n for (const [key, pty] of ptySessions.current) {\n if (pty.exited) continue;\n map.set(key, pty.idle ? 'idle' : 'running');\n }\n return map;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [statusVersion]);\n\n const scheduleTerminalRender = useCallback((pty: PtySession) => {\n if (renderPending.current) return;\n renderPending.current = true;\n setTimeout(() => {\n renderPending.current = false;\n try {\n // If scrolled back, don't auto-update (user is reading scrollback)\n if (termScrollBack.current > 0) return;\n const lines = renderBufferLines(pty.terminal.buffer.active, termInner, contentHeight - 2);\n setTermLines(lines);\n } catch { /* buffer not ready */ }\n }, RENDER_INTERVAL_MS);\n }, [termInner, contentHeight]);\n\n /** Re-render terminal at current scroll offset. */\n const renderTermAtScroll = useCallback(() => {\n if (!activeKeyRef.current) return;\n const pty = ptySessions.current.get(activeKeyRef.current);\n if (!pty || pty.exited) return;\n try {\n setTermLines(renderBufferLines(pty.terminal.buffer.active, termInner, contentHeight - 2, termScrollBack.current));\n } catch { /* */ }\n }, [termInner, contentHeight]);\n\n const findPtyByCwd = useCallback((cwd: string): PtySession | undefined => {\n const normalized = path.resolve(cwd).toLowerCase();\n for (const pty of ptySessions.current.values()) {\n if (pty.exited) continue;\n // Match if hook cwd is inside the PTY's launch directory\n if (normalized.startsWith(path.resolve(pty.cwd).toLowerCase())) return pty;\n }\n return undefined;\n }, []);\n\n // Enable SGR mouse tracking for scroll wheel support\n useEffect(() => {\n process.stdout.write('\\x1b[?1000h\\x1b[?1006h');\n return () => { process.stdout.write('\\x1b[?1000l\\x1b[?1006l'); };\n }, []);\n\n // Initial sync: fetch remotes, check gh, then refresh everything\n useEffect(() => {\n let cancelled = false;\n setSyncing(true);\n\n (async () => {\n const [, ghOk, acliOk] = await Promise.all([\n config\n ? Promise.all(\n Object.values(config.repos).map((repoPath) =>\n fetchRemoteAsync(repoPath).catch(() => {}),\n ),\n )\n : Promise.resolve(),\n isGhAvailable(),\n isAcliAvailable(),\n ]);\n\n if (cancelled) return;\n setSyncing(false);\n refreshSessions();\n setGhAvailable(ghOk);\n setAcliAvailable(acliOk);\n })();\n\n return () => { cancelled = true; };\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n // Watch history file for changes (e.g. when work tree creates a new session)\n useEffect(() => {\n const historyPath = getHistoryPath();\n try {\n const watcher = fs.watch(historyPath, () => refreshSessions());\n return () => watcher.close();\n } catch { /* file may not exist yet */ }\n }, [refreshSessions]);\n\n // Watch tasks file for external changes\n useEffect(() => {\n const tasksPath = getTasksPath_();\n try {\n const watcher = fs.watch(tasksPath, () => refreshTasks());\n return () => watcher.close();\n } catch { /* file may not exist yet */ }\n }, [refreshTasks]);\n\n // Fetch PRs once gh is confirmed available\n useEffect(() => {\n if (ghAvailable) refreshPrs();\n }, [ghAvailable, refreshPrs]);\n\n // Fetch Jira issues once acli is confirmed available\n useEffect(() => {\n if (acliAvailable) refreshJira();\n }, [acliAvailable, refreshJira]);\n\n // Hook server\n useEffect(() => {\n const hookServer = new HookServer({\n owner: 'dash',\n callback: (cwd: string, event: HookEvent) => {\n const pty = findPtyByCwd(cwd);\n if (!pty) return;\n if (event === 'stop' || event === 'notification') {\n pty.setIdle(true);\n } else if (event === 'prompt_submit') {\n pty.setIdle(false);\n }\n // Desktop notification (opt-in; no-op when disabled/unsupported).\n // De-duped per session per idle period via notifyKindForEvent, so the\n // first idle alerts and repeated Stop events don't spam — independent\n // of PtySession's initial idle state.\n //\n // The dedup key is derived from the resolved PtySession identity\n // (its launch dir), NOT the raw hook cwd. findPtyByCwd matches by\n // prefix, so multiple sub-repo / group cwds resolve to one PtySession;\n // keying off the raw cwd would let that single session notify multiple\n // times for distinct child paths.\n const dedupKey = ptyDedupKey(pty);\n const kind = notifyKindForEvent(\n event,\n dedupKey,\n notifiedSessionsRef.current,\n );\n if (kind) {\n notifyDesktop(path.basename(pty.cwd), kind, {\n enabled: config?.notifications === true,\n });\n // User-configurable status-change commands (opt-in via config).\n // Fire-and-forget; independent of the desktop-notification path.\n // Use the resolved PtySession launch dir (pty.cwd), not the raw hook\n // cwd, so a group/sub-repo cwd doesn't run the hook in a sub-repo\n // directory or expose a sub-repo basename as $WORK_SESSION — matching\n // the notifyDesktop call above.\n runStatusHooks(\n kind,\n pty.cwd,\n path.basename(pty.cwd),\n config?.statusHooks,\n );\n }\n },\n });\n hookServerRef.current = hookServer;\n hookServer.start().catch(() => {});\n return () => {\n if (hookServerRef.current === hookServer) hookServerRef.current = null;\n hookServer.stop().catch(() => {});\n };\n }, [findPtyByCwd]);\n\n // Reliable cleanup that also runs on signal/crash exit (not just React\n // unmount). Disposes every live PTY (kills spawned `claude` processes) and\n // restores the injected Claude hook settings synchronously via\n // hookServer.cleanupSync(). Idempotent — guarded against double-run.\n useEffect(() => {\n let done = false;\n const cleanup = () => {\n if (done) return;\n done = true;\n try {\n for (const pty of ptySessions.current.values()) {\n try { pty.dispose(); } catch { /* already gone */ }\n }\n ptySessions.current.clear();\n notifiedSessionsRef.current.clear();\n } catch { /* best effort */ }\n // cleanupSync() is synchronous fs work, safe in a process 'exit' handler.\n try { hookServerRef.current?.cleanupSync(); } catch { /* best effort */ }\n };\n dashboardCleanup = cleanup;\n process.once('exit', cleanup);\n return () => {\n process.removeListener('exit', cleanup);\n if (dashboardCleanup === cleanup) dashboardCleanup = null;\n };\n }, []);\n\n /** Switch the right pane to show a different session's terminal. */\n const switchDisplay = useCallback((s: WorktreeSession) => {\n const k = sessionKey(s);\n if (activeKeyRef.current === k) return;\n\n termScrollBack.current = 0;\n if (activeKeyRef.current) {\n ptySessions.current.get(activeKeyRef.current)?.setOutputHandler(undefined);\n }\n\n setActiveKey(k);\n\n const pty = ptySessions.current.get(k);\n if (pty && !pty.exited) {\n pty.resize(termInner, contentHeight - 2);\n pty.setOutputHandler(() => scheduleTerminalRender(pty));\n try {\n setTermLines(renderBufferLines(pty.terminal.buffer.active, termInner, contentHeight - 2));\n } catch { /* */ }\n } else {\n setTermLines([]);\n }\n }, [termInner, contentHeight, scheduleTerminalRender]);\n\n /** Move cursor within the top (session/project) pane. */\n const moveSessionCursor = useCallback((delta: number) => {\n const max = countSelectable(topRowsRef.current) - 1;\n if (max < 0) return;\n const next = Math.max(0, Math.min(max, sessionCursorRef.current + delta));\n setSessionCursor(next);\n\n // Auto-switch display if in sessions mode\n if (topPaneModeRef.current === TopPaneMode.SESSIONS) {\n const row = cursorToRow(topRowsRef.current, next);\n if (row?.type === 'session') {\n switchDisplay(row.session);\n }\n }\n }, [switchDisplay]);\n\n /** Move cursor within the PR pane. */\n const movePrCursor = useCallback((delta: number) => {\n const max = countSelectable(prRowsRef.current) - 1;\n if (max < 0) return;\n const next = Math.max(0, Math.min(max, prCursorRef.current + delta));\n setPrCursor(next);\n }, []);\n\n /** Move cursor within the Jira pane. */\n const moveJiraCursor = useCallback((delta: number) => {\n const max = countSelectable(jiraRowsRef.current) - 1;\n if (max < 0) return;\n const next = Math.max(0, Math.min(max, jiraCursorRef.current + delta));\n setJiraCursor(next);\n }, []);\n\n const moveTaskCursor = useCallback((delta: number) => {\n const max = countSelectable(taskRowsRef.current) - 1;\n if (max < 0) return;\n const next = Math.max(0, Math.min(max, taskCursorRef.current + delta));\n setTaskCursor(next);\n }, []);\n\n /** Connect a PTY to the display and focus on it. */\n const connectPty = useCallback((key: string, pty: PtySession) => {\n if (activeKeyRef.current && activeKeyRef.current !== key) {\n ptySessions.current.get(activeKeyRef.current)?.setOutputHandler(undefined);\n }\n setActiveKey(key);\n setFocus(Focus.TERMINAL);\n pty.resize(termInner, contentHeight - 2);\n pty.setOutputHandler(() => scheduleTerminalRender(pty));\n try {\n setTermLines(renderBufferLines(pty.terminal.buffer.active, termInner, contentHeight - 2));\n } catch { /* */ }\n }, [termInner, contentHeight, scheduleTerminalRender]);\n\n /** Register a PTY: wire up exit/status handlers and track in the sessions map. */\n const registerPty = useCallback((key: string, pty: PtySession, exitMessage: string, onExitExtra?: () => void) => {\n ptySessions.current.set(key, pty);\n pty.onExit = (code: number) => {\n debug('onExit PTY', { key, code });\n // Evict the notification-dedup entry so a future PTY for the same dir\n // re-arms (and the Set doesn't grow unbounded across the session).\n notifiedSessionsRef.current.delete(ptyDedupKey(pty));\n pty.dispose();\n ptySessions.current.delete(key);\n if (activeKeyRef.current === key) {\n setActiveKey(null);\n setFocus(Focus.SESSIONS);\n setTermLines([]);\n setMessage(`${exitMessage} (code ${code})`);\n }\n setStatusVersion((v) => v + 1);\n onExitExtra?.();\n };\n pty.onStatusChange = () => setStatusVersion((v) => v + 1);\n setStatusVersion((v) => v + 1);\n }, []);\n\n const startPtyForSession = useCallback((s: WorktreeSession, key: string) => {\n const existing = s.paths.find((p) => fs.existsSync(p));\n if (!existing) {\n setMessage('Session path no longer exists');\n refreshSessions();\n return;\n }\n\n const dir = s.isGroup ? path.dirname(existing) : existing;\n const resume = hasClaudeConversation(dir);\n const tool = getAiTool(config ?? {});\n const pty = new PtySession(dir, termInner, contentHeight - 2, undefined, { tool, unsafe, resume, port: s.port });\n void upsertSession(s.target, s.isGroup, s.branch, s.paths).catch((err) =>\n setMessage(`Failed to save session: ${(err as Error).message}`),\n );\n registerPty(key, pty, `Session exited: ${s.target} / ${s.branch}`);\n return pty;\n }, [unsafe, termInner, contentHeight, refreshSessions, registerPty, config]);\n\n const activateSession = useCallback((s: WorktreeSession) => {\n const key = sessionKey(s);\n let pty = ptySessions.current.get(key);\n\n if (!pty || pty.exited) {\n pty = startPtyForSession(s, key);\n if (!pty) return;\n }\n connectPty(key, pty);\n }, [startPtyForSession, connectPty]);\n\n const syncSessionCursorToActive = useCallback(() => {\n if (!activeKeyRef.current) return;\n let selectableIdx = 0;\n for (const row of topRowsRef.current) {\n if (row.type === 'header') continue;\n if (row.type === 'session' && sessionKey(row.session) === activeKeyRef.current) {\n setSessionCursor(selectableIdx);\n return;\n }\n selectableIdx++;\n }\n }, []);\n\n /** Launch Claude directly in the base repo (no worktree). */\n const handleLaunchBaseRepo = useCallback((projectName: string) => {\n setTopPaneMode(TopPaneMode.SESSIONS);\n setSessionCursor(savedSessionCursor);\n\n if (!config) return;\n const repoPath = config.repos[projectName];\n if (!repoPath || !fs.existsSync(repoPath)) {\n setMessage(`Repo path not found for ${projectName}`);\n return;\n }\n\n const branch = '(base)';\n const key = `${projectName}:${branch}`;\n void upsertSession(projectName, false, branch, [repoPath]).catch((err) =>\n setMessage(`Failed to save session: ${(err as Error).message}`),\n );\n refreshSessions();\n\n const tool = getAiTool(config);\n const pty = new PtySession(repoPath, termInner, contentHeight - 2, undefined, { tool, unsafe });\n registerPty(key, pty, `Session exited: ${projectName} (base)`);\n connectPty(key, pty);\n setMessage(`Launched: ${projectName} (base repo)`);\n }, [unsafe, termInner, contentHeight, connectPty, refreshSessions, savedSessionCursor, config]);\n\n /** Create a worktree (visible in terminal pane) then launch Claude in it. */\n const handleCreateWorktree = useCallback((projectName: string, branchName: string, jiraIssue?: JiraIssue | null) => {\n setTopPaneMode(TopPaneMode.SESSIONS);\n setSessionCursor(savedSessionCursor);\n\n if (!config) { setMessage('No config loaded'); return; }\n\n const key = `${projectName}:${branchName}`;\n\n // Build prompt file for Jira issues\n let promptFile: string | undefined;\n if (jiraIssue) {\n const prompt = [\n `Read Jira issue ${jiraIssue.key} (${jiraIssue.url}) and determine how to handle it.`,\n '',\n '1. Read the Jira issue details to understand requirements, acceptance criteria, and context.',\n '',\n '2. Classify the issue:',\n ' - **One-time data task**: The issue asks for a data lookup, data fix, report, or one-off operation (e.g., \"find all X\", \"update Y records\", \"generate a report for Z\").',\n ' - **One-time script/query**: The issue asks for a SQL query, a script, or a one-off test client invocation — not a permanent code change.',\n ' - **Code change**: The issue requires modifying the codebase (bug fix, feature, refactor).',\n '',\n '3. Based on the classification:',\n '',\n ' **If one-time data task or script/query:**',\n ' - Generate the SQL query, script, or test client code directly.',\n ' - If a test client or similar tool exists in the codebase, use it.',\n ' - Present the output ready to run. Explain what it does and any parameters to adjust.',\n ' - Ask if I want to run it, modify it, or need something different.',\n '',\n ' **If code change:**',\n ' - Analyze the codebase to understand which files/components need changes, existing patterns, dependencies, and impact areas.',\n ' - Present a structured implementation plan:',\n '',\n ' **Summary**: Brief overview of what the issue requires.',\n '',\n ' **Affected Areas**: List the files, components, or modules that will need changes.',\n '',\n ' **Implementation Approach**:',\n ' - For simple issues (bug fixes, small features): provide a single clear approach with step-by-step details.',\n ' - For complex issues (new features, architectural changes): present 2-3 alternative approaches, each with Description, Pros, Cons, and Effort (Low/Medium/High). Include a Recommendation with reasoning.',\n '',\n ' **Key Considerations**: Security, performance, testing requirements, migration needs, backwards compatibility.',\n '',\n ' **Next Steps**: Ordered list of implementation tasks, ready to be executed.',\n '',\n ' - Ask if I want to proceed with the recommended approach, choose a different one, get more details, or make adjustments.',\n ].join('\\n');\n promptFile = path.join(os.tmpdir(), `work-prompt-${Date.now()}.txt`);\n fs.writeFileSync(promptFile, prompt, 'utf-8');\n }\n\n // Phase 1: Run `work tree --setup-only` in a PTY to show setup progress\n const setupArgs = ['tree', projectName, branchName, '--setup-only'];\n if (jiraIssue?.key) setupArgs.push('--jira-key', jiraIssue.key);\n const setupPty = new PtySession(process.cwd(), termInner, contentHeight - 2,\n { cmd: 'work', args: setupArgs });\n ptySessions.current.set(key, setupPty);\n setStatusVersion((v) => v + 1);\n\n setupPty.onExit = (code: number) => {\n setupPty.dispose();\n ptySessions.current.delete(key);\n\n if (code !== 0) {\n setMessage(`Failed to create worktree: ${projectName}/${branchName}`);\n if (activeKeyRef.current === key) {\n setActiveKey(null);\n setFocus(Focus.SESSIONS);\n setTermLines([]);\n }\n setStatusVersion((v) => v + 1);\n return;\n }\n\n // Phase 2: Setup succeeded — refresh sessions and launch Claude in the worktree\n refreshSessions();\n const sessions = getRecentSessions(loadHistory(), 50);\n const session = sessions.find((s) => s.target === projectName && s.branch === branchName);\n if (!session) {\n setMessage(`Worktree created but session not found: ${projectName}/${branchName}`);\n setStatusVersion((v) => v + 1);\n return;\n }\n\n const existing = session.paths.find((p) => fs.existsSync(p));\n if (!existing) {\n setMessage(`Worktree path not found after setup`);\n setStatusVersion((v) => v + 1);\n return;\n }\n\n const launchDir = session.isGroup ? path.dirname(existing) : existing;\n const tool = getAiTool(config);\n const aiPty = new PtySession(launchDir, termInner, contentHeight - 2, undefined,\n { tool, unsafe, promptFile, port: session.port });\n registerPty(key, aiPty, `Session exited: ${projectName} / ${branchName}`);\n connectPty(key, aiPty);\n setMessage(`Launched: ${projectName}/${branchName}`);\n };\n\n setMessage(`Creating: ${projectName}/${branchName}...`);\n connectPty(key, setupPty);\n }, [unsafe, termInner, contentHeight, refreshSessions, connectPty, registerPty, savedSessionCursor, config]);\n\n // Raw stdin input handling\n useEffect(() => {\n const handler = (data: Buffer) => {\n const key = data.toString('utf8');\n\n // Mouse wheel: scroll the pane under the cursor\n const mouseMatch = MOUSE_SGR_RE.exec(key);\n if (mouseMatch) {\n const button = parseInt(mouseMatch[1], 10);\n const col = parseInt(mouseMatch[2], 10);\n const row = parseInt(mouseMatch[3], 10);\n if (button === 64 || button === 65) {\n const delta = button === 64 ? -1 : 1;\n // Determine which pane the mouse is over based on column and row\n if (col <= sidebarWidth) {\n // Left column — determine which pane by row\n if (row <= sessionPaneHeight) {\n moveSessionCursor(delta);\n } else if (row <= sessionPaneHeight + prPaneHeight) {\n movePrCursor(delta);\n } else if (jiraPaneHeight > 0 && row <= sessionPaneHeight + prPaneHeight + jiraPaneHeight) {\n moveJiraCursor(delta);\n } else {\n moveTaskCursor(delta);\n }\n } else {\n // Right column (terminal) — scroll through scrollback buffer\n const pty = activeKeyRef.current ? ptySessions.current.get(activeKeyRef.current) : undefined;\n if (pty && !pty.exited) {\n const maxScroll = pty.terminal.buffer.active.baseY;\n const newScroll = Math.max(0, Math.min(maxScroll, termScrollBack.current - delta * 3));\n if (newScroll !== termScrollBack.current) {\n termScrollBack.current = newScroll;\n renderTermAtScroll();\n }\n }\n }\n return;\n }\n // Left click (button 0): focus the pane under cursor and select the clicked row\n if (button === 0) {\n if (col <= sidebarWidth) {\n // row is 1-indexed, each pane has a 1-row top border, so content starts at paneStart + 2\n if (row <= sessionPaneHeight) {\n setFocus(Focus.SESSIONS);\n const visualIdx = row - 2; // skip top border\n if (visualIdx >= 0) setSessionCursor(visualRowToCursor(topRowsRef.current, visualIdx));\n } else if (row <= sessionPaneHeight + prPaneHeight) {\n setFocus(Focus.PRS);\n const visualIdx = row - sessionPaneHeight - 2;\n if (visualIdx >= 0) setPrCursor(visualRowToCursor(prRowsRef.current, visualIdx));\n } else if (jiraPaneHeight > 0 && row <= sessionPaneHeight + prPaneHeight + jiraPaneHeight) {\n setFocus(Focus.JIRA);\n const visualIdx = row - sessionPaneHeight - prPaneHeight - 2;\n if (visualIdx >= 0) setJiraCursor(visualRowToCursor(jiraRowsRef.current, visualIdx));\n } else {\n setFocus(Focus.TASKS);\n const taskStart = sessionPaneHeight + prPaneHeight + jiraPaneHeight;\n const visualIdx = row - taskStart - 2;\n if (visualIdx >= 0) setTaskCursor(visualRowToCursor(taskRowsRef.current, visualIdx));\n }\n } else {\n const activePty = activeKeyRef.current ? ptySessions.current.get(activeKeyRef.current) : undefined;\n if (activePty && !activePty.exited) setFocus(Focus.TERMINAL);\n }\n setMessage('');\n return;\n }\n // Ignore other mouse events\n return;\n }\n\n // Branch input mode (top pane)\n if (branchInputRef.current) {\n if (key === '\\x1B' || key === '\\x03') {\n setBranchInput(null);\n setTopPaneMode(TopPaneMode.PROJECTS);\n setMessage('Esc to go back');\n return;\n }\n if (key === '\\r') {\n const { projectName, value, isGroup } = branchInputRef.current;\n setBranchInput(null);\n if (value.trim()) {\n handleCreateWorktree(projectName, value.trim());\n } else if (!isGroup) {\n handleLaunchBaseRepo(projectName);\n } else {\n setMessage('Branch name is required for groups');\n }\n return;\n }\n if (key === '\\x7F' || key === '\\b') {\n setBranchInput((prev) => prev ? { ...prev, value: prev.value.slice(0, -1) } : null);\n return;\n }\n if (key.charCodeAt(0) < 32) return;\n setBranchInput((prev) => prev ? { ...prev, value: prev.value + key } : null);\n return;\n }\n\n // Tab: cycle focus SESSIONS → PRS → JIRA → TERMINAL → SESSIONS\n if (key === TAB_KEY) {\n if (topPaneModeRef.current !== TopPaneMode.SESSIONS) return;\n const activePty = activeKeyRef.current ? ptySessions.current.get(activeKeyRef.current) : undefined;\n const hasTerminal = activePty && !activePty.exited;\n\n if (focusRef.current === Focus.SESSIONS) {\n setFocus(Focus.PRS);\n } else if (focusRef.current === Focus.PRS) {\n setFocus(acliAvailableRef.current ? Focus.JIRA : Focus.TASKS);\n } else if (focusRef.current === Focus.JIRA) {\n setFocus(Focus.TASKS);\n } else if (focusRef.current === Focus.TASKS) {\n if (hasTerminal) setFocus(Focus.TERMINAL);\n else { setFocus(Focus.SESSIONS); syncSessionCursorToActive(); }\n } else {\n setFocus(Focus.SESSIONS);\n syncSessionCursorToActive();\n }\n setMessage('');\n return;\n }\n\n // Terminal mode\n if (focusRef.current === Focus.TERMINAL) {\n if (key === DETACH_KEY) {\n setFocus(Focus.SESSIONS);\n syncSessionCursorToActive();\n setMessage('');\n return;\n }\n // Reset scroll to bottom when typing\n termScrollBack.current = 0;\n ptySessions.current.get(activeKeyRef.current!)?.write(key);\n return;\n }\n\n // --- Left pane navigation ---\n setMessage('');\n\n // Global sync (G = shift+g) from any left pane\n if (key === 'G') {\n if (syncing) return;\n setSyncing(true);\n setMessage('Syncing all...');\n (async () => {\n if (config) {\n await Promise.all(\n Object.values(config.repos).map((repoPath) =>\n fetchRemoteAsync(repoPath).catch(() => {}),\n ),\n );\n }\n refreshSessions();\n refreshPrs();\n refreshJira();\n refreshTasks();\n computeConflictsAndMerged(sessionsRef.current);\n setSyncing(false);\n setMessage('All synced');\n })();\n return;\n }\n\n // Project picker mode (top pane)\n if (topPaneModeRef.current === TopPaneMode.PROJECTS) {\n if (key === '\\x1B' || key === '\\x03' || key === 'q') {\n setTopPaneMode(TopPaneMode.SESSIONS);\n setSessionCursor(savedSessionCursor);\n setPendingBranch(null);\n setPendingJiraIssue(null);\n setPendingTask(null);\n setMessage('');\n return;\n }\n if (key === '\\x1B[A' || key === 'k') { moveSessionCursor(-1); return; }\n if (key === '\\x1B[B' || key === 'j') { moveSessionCursor(1); return; }\n if (key === '\\r') {\n const row = cursorToRow(topRowsRef.current, sessionCursorRef.current);\n if (row?.type === 'project') {\n const issue = pendingJiraIssueRef.current;\n const task = pendingTaskRef.current;\n if (issue) {\n // Jira flow: generate slug then create worktree in PTY\n setPendingJiraIssue(null);\n setMessage(`Generating branch name for ${issue.key}...`);\n const keyLower = issue.key.toLowerCase();\n generateSlug(issue.summary).then((slug) => {\n const branchName = `task/${keyLower}-${slug}`;\n handleCreateWorktree(row.name, branchName, issue);\n });\n } else if (task) {\n // Task flow: generate slug from task text\n setPendingTask(null);\n setMessage(`Generating branch name for task #${task.id}...`);\n generateSlug(task.text).then((slug) => {\n const branchName = `todo/${slug}`;\n handleCreateWorktree(row.name, branchName);\n });\n } else if (pendingBranchRef.current) {\n const branch = pendingBranchRef.current;\n setPendingBranch(null);\n handleCreateWorktree(row.name, branch);\n } else {\n setTopPaneMode(TopPaneMode.BRANCH_INPUT);\n setBranchInput({ projectName: row.name, value: '', isGroup: row.isGroup });\n setMessage(row.isGroup\n ? 'Type branch name, Enter to create, Esc to cancel'\n : 'Type branch name (empty for base repo), Enter to create, Esc to cancel');\n }\n }\n return;\n }\n return;\n }\n\n // Tasks pane focused\n if (focusRef.current === Focus.TASKS) {\n // Task input mode\n if (taskInputRef.current !== null) {\n if (key === '\\x1B' || key === '\\x03') {\n setTaskInput(null);\n setTaskEditId(null);\n setMessage('');\n return;\n }\n if (key === '\\r') {\n const text = taskInputRef.current.trim();\n const editId = taskEditIdRef.current;\n setTaskInput(null);\n setTaskEditId(null);\n if (text) {\n if (editId !== null) {\n void editTask(editId, text).then(() => refreshTasks());\n setMessage(`Updated task #${editId}`);\n } else {\n void addTask(text).then(() => refreshTasks());\n setMessage(`Added task: ${text}`);\n }\n }\n return;\n }\n if (key === '\\x7F' || key === '\\b') {\n setTaskInput((prev) => prev !== null ? prev.slice(0, -1) : null);\n return;\n }\n if (key.charCodeAt(0) < 32) return;\n setTaskInput((prev) => prev !== null ? prev + key : null);\n return;\n }\n\n if (key === '\\x1B[A' || key === 'k') { moveTaskCursor(-1); return; }\n if (key === '\\x1B[B' || key === 'j') { moveTaskCursor(1); return; }\n\n // Toggle done/undone\n if (key === '\\r' || key === 'x') {\n const row = cursorToRow(taskRowsRef.current, taskCursorRef.current);\n if (row?.type === 'task') {\n const p = row.task.done\n ? uncompleteTask(row.task.id)\n : completeTask(row.task.id);\n void p.then(() => refreshTasks());\n }\n return;\n }\n\n // Add new task\n if (key === 'a') {\n setTaskInput('');\n setMessage('Type task, Enter to add, Esc to cancel');\n return;\n }\n\n // Edit task\n if (key === 'e') {\n const row = cursorToRow(taskRowsRef.current, taskCursorRef.current);\n if (row?.type === 'task') {\n setTaskEditId(row.task.id);\n setTaskInput(row.task.text);\n setMessage('Edit task, Enter to save, Esc to cancel');\n }\n return;\n }\n\n // Create worktree for task\n if (key === 'w') {\n const row = cursorToRow(taskRowsRef.current, taskCursorRef.current);\n if (row?.type === 'task') {\n setPendingTask(row.task);\n setSavedSessionCursor(sessionCursorRef.current);\n setFocus(Focus.SESSIONS);\n setTopPaneMode(TopPaneMode.PROJECTS);\n setSessionCursor(0);\n setMessage(`Select project for task #${row.task.id}`);\n }\n return;\n }\n\n // Remove task\n if (key === 'd') {\n const row = cursorToRow(taskRowsRef.current, taskCursorRef.current);\n if (row?.type === 'task') {\n void removeTask(row.task.id).then(() => refreshTasks());\n setMessage(`Removed: ${row.task.text}`);\n }\n return;\n }\n\n if (key === 'g') {\n refreshTasks();\n setMessage('Tasks refreshed');\n return;\n }\n\n if (key === '\\x03' || key === 'q') {\n for (const pty of ptySessions.current.values()) pty.dispose();\n ptySessions.current.clear();\n onExit();\n return;\n }\n\n return;\n }\n\n // Jira pane focused\n if (focusRef.current === Focus.JIRA) {\n if (key === '\\x1B[A' || key === 'k') { moveJiraCursor(-1); return; }\n if (key === '\\x1B[B' || key === 'j') { moveJiraCursor(1); return; }\n\n if (key === '\\r') {\n const row = cursorToRow(jiraRowsRef.current, jiraCursorRef.current);\n if (row?.type === 'jira') {\n const issue = row.issue;\n\n // Check if any existing session is linked to this Jira issue\n const existing = sessionsRef.current.find((s) =>\n s.jiraKey === issue.key,\n );\n if (existing) {\n setFocus(Focus.SESSIONS);\n activateSession(existing);\n setMessage(`Resumed: ${existing.target}/${existing.branch}`);\n } else {\n // Show project picker immediately; slug generation happens after selection\n setPendingJiraIssue(issue);\n setSavedSessionCursor(sessionCursorRef.current);\n setFocus(Focus.SESSIONS);\n setTopPaneMode(TopPaneMode.PROJECTS);\n setSessionCursor(0);\n setMessage(`Select project for ${issue.key}`);\n }\n }\n return;\n }\n\n if (key === 'o') {\n const row = cursorToRow(jiraRowsRef.current, jiraCursorRef.current);\n if (row?.type === 'jira' && row.issue.url) {\n openUrl(row.issue.url);\n setMessage(`Opened: ${row.issue.key}`);\n }\n return;\n }\n\n if (key === '\\x03' || key === 'q') {\n for (const pty of ptySessions.current.values()) pty.dispose();\n ptySessions.current.clear();\n onExit();\n return;\n }\n\n if (key === 'g') {\n if (syncing) return;\n setSyncing(true);\n setMessage('Syncing Jira...');\n (async () => {\n await refreshJira();\n setSyncing(false);\n setMessage('Jira synced');\n })();\n return;\n }\n\n return;\n }\n\n // PR pane focused\n if (focusRef.current === Focus.PRS) {\n if (key === '\\x1B[A' || key === 'k') { movePrCursor(-1); return; }\n if (key === '\\x1B[B' || key === 'j') { movePrCursor(1); return; }\n\n if (key === '\\r') {\n const row = cursorToRow(prRowsRef.current, prCursorRef.current);\n if (row?.type === 'pr' && config) {\n const pr = row.pr;\n const projectName = repoToProject(pr.repoAlias, config);\n\n // Check if any session already has this branch (regardless of target name)\n const existing = sessionsRef.current.find(\n (s) => s.branch === pr.branch,\n );\n if (existing) {\n setFocus(Focus.SESSIONS);\n activateSession(existing);\n setMessage(`Resumed: ${existing.target}/${pr.branch}`);\n } else {\n handleCreateWorktree(projectName, pr.branch);\n }\n }\n return;\n }\n\n if (key === 'o') {\n const row = cursorToRow(prRowsRef.current, prCursorRef.current);\n if (row?.type === 'pr' && row.pr.url) {\n openUrl(row.pr.url);\n setMessage(`Opened: ${row.pr.title}`);\n }\n return;\n }\n\n if (key === '\\x03' || key === 'q') {\n for (const pty of ptySessions.current.values()) pty.dispose();\n ptySessions.current.clear();\n onExit();\n return;\n }\n\n if (key === 'g') {\n if (syncing) return;\n setSyncing(true);\n setMessage('Syncing PRs...');\n (async () => {\n await refreshPrs();\n setSyncing(false);\n setMessage('PRs synced');\n })();\n return;\n }\n\n return;\n }\n\n // Sessions pane focused\n if (key === '\\x03' || key === 'q') {\n for (const pty of ptySessions.current.values()) pty.dispose();\n ptySessions.current.clear();\n onExit();\n return;\n }\n\n if (key === '\\x1B[A' || key === 'k') { moveSessionCursor(-1); return; }\n if (key === '\\x1B[B' || key === 'j') { moveSessionCursor(1); return; }\n\n if (key === '\\r') {\n const row = cursorToRow(topRowsRef.current, sessionCursorRef.current);\n if (row?.type === 'session') {\n activateSession(row.session);\n }\n return;\n }\n\n if (key === 'd') {\n const row = cursorToRow(topRowsRef.current, sessionCursorRef.current);\n if (row?.type !== 'session') return;\n\n const s = row.session;\n const k = sessionKey(s);\n const pty = ptySessions.current.get(k);\n if (pty) {\n notifiedSessionsRef.current.delete(ptyDedupKey(pty));\n pty.dispose();\n ptySessions.current.delete(k);\n }\n if (activeKeyRef.current === k) {\n setActiveKey(null);\n setTermLines([]);\n }\n setMessage(`Removing: ${s.target} / ${s.branch}...`);\n\n // Spawn work remove in a PTY so output shows in the right pane\n const removeKey = `remove:${s.target}:${s.branch}`;\n const removePty = new PtySession(\n process.cwd(),\n termInner,\n contentHeight - 2,\n { cmd: 'work', args: ['remove', s.target, s.branch, '--force'] },\n );\n registerPty(removeKey, removePty, `Removed: ${s.target} / ${s.branch}`, () => {\n refreshSessions();\n setMessage(`Removed: ${s.target} / ${s.branch}`);\n });\n connectPty(removeKey, removePty);\n return;\n }\n\n if (key === 'n') {\n setSavedSessionCursor(sessionCursorRef.current);\n setTopPaneMode(TopPaneMode.PROJECTS);\n setSessionCursor(0);\n setMessage('Select project, Esc to cancel');\n return;\n }\n\n if (key === '.') {\n const row = cursorToRow(topRowsRef.current, sessionCursorRef.current);\n if (row?.type !== 'session') return;\n const s = row.session;\n const existing = s.paths.find((p) => fs.existsSync(p));\n if (!existing) { setMessage('Session path no longer exists'); return; }\n const dir = s.isGroup ? path.dirname(existing) : existing;\n const editor = config?.editor ?? 'code';\n spawn(editor, [dir], { detached: true, stdio: 'ignore' }).unref();\n setMessage(`Opened in ${editor}: ${s.branch}`);\n return;\n }\n\n if (key === 'u') {\n const row = cursorToRow(topRowsRef.current, sessionCursorRef.current);\n if (row?.type !== 'session') return;\n const s = row.session;\n const existing = s.paths.find((p) => fs.existsSync(p));\n if (!existing) { setMessage('Session path no longer exists'); return; }\n setMessage(`Rebasing ${s.branch}...`);\n const err = rebaseOntoMain(s.branch, existing);\n if (err) {\n setMessage(`Rebase failed: ${err}`);\n } else {\n setMessage(`Rebased ${s.branch} onto main`);\n computeConflictsAndMerged(sessionsRef.current);\n }\n return;\n }\n\n if (key === 'g') {\n if (syncing) return;\n setSyncing(true);\n setMessage('Syncing sessions...');\n (async () => {\n if (config) {\n await Promise.all(\n Object.values(config.repos).map((repoPath) =>\n fetchRemoteAsync(repoPath).catch(() => {}),\n ),\n );\n }\n refreshSessions();\n refreshPrs();\n computeConflictsAndMerged(sessionsRef.current);\n setSyncing(false);\n setMessage('Sessions synced');\n })();\n return;\n }\n\n if (key === 'r') {\n refreshSessions();\n refreshPrs();\n refreshJira();\n refreshTasks();\n if (!ghAvailable) setMessage('Refreshed');\n return;\n }\n };\n\n process.stdin.on('data', handler);\n return () => { process.stdin.removeListener('data', handler); };\n }, [onExit, activateSession, refreshSessions, refreshPrs, refreshJira, refreshTasks, moveSessionCursor, movePrCursor, moveJiraCursor, moveTaskCursor, syncSessionCursorToActive, handleCreateWorktree, savedSessionCursor, config, computeConflictsAndMerged, syncing, ghAvailable, prMap]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // Resize handling\n useEffect(() => {\n const handler = () => {\n const newCols = process.stdout.columns || 80;\n const newRows = process.stdout.rows || 24;\n setDims({ cols: newCols, rows: newRows });\n if (activeKeyRef.current) {\n const pty = ptySessions.current.get(activeKeyRef.current);\n if (pty && !pty.exited) {\n const newTermInner = newCols - Math.min(60, Math.floor(newCols * 0.525)) - 2;\n const newContentHeight = newRows - 3;\n pty.resize(newTermInner, newContentHeight);\n }\n }\n };\n process.stdout.on('resize', handler);\n return () => { process.stdout.removeListener('resize', handler); };\n }, []);\n\n // Periodically clear scrollback for non-active PTYs to free memory\n useEffect(() => {\n const interval = setInterval(() => {\n for (const [key, pty] of ptySessions.current) {\n if (pty.exited) continue;\n if (key !== activeKeyRef.current) {\n pty.resetTerminal();\n }\n }\n }, 5 * 60 * 1000);\n return () => clearInterval(interval);\n }, []);\n\n const statusMap = buildStatusMap();\n\n const placeholder = !activeKey\n ? 'Select a session and press Enter'\n : 'Press Enter to start session';\n\n return (\n <Box flexDirection=\"column\" width={cols} height={rows}>\n <Box flexDirection=\"row\" height={contentHeight}>\n <Box flexDirection=\"column\" width={sidebarWidth}>\n <Sidebar\n sidebarRows={topRows}\n cursor={sessionCursor}\n focused={focus === Focus.SESSIONS}\n statusMap={statusMap}\n conflictCounts={conflictCounts}\n mergedSet={mergedSet}\n prMap={prMap}\n activeKey={activeKey}\n width={sidebarWidth}\n height={sessionPaneHeight}\n branchInput={branchInput}\n />\n <PrPane\n prRows={prRows}\n cursor={prCursor}\n focused={focus === Focus.PRS}\n localBranches={localBranches}\n width={sidebarWidth}\n height={prPaneHeight}\n />\n {jiraPaneHeight > 0 && (\n <JiraPane\n jiraRows={jiraRows}\n cursor={jiraCursor}\n focused={focus === Focus.JIRA}\n width={sidebarWidth}\n height={jiraPaneHeight}\n />\n )}\n <TaskPane\n taskRows={taskRows}\n cursor={taskCursor}\n focused={focus === Focus.TASKS}\n width={sidebarWidth}\n height={taskPaneHeight}\n taskInput={taskInput}\n />\n </Box>\n <TerminalPane\n lines={termLines}\n width={termWidth}\n height={contentHeight}\n focused={focus === Focus.TERMINAL}\n placeholder={placeholder}\n title=\"Terminal\"\n />\n </Box>\n <StatusBar message={message} pane={focus === Focus.TERMINAL ? 'terminal' : focus === Focus.PRS ? 'prs' : focus === Focus.JIRA ? 'jira' : focus === Focus.TASKS ? 'tasks' : 'sessions'} syncing={syncing} />\n </Box>\n );\n}\n","import { execFile } from 'node:child_process';\n\nexport interface PullRequestInfo {\n number: number;\n title: string;\n branch: string;\n url: string;\n isDraft: boolean;\n checksStatus: 'SUCCESS' | 'FAILURE' | 'PENDING' | 'NONE';\n reviewDecision: 'APPROVED' | 'CHANGES_REQUESTED' | 'REVIEW_REQUIRED' | 'NONE';\n /** Current user's latest review state on this PR. */\n myReview: 'APPROVED' | 'CHANGES_REQUESTED' | 'COMMENTED' | 'NONE';\n /** Whether the current user is the PR author. */\n isMine: boolean;\n /** Repo alias this PR belongs to (for distinguishing group PRs). */\n repoAlias: string;\n}\n\n/**\n * Map from branch name to array of PRs (one per repo that has a PR for that branch).\n * Single-repo sessions will have at most 1 entry; groups can have multiple.\n */\nexport type BranchPrMap = Map<string, PullRequestInfo[]>;\n\nfunction execAsync(cmd: string, args: string[], cwd: string, timeout: number): Promise<string> {\n return new Promise((resolve, reject) => {\n // `windowsHide: true` prevents a console window from flashing on\n // Windows whenever the PRs pane refreshes. Without it, opening\n // the PRs pane in `work web` triggers a visible terminal popup\n // for every configured repo's `gh pr list` invocation.\n execFile(\n cmd,\n args,\n { cwd, encoding: 'utf-8', timeout, windowsHide: true },\n (err, stdout) => {\n if (err) reject(err);\n else resolve(stdout ?? '');\n },\n );\n });\n}\n\nfunction parsePrJson(stdout: string, repoAlias: string, currentUser: string): PullRequestInfo[] {\n const prs: any[] = JSON.parse(stdout);\n const results: PullRequestInfo[] = [];\n\n for (const pr of prs) {\n let checksStatus: PullRequestInfo['checksStatus'] = 'NONE';\n const checks: any[] = pr.statusCheckRollup ?? [];\n if (checks.length > 0) {\n const hasFailure = checks.some((c: any) =>\n c.conclusion === 'FAILURE' || c.conclusion === 'TIMED_OUT' || c.conclusion === 'CANCELLED',\n );\n const hasPending = checks.some((c: any) =>\n c.status === 'IN_PROGRESS' || c.status === 'QUEUED' || c.status === 'PENDING',\n );\n if (hasFailure) checksStatus = 'FAILURE';\n else if (hasPending) checksStatus = 'PENDING';\n else checksStatus = 'SUCCESS';\n }\n\n // Merge conflict overrides to failure\n if (pr.mergeable === 'CONFLICTING') checksStatus = 'FAILURE';\n\n let reviewDecision: PullRequestInfo['reviewDecision'] = 'NONE';\n if (pr.reviewDecision === 'APPROVED') reviewDecision = 'APPROVED';\n else if (pr.reviewDecision === 'CHANGES_REQUESTED') reviewDecision = 'CHANGES_REQUESTED';\n else if (pr.reviewDecision === 'REVIEW_REQUIRED') reviewDecision = 'REVIEW_REQUIRED';\n\n // Check current user's latest review state\n let myReview: PullRequestInfo['myReview'] = 'NONE';\n if (currentUser) {\n const reviews: any[] = pr.reviews ?? [];\n for (let i = reviews.length - 1; i >= 0; i--) {\n if (reviews[i].author?.login?.toLowerCase() === currentUser.toLowerCase()) {\n const state = reviews[i].state;\n if (state === 'APPROVED') myReview = 'APPROVED';\n else if (state === 'CHANGES_REQUESTED') myReview = 'CHANGES_REQUESTED';\n else if (state === 'COMMENTED') myReview = 'COMMENTED';\n break;\n }\n }\n }\n\n results.push({\n number: pr.number,\n title: pr.title,\n branch: pr.headRefName,\n url: pr.url,\n isDraft: pr.isDraft ?? false,\n checksStatus,\n reviewDecision,\n myReview,\n isMine: currentUser ? pr.author?.login?.toLowerCase() === currentUser.toLowerCase() : false,\n repoAlias,\n });\n }\n\n return results;\n}\n\n/**\n * Fetch open PRs for a repo using `gh` CLI (async, non-blocking).\n */\nasync function fetchPullRequests(repoPath: string, repoAlias: string, currentUser: string): Promise<PullRequestInfo[]> {\n try {\n const stdout = await execAsync(\n 'gh',\n [\n 'pr', 'list',\n '--state', 'open',\n '--json', 'number,title,headRefName,url,isDraft,statusCheckRollup,reviewDecision,reviews,mergeable,author',\n '--limit', '100',\n ],\n repoPath,\n 15000,\n );\n if (!stdout) return [];\n return parsePrJson(stdout, repoAlias, currentUser);\n } catch {\n return [];\n }\n}\n\n/**\n * Fetch PRs for all configured repos (async, non-blocking).\n * Runs all repo fetches in parallel.\n * Returns a map from branch name → array of PRs across repos.\n */\nasync function getCurrentUser(): Promise<string> {\n try {\n const stdout = await execAsync('gh', ['api', 'user', '--jq', '.login'], process.cwd(), 5000);\n return stdout.trim();\n } catch {\n return '';\n }\n}\n\nexport async function fetchAllPullRequests(repos: Record<string, string>): Promise<BranchPrMap> {\n const currentUser = await getCurrentUser();\n const entries = Object.entries(repos);\n const results = await Promise.all(\n entries.map(([alias, repoPath]) => fetchPullRequests(repoPath, alias, currentUser)),\n );\n\n const map: BranchPrMap = new Map();\n for (const prList of results) {\n for (const pr of prList) {\n const existing = map.get(pr.branch);\n if (existing) {\n existing.push(pr);\n } else {\n map.set(pr.branch, [pr]);\n }\n }\n }\n\n return map;\n}\n\n/** Check if `gh` CLI is available and authenticated (async). */\nexport async function isGhAvailable(): Promise<boolean> {\n try {\n await execAsync('gh', ['auth', 'status'], process.cwd(), 5000);\n return true;\n } catch {\n return false;\n }\n}\n","import { execFile } from 'node:child_process';\r\n\r\nexport interface JiraIssue {\r\n key: string;\r\n summary: string;\r\n status: string;\r\n issuetype: string;\r\n priority: string;\r\n url: string;\r\n}\r\n\r\nfunction execAsync(cmd: string, args: string[], timeout: number): Promise<string> {\r\n return new Promise((resolve, reject) => {\r\n // `windowsHide: true` prevents a console window from flashing on\r\n // Windows whenever the Jira pane refreshes (every 120 s when open).\r\n // Without it, opening the Jira pane in `work web` triggers a\r\n // visible terminal popup.\r\n execFile(\r\n cmd,\r\n args,\r\n { encoding: 'utf-8', timeout, windowsHide: true },\r\n (err, stdout) => {\r\n if (err) reject(err);\r\n else resolve(stdout ?? '');\r\n },\r\n );\r\n });\r\n}\r\n\r\n/** Check if `acli` is available and authenticated. Kept for the TUI's\r\n * pre-flight; the web pane uses fetchMyJiraIssues directly which short-\r\n * circuits on auth failure too. */\r\nexport async function isAcliAvailable(): Promise<boolean> {\r\n try {\r\n await execAsync('acli', ['jira', 'auth', 'status'], 5000);\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/** Run `acli jira auth status` once and parse it for both availability\r\n * AND the site URL. Replaces the previous two-call pattern where\r\n * `isAcliAvailable` and `getJiraSiteUrl` each shelled out independently. */\r\nasync function probeAcli(): Promise<{ available: boolean; siteUrl: string }> {\r\n try {\r\n const stdout = await execAsync('acli', ['jira', 'auth', 'status'], 5000);\r\n const match = stdout.match(/Site:\\s+(\\S+)/);\r\n return {\r\n available: true,\r\n siteUrl: match ? `https://${match[1]}` : '',\r\n };\r\n } catch {\r\n return { available: false, siteUrl: '' };\r\n }\r\n}\r\n\r\nfunction parseIssuesJson(stdout: string, siteUrl: string): JiraIssue[] {\r\n const parsed = JSON.parse(stdout);\r\n const issues: any[] = parsed.issues ?? parsed ?? [];\r\n\r\n return issues.map((issue: any) => {\r\n const fields = issue.fields ?? {};\r\n return {\r\n key: issue.key ?? '',\r\n summary: fields.summary ?? '',\r\n status: fields.status?.name ?? '',\r\n issuetype: fields.issuetype?.name ?? '',\r\n priority: fields.priority?.name ?? '',\r\n url: siteUrl ? `${siteUrl}/browse/${issue.key}` : '',\r\n };\r\n });\r\n}\r\n\r\nasync function searchMyIssues(siteUrl: string): Promise<JiraIssue[]> {\r\n try {\r\n const stdout = await execAsync(\r\n 'acli',\r\n [\r\n 'jira', 'workitem', 'search',\r\n '--jql', 'assignee = currentUser() AND resolution = Unresolved AND status NOT IN (Archived, Done) ORDER BY updated DESC',\r\n '--json',\r\n '--limit', '50',\r\n ],\r\n 15000,\r\n );\r\n if (!stdout) return [];\r\n return parseIssuesJson(stdout, siteUrl);\r\n } catch {\r\n return [];\r\n }\r\n}\r\n\r\n/**\r\n * Fetch Jira issues assigned to the current user.\r\n *\r\n * Backwards-compatible signature for the TUI — returns an empty array\r\n * when acli is unavailable or errors. Web callers that need to\r\n * distinguish unavailable from empty should use `fetchJiraPane()`.\r\n */\r\nexport async function fetchMyJiraIssues(): Promise<JiraIssue[]> {\r\n const probe = await probeAcli();\r\n if (!probe.available) return [];\r\n return searchMyIssues(probe.siteUrl);\r\n}\r\n\r\n/**\r\n * Combined availability check + issue fetch in one acli probe. Used by\r\n * the dashboard's Jira pane so a refresh only spawns `acli jira auth\r\n * status` once instead of twice. The pane needs `available` separately\r\n * from `issues` so it can render the \"acli not configured\" hint.\r\n */\r\nexport async function fetchJiraPane(): Promise<{\r\n available: boolean;\r\n issues: JiraIssue[];\r\n}> {\r\n const probe = await probeAcli();\r\n if (!probe.available) return { available: false, issues: [] };\r\n const issues = await searchMyIssues(probe.siteUrl);\r\n return { available: true, issues };\r\n}\r\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { getConfigDir } from './config.js';\nimport { atomicWriteFile, ensureFile, withFileLock } from './fs-safe.js';\n\nexport interface Task {\n id: number;\n text: string;\n done: boolean;\n createdAt: string;\n doneAt?: string;\n /** Optional link to a worktree session (target:branch). */\n link?: string;\n}\n\ninterface TaskStore {\n nextId: number;\n tasks: Task[];\n}\n\nconst EMPTY_STORE: TaskStore = { nextId: 1, tasks: [] };\n\nfunction getTasksPath(): string {\n return path.join(getConfigDir(), 'tasks.json');\n}\n\nfunction loadStore(): TaskStore {\n const p = getTasksPath();\n if (!fs.existsSync(p)) return { ...EMPTY_STORE };\n try {\n const parsed = JSON.parse(fs.readFileSync(p, 'utf-8'));\n if (!parsed || typeof parsed !== 'object' || !Array.isArray(parsed.tasks)) {\n return { ...EMPTY_STORE };\n }\n return parsed;\n } catch {\n return { ...EMPTY_STORE };\n }\n}\n\nfunction saveStore(store: TaskStore): void {\n atomicWriteFile(getTasksPath(), JSON.stringify(store, null, 2));\n}\n\n/**\n * Serialize read-modify-write against other work processes so concurrent\n * `work todo` / TUI edits can't clobber each other.\n */\nasync function withTasksLock<T>(fn: () => T): Promise<T> {\n const tasksPath = getTasksPath();\n ensureFile(tasksPath, JSON.stringify(EMPTY_STORE, null, 2));\n return withFileLock(tasksPath, fn);\n}\n\nexport function getTasks(): Task[] {\n return loadStore().tasks;\n}\n\nexport async function addTask(text: string, link?: string): Promise<Task> {\n return withTasksLock(() => {\n const store = loadStore();\n const task: Task = {\n id: store.nextId++,\n text,\n done: false,\n createdAt: new Date().toISOString(),\n link,\n };\n store.tasks.push(task);\n saveStore(store);\n return task;\n });\n}\n\nexport async function completeTask(id: number): Promise<Task | null> {\n return withTasksLock(() => {\n const store = loadStore();\n const task = store.tasks.find((t) => t.id === id);\n if (!task) return null;\n task.done = true;\n task.doneAt = new Date().toISOString();\n saveStore(store);\n return task;\n });\n}\n\nexport async function uncompleteTask(id: number): Promise<Task | null> {\n return withTasksLock(() => {\n const store = loadStore();\n const task = store.tasks.find((t) => t.id === id);\n if (!task) return null;\n task.done = false;\n task.doneAt = undefined;\n saveStore(store);\n return task;\n });\n}\n\nexport async function removeTask(id: number): Promise<Task | null> {\n return withTasksLock(() => {\n const store = loadStore();\n const idx = store.tasks.findIndex((t) => t.id === id);\n if (idx === -1) return null;\n const [removed] = store.tasks.splice(idx, 1);\n saveStore(store);\n return removed;\n });\n}\n\nexport async function editTask(id: number, text: string): Promise<Task | null> {\n return withTasksLock(() => {\n const store = loadStore();\n const task = store.tasks.find((t) => t.id === id);\n if (!task) return null;\n task.text = text;\n saveStore(store);\n return task;\n });\n}\n\nexport function getTasksPath_(): string {\n return getTasksPath();\n}\n","import pty, { type IPty } from 'node-pty';\nimport xtermHeadless from '@xterm/headless';\nimport { debug } from '../core/logger.js';\nimport { buildAiLaunchArgs, type AiToolSpec } from '../core/ai-launcher.js';\n\nconst { Terminal } = xtermHeadless;\n\nexport type SessionStatus = 'stopped' | 'running' | 'idle';\n\nexport interface PtyAiOptions {\n /** Resolved AI tool spec (from `getAiTool(config)`). */\n tool: AiToolSpec;\n unsafe?: boolean;\n resume?: boolean;\n promptFile?: string;\n /** Dev-server port exposed to the launched process as $PORT. */\n port?: number;\n}\n\nexport class PtySession {\n readonly pty: IPty;\n terminal: InstanceType<typeof Terminal>;\n readonly cwd: string;\n private outputHandler?: (data: string) => void;\n private _exited = false;\n private _idle = true;\n private _outputBuffer = '';\n private _loggedOutput = false;\n onExit?: (code: number) => void;\n onStatusChange?: () => void;\n\n constructor(\n cwd: string,\n cols: number,\n rows: number,\n command?: { cmd: string; args: string[] },\n aiOptions?: PtyAiOptions,\n ) {\n this.cwd = cwd;\n this.terminal = new Terminal({\n cols,\n rows,\n scrollback: 200,\n allowProposedApi: true,\n });\n\n const isWindows = process.platform === 'win32';\n\n let spawnCmd: string;\n let spawnArgs: string[];\n\n if (command) {\n // Custom command (e.g. work tree)\n spawnCmd = isWindows ? 'cmd.exe' : command.cmd;\n spawnArgs = isWindows ? ['/c', command.cmd, ...command.args] : command.args;\n } else if (aiOptions) {\n // Launch configured AI tool (default: claude)\n const { cmd, args } = buildAiLaunchArgs(aiOptions.tool, {\n unsafe: aiOptions.unsafe,\n resume: aiOptions.resume,\n promptFile: aiOptions.promptFile,\n });\n spawnCmd = isWindows ? 'cmd.exe' : cmd;\n spawnArgs = isWindows ? ['/c', cmd, ...args] : args;\n } else {\n throw new Error('PtySession requires either a custom command or aiOptions');\n }\n\n const env: Record<string, string> = Object.fromEntries(\n Object.entries(process.env).filter((e): e is [string, string] => e[1] != null),\n );\n if (aiOptions?.port !== undefined) {\n env.PORT = String(aiOptions.port);\n }\n\n debug('PtySession spawn', { spawnCmd, spawnArgs, cwd, cols, rows });\n this.pty = pty.spawn(spawnCmd, spawnArgs, {\n name: 'xterm-256color',\n cwd,\n cols,\n rows,\n env,\n });\n debug('PtySession spawned pid=', this.pty.pid);\n\n this.pty.onData((data) => {\n this.terminal.write(data);\n this.outputHandler?.(data);\n // Log first 500 chars of PTY output for debugging early exits\n if (!this._loggedOutput) {\n this._outputBuffer = (this._outputBuffer || '') + data;\n if (this._outputBuffer.length > 500) {\n debug('PtySession first output', { cwd, output: this._outputBuffer.slice(0, 500) });\n this._loggedOutput = true;\n this._outputBuffer = '';\n }\n }\n });\n\n this.pty.onExit(({ exitCode }) => {\n if (!this._loggedOutput && this._outputBuffer) {\n debug('PtySession output before exit', { cwd, output: this._outputBuffer.slice(0, 500) });\n }\n this._outputBuffer = '';\n debug('PtySession exited', { cwd, exitCode });\n this._exited = true;\n this.onExit?.(exitCode);\n });\n }\n\n get exited() {\n return this._exited;\n }\n\n get idle() {\n return this._idle;\n }\n\n /** Called by the dashboard when a hook event indicates idle state change. */\n setIdle(idle: boolean) {\n if (this._exited || this._idle === idle) return;\n this._idle = idle;\n this.onStatusChange?.();\n }\n\n write(data: string) {\n if (!this._exited) {\n try { this.pty.write(data); } catch { /* PTY already exited */ }\n }\n }\n\n resize(cols: number, rows: number) {\n if (!this._exited) {\n try {\n this.pty.resize(cols, rows);\n } catch {\n // PTY already exited natively before our flag was set — ignore\n }\n this.terminal.resize(cols, rows);\n }\n }\n\n /** Clear scrollback buffer to free memory. */\n clearScrollback() {\n if (!this._exited) {\n this.terminal.clear();\n }\n }\n\n /**\n * Dispose the xterm Terminal and create a fresh one to fully release\n * internal parser/buffer memory. The visible viewport content is\n * captured first and replayed into the new terminal so nothing looks\n * different when the user switches back.\n */\n resetTerminal() {\n if (this._exited) return;\n\n const { cols, rows } = this.terminal;\n const buf = this.terminal.buffer.active;\n\n // Capture visible viewport lines as plain text\n const viewportLines: string[] = [];\n for (let y = 0; y < rows; y++) {\n const line = buf.getLine(buf.baseY + y);\n viewportLines.push(line ? line.translateToString(true) : '');\n }\n\n this.terminal.dispose();\n\n this.terminal = new Terminal({\n cols,\n rows,\n scrollback: 200,\n allowProposedApi: true,\n });\n\n // Replay viewport content (skip trailing empty lines)\n let lastNonEmpty = viewportLines.length - 1;\n while (lastNonEmpty >= 0 && viewportLines[lastNonEmpty].trim() === '') lastNonEmpty--;\n for (let i = 0; i <= lastNonEmpty; i++) {\n this.terminal.write(viewportLines[i] + (i < lastNonEmpty ? '\\r\\n' : ''));\n }\n }\n\n setOutputHandler(handler?: (data: string) => void) {\n this.outputHandler = handler;\n }\n\n dispose() {\n this.setOutputHandler(undefined);\n if (!this._exited) {\n try { this.pty.kill(); } catch { /* PTY already exited */ }\n }\n this.terminal.dispose();\n }\n}\n","/**\n * Local HTTP receiver for Claude Code hook events (Stop, Notification,\n * UserPromptSubmit). Multiple `work` subcommands can each run their own\n * HookServer concurrently — each tags its entries in ~/.claude/settings.json\n * via the shared `settings-editor` so the file write is atomic and stale\n * entries from a crashed previous run get pruned automatically.\n *\n * Originally lived under src/tui/hooks.ts and was hard-wired to `work dash`.\n * Now it's a shared primitive: `work dash` keeps using it for idle tracking\n * and `work web` mounts its own instance.\n */\n\nimport http from 'node:http';\nimport path from 'node:path';\nimport {\n editSettings,\n editSettingsSync,\n isLegacyEntry,\n isOwnerEntry,\n isStaleEntry,\n tag,\n type HookEntry,\n} from './settings-editor.js';\n\nexport type HookEvent = 'stop' | 'notification' | 'prompt_submit';\n\nexport interface HookPayload {\n session_id: string;\n cwd: string;\n hook_event_name: string;\n notification_type?: string;\n}\n\nexport type HookCallback = (cwd: string, event: HookEvent) => void;\n\nexport interface HookServerOptions {\n /** Identifies the subscriber so multiple `work` processes can coexist. */\n owner: string;\n callback: HookCallback;\n}\n\nexport class HookServer {\n private readonly server: http.Server;\n private readonly owner: string;\n private readonly callback: HookCallback;\n private port = 0;\n\n constructor(opts: HookServerOptions) {\n this.owner = opts.owner;\n this.callback = opts.callback;\n\n this.server = http.createServer((req, res) => {\n if (req.method !== 'POST') {\n res.writeHead(404);\n res.end();\n return;\n }\n let body = '';\n req.on('data', (chunk: string) => { body += chunk; });\n req.on('end', () => {\n res.writeHead(200);\n res.end();\n try {\n const payload: HookPayload = JSON.parse(body);\n const cwd = normalizePath(payload.cwd);\n if (payload.hook_event_name === 'Stop') {\n this.callback(cwd, 'stop');\n } else if (payload.hook_event_name === 'Notification') {\n this.callback(cwd, 'notification');\n } else if (payload.hook_event_name === 'UserPromptSubmit') {\n this.callback(cwd, 'prompt_submit');\n }\n } catch { /* malformed — ignore */ }\n });\n });\n }\n\n async start(): Promise<number> {\n const port = await new Promise<number>((resolve, reject) => {\n this.server.listen(0, '127.0.0.1', () => {\n const addr = this.server.address();\n if (!addr || typeof addr === 'string') {\n reject(new Error('Failed to get server address'));\n return;\n }\n resolve(addr.port);\n });\n this.server.on('error', reject);\n });\n this.port = port;\n await injectHttpHooks(this.owner, port);\n return port;\n }\n\n async stop(): Promise<void> {\n await removeHttpHooks(this.owner);\n return new Promise((resolve) => this.server.close(() => resolve()));\n }\n\n /** Synchronous best-effort cleanup for use in signal handlers where we\n * can't await. Falls back to a sync write of the settings file. */\n cleanupSync(): void {\n removeHttpHooksSync(this.owner);\n try { this.server.close(); } catch { /* */ }\n }\n}\n\nconst EVENTS = ['Stop', 'Notification', 'UserPromptSubmit'] as const;\n\nfunction pathForEvent(event: string): string {\n if (event === 'Stop') return '/stop';\n if (event === 'Notification') return '/notification';\n return '/prompt_submit';\n}\n\nfunction buildEntry(owner: string, baseUrl: string, event: string): HookEntry {\n return tag(\n {\n hooks: [\n {\n type: 'http',\n url: `${baseUrl}${pathForEvent(event)}`,\n timeout: 5,\n },\n ],\n ...(event === 'Notification' ? { matcher: 'idle_prompt' } : {}),\n },\n owner,\n );\n}\n\nfunction mutateInject(owner: string, port: number) {\n const baseUrl = `http://127.0.0.1:${port}`;\n return (s: { hooks?: Record<string, HookEntry[] | undefined> }) => {\n if (!s.hooks) s.hooks = {};\n for (const event of EVENTS) {\n const list = (s.hooks[event] ?? []) as HookEntry[];\n const cleaned = list.filter(\n (h) => !isLegacyEntry(h) && !isStaleEntry(h) && !isOwnerEntry(h, owner),\n );\n cleaned.push(buildEntry(owner, baseUrl, event));\n s.hooks[event] = cleaned;\n }\n };\n}\n\nfunction mutateRemove(owner: string) {\n return (s: { hooks?: Record<string, HookEntry[] | undefined> }) => {\n if (!s.hooks) return;\n for (const key of Object.keys(s.hooks)) {\n const list = s.hooks[key];\n if (!Array.isArray(list)) continue;\n s.hooks[key] = list.filter(\n (h) => !isLegacyEntry(h) && !isOwnerEntry(h, owner) && !isStaleEntry(h),\n );\n if (s.hooks[key]!.length === 0) delete s.hooks[key];\n }\n };\n}\n\nfunction injectHttpHooks(owner: string, port: number): Promise<void> {\n return editSettings(mutateInject(owner, port));\n}\n\nfunction removeHttpHooks(owner: string): Promise<void> {\n return editSettings(mutateRemove(owner));\n}\n\nfunction removeHttpHooksSync(owner: string): void {\n editSettingsSync(mutateRemove(owner));\n}\n\nfunction normalizePath(p: string): string {\n return path.resolve(p).toLowerCase();\n}\n","/**\n * Atomic read/edit/write helpers for `~/.claude/settings.json` — the user's\n * global Claude Code settings.\n *\n * Both `HookServer` (http-type hooks) and `installCommandHook` (command-type\n * hooks) mutate this file at startup/shutdown. They previously each had\n * their own copy of `readSettings` / `writeSettings`, and the write was a\n * plain `fs.writeFileSync`. If two `work` processes started concurrently —\n * or one was killed mid-write — the user's global hooks would be silently\n * truncated. That breaks hooks for every project, not just `work`.\n *\n * Everything here writes through `editSettings`, which does a single\n * tmp-file + rename atomic write under a process-level mutex (best-effort —\n * no cross-process locking, but the rename is OS-atomic and the in-process\n * mutex is enough to serialize the dash + web case).\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\n\nconst SETTINGS_PATH = path.join(os.homedir(), '.claude', 'settings.json');\nconst OWNER_TAG = '_workHookOwner';\nconst PID_TAG = '_workHookPid';\nconst LEGACY_TAGS = ['_workDash', '_work2Dash'];\n\nexport interface SettingsFile {\n hooks?: Record<string, HookEntry[] | undefined>;\n [k: string]: unknown;\n}\nexport interface HookEntry {\n hooks?: { type: string; url?: string; command?: string; timeout?: number }[];\n matcher?: string;\n [k: string]: unknown;\n}\n\nexport const HOOK_TAGS = {\n OWNER_TAG,\n PID_TAG,\n LEGACY_TAGS,\n} as const;\n\nfunction readSettings(): SettingsFile {\n try {\n if (!fs.existsSync(SETTINGS_PATH)) return {};\n return JSON.parse(fs.readFileSync(SETTINGS_PATH, 'utf8')) as SettingsFile;\n } catch {\n return {};\n }\n}\n\n/** Atomic write: tmp-file + rename. Caller must hold the in-process queue. */\nfunction writeAtomic(s: SettingsFile): void {\n try {\n fs.mkdirSync(path.dirname(SETTINGS_PATH), { recursive: true });\n const tmp = `${SETTINGS_PATH}.tmp-${process.pid}-${Date.now()}`;\n fs.writeFileSync(tmp, JSON.stringify(s, null, 2), 'utf-8');\n fs.renameSync(tmp, SETTINGS_PATH);\n } catch { /* best-effort */ }\n}\n\n/**\n * Serialise in-process edits to the settings file. Two HookServer / hook\n * installer calls from the same `work` process can't race each other.\n * (Cross-process races still exist but are bounded by the OS-atomic rename\n * — the worst case is one process's edit clobbering another's, never a\n * truncated half-written file.)\n */\nlet editQueue: Promise<void> = Promise.resolve();\nexport function editSettings(\n mutate: (s: SettingsFile) => void,\n): Promise<void> {\n editQueue = editQueue.then(() => {\n const s = readSettings();\n if (!s.hooks) s.hooks = {};\n mutate(s);\n if (s.hooks && Object.keys(s.hooks).length === 0) delete s.hooks;\n writeAtomic(s);\n });\n return editQueue;\n}\n\n/** Synchronous mutation variant — used in shutdown handlers where there's\n * no time to await a promise. Still atomic on the rename, just doesn't\n * participate in the in-process queue. */\nexport function editSettingsSync(mutate: (s: SettingsFile) => void): void {\n const s = readSettings();\n if (!s.hooks) s.hooks = {};\n mutate(s);\n if (s.hooks && Object.keys(s.hooks).length === 0) delete s.hooks;\n writeAtomic(s);\n}\n\n/** Common predicate: is this entry tagged with our owner? */\nexport function isOwnerEntry(h: HookEntry, owner: string): boolean {\n return h[OWNER_TAG] === owner;\n}\n\n/** Common predicate: was this entry tagged by a `work` process that's no\n * longer running? Stale entries get pruned on every install. */\nexport function isStaleEntry(h: HookEntry): boolean {\n if (typeof h[OWNER_TAG] !== 'string') return false;\n const pid = h[PID_TAG];\n if (typeof pid !== 'number') return false;\n try {\n process.kill(pid, 0);\n return false;\n } catch {\n return true;\n }\n}\n\nexport function isLegacyEntry(h: HookEntry): boolean {\n return LEGACY_TAGS.some((t) => (h as Record<string, unknown>)[t] === true);\n}\n\n/** Tag an entry so future installs can find/remove it. */\nexport function tag(entry: HookEntry, owner: string): HookEntry {\n return {\n ...entry,\n [OWNER_TAG]: owner,\n [PID_TAG]: process.pid,\n };\n}\n","/**\n * Best-effort desktop notifications for session lifecycle events. Used by the\n * dashboard hook callback to alert the user when a background Claude session\n * goes idle (finished its turn) or needs input — so parallel sessions don't\n * have to be babysat in the TUI.\n *\n * Opt-in via `WorkConfig.notifications`. Cross-platform with a graceful no-op\n * when the platform is unsupported or the notifier binary is absent. Spawns\n * fire-and-forget via cross-spawn argv arrays (never a shell string) and never\n * throws back to the caller.\n */\n\nimport spawn from 'cross-spawn';\n\nexport type NotifyKind = 'idle' | 'needs_input';\n\nconst MESSAGES: Record<NotifyKind, string> = {\n idle: 'Idle — finished its turn',\n needs_input: 'Needs your input',\n};\n\n/** Drop control characters that could break notifier argument parsing. */\nfunction sanitize(s: string): string {\n // eslint-disable-next-line no-control-regex\n return s.replace(/[\\u0000-\\u001f\\u007f]/g, \" \").trim();\n}\n\n/** Escape for embedding inside an AppleScript double-quoted string literal. */\nfunction escapeAppleScript(s: string): string {\n return sanitize(s).replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n}\n\n/** Escape for a PowerShell single-quoted string literal (double the quote). */\nfunction escapePwshSingle(s: string): string {\n return sanitize(s).replace(/'/g, \"''\");\n}\n\n/**\n * Build the notifier command for a platform. Pure and side-effect-free so it\n * can be unit-tested without spawning. Returns null for unsupported platforms.\n */\nexport function buildNotifyCommand(\n sessionName: string,\n kind: NotifyKind,\n platform: NodeJS.Platform,\n): { cmd: string; args: string[] } | null {\n const title = `work: ${sessionName}`;\n const message = MESSAGES[kind];\n\n if (platform === 'darwin') {\n const t = escapeAppleScript(title);\n const m = escapeAppleScript(message);\n return {\n cmd: 'osascript',\n args: ['-e', `display notification \"${m}\" with title \"${t}\"`],\n };\n }\n\n if (platform === 'linux') {\n // notify-send takes title/message as separate argv args — injection-safe.\n return { cmd: 'notify-send', args: [sanitize(title), sanitize(message)] };\n }\n\n if (platform === 'win32') {\n const t = escapePwshSingle(title);\n const m = escapePwshSingle(message);\n // The Windows path is BEST-EFFORT. There is no reliable, dependency-free\n // way to raise a real toast from a short-lived headless PowerShell process:\n // - WinRT toasts require a registered AppUserModelID; without one they\n // are silently dropped on many systems.\n // - A System.Windows.Forms NotifyIcon balloon needs a live message pump,\n // and disposing the process immediately can cancel the balloon before\n // it renders — but keeping the process alive leaks a lingering pwsh.\n // We therefore prefer BurntToast (proper WinRT toasts) when the module is\n // installed, and otherwise fall back to a balloon shown without a\n // load-bearing Start-Sleep: we pump messages briefly (DoEvents) so the\n // balloon has a chance to appear, then dispose. This may occasionally\n // no-op, which is acceptable for an opt-in convenience notification.\n const script =\n \"$ErrorActionPreference='SilentlyContinue';\" +\n // Preferred path: BurntToast (real WinRT toast, no lingering process).\n 'if(Get-Module -ListAvailable -Name BurntToast){' +\n 'Import-Module BurntToast;' +\n `New-BurntToastNotification -Text '${t}','${m}';` +\n '}else{' +\n // Fallback: transient balloon. Pump messages so it can render without a\n // multi-second sleep, then dispose so the process exits promptly.\n 'Add-Type -AssemblyName System.Windows.Forms;' +\n 'Add-Type -AssemblyName System.Drawing;' +\n '$n=New-Object System.Windows.Forms.NotifyIcon;' +\n '$n.Icon=[System.Drawing.SystemIcons]::Information;' +\n '$n.Visible=$true;' +\n `$n.ShowBalloonTip(5000,'${t}','${m}',[System.Windows.Forms.ToolTipIcon]::Info);` +\n '[System.Windows.Forms.Application]::DoEvents();' +\n 'Start-Sleep -Milliseconds 500;' +\n '[System.Windows.Forms.Application]::DoEvents();' +\n '$n.Dispose();' +\n '}';\n return { cmd: 'powershell', args: ['-NoProfile', '-Command', script] };\n }\n\n return null;\n}\n\n/**\n * Decide whether a hook event should fire a notification, tracking which\n * sessions have already been alerted for the current idle period in `notified`\n * (mutated in place). Returns the kind to fire, or null to stay silent.\n *\n * Decoupled from `PtySession.idle` so it does not depend on that field's\n * initial value: a session's first `stop`/`notification` still alerts, while\n * repeated `stop` events within one idle period are de-duplicated. A\n * `prompt_submit` (the user replied / a new turn started) clears the session\n * so the next idle alerts again.\n */\nexport function notifyKindForEvent(\n event: 'stop' | 'notification' | 'prompt_submit',\n sessionKey: string,\n notified: Set<string>,\n): NotifyKind | null {\n if (event === 'prompt_submit') {\n notified.delete(sessionKey);\n return null;\n }\n if (notified.has(sessionKey)) return null;\n notified.add(sessionKey);\n return event === 'notification' ? 'needs_input' : 'idle';\n}\n\nexport interface NotifyOptions {\n /** Notifications only fire when this is explicitly true. */\n enabled?: boolean;\n /** Override the detected platform (testing). */\n platform?: NodeJS.Platform;\n /** Override the spawn function (testing). */\n spawnFn?: typeof spawn;\n}\n\n/**\n * Fire a desktop notification for a session event. No-op when disabled, on an\n * unsupported platform, or when the notifier binary is missing. Never throws.\n */\nexport function notifyDesktop(\n sessionName: string,\n kind: NotifyKind,\n opts: NotifyOptions = {},\n): void {\n if (opts.enabled !== true) return;\n\n const platform = opts.platform ?? process.platform;\n const command = buildNotifyCommand(sessionName, kind, platform);\n if (!command) return;\n\n const spawnFn = opts.spawnFn ?? spawn;\n try {\n const child = spawnFn(command.cmd, command.args, {\n detached: true,\n stdio: 'ignore',\n });\n // Swallow async spawn errors (e.g. ENOENT when the binary is absent);\n // without a handler these would surface as an uncaught exception.\n child.on?.('error', () => {});\n child.unref?.();\n } catch {\n /* notifier unavailable — silent no-op */\n }\n}\n","/**\n * Opt-in, user-configurable shell commands that run when a background session\n * changes status (idle / needs input). Generalizes the desktop-notification\n * path: instead of a fixed OS notification, the user can run any command from\n * their own config (e.g. play a sound, ping a webhook, focus a window).\n *\n * Each hook is spawned fire-and-forget with the session directory as cwd\n * (passed as the spawn `cwd` option — never interpolated into the command\n * string). Commands run with `shell: true` intentionally, since they come only\n * from the user's own `~/.work/config.json`. Mirrors notifier.ts's defensive\n * style: a bad command never throws back to the caller.\n */\n\nimport { spawn } from 'node:child_process';\nimport type { StatusHook } from './config.js';\n\n/**\n * Run every configured status hook whose `on` matches `kind`, fire-and-forget.\n * No-op when `hooks` is undefined/empty or nothing matches. Never throws.\n *\n * @param kind The status the session changed to.\n * @param cwd Session directory; used as the spawned command's cwd.\n * @param sessionName Friendly session name, exposed as $WORK_SESSION.\n * @param hooks The configured hooks (from WorkConfig.statusHooks).\n */\nexport function runStatusHooks(\n kind: 'idle' | 'needs_input',\n cwd: string,\n sessionName: string,\n hooks: StatusHook[] | undefined,\n): void {\n if (!hooks || hooks.length === 0) return;\n\n for (const hook of hooks) {\n try {\n // loadConfig only guarantees `hooks` is an array, not that each element\n // is a well-formed {on, command}. A hand-edited config such as\n // `statusHooks: [null]` or `[\"x\"]` must not throw past this guard, so\n // validate the element shape inside the try before reading its fields.\n if (!hook || typeof hook !== 'object') continue;\n const { on, command } = hook as Partial<StatusHook>;\n if (on !== kind) continue;\n if (typeof command !== 'string' || command.length === 0) continue;\n const child = spawn(command, {\n cwd,\n shell: true,\n detached: true,\n stdio: 'ignore',\n env: {\n ...process.env,\n WORK_SESSION: sessionName,\n WORK_STATUS: kind,\n },\n });\n // Swallow async spawn errors (e.g. ENOENT); without a handler these\n // would surface as an uncaught exception.\n child.on?.('error', () => {});\n child.unref?.();\n } catch {\n /* bad command — silent no-op, never throw back to the caller */\n }\n }\n}\n","/**\n * Renders an xterm-headless buffer into an array of ANSI-styled strings (one per row).\n * No cursor positioning — just SGR codes + text, suitable for Ink <Text> components.\n *\n * SGR utility functions are copied from src/tui/renderer.ts.\n */\n\ninterface CellAttrs {\n fgMode: number;\n fg: number;\n bgMode: number;\n bg: number;\n bold: number;\n dim: number;\n italic: number;\n underline: number;\n blink: number;\n inverse: number;\n invisible: number;\n strikethrough: number;\n overline: number;\n}\n\nfunction readAttrs(cell: any): CellAttrs {\n return {\n fgMode: cell.getFgColorMode(),\n fg: cell.getFgColor(),\n bgMode: cell.getBgColorMode(),\n bg: cell.getBgColor(),\n bold: cell.isBold(),\n dim: cell.isDim(),\n italic: cell.isItalic(),\n underline: cell.isUnderline(),\n blink: cell.isBlink(),\n inverse: cell.isInverse(),\n invisible: cell.isInvisible(),\n strikethrough: cell.isStrikethrough(),\n overline: cell.isOverline(),\n };\n}\n\nfunction attrsEqual(a: CellAttrs, b: CellAttrs): boolean {\n return (\n a.fgMode === b.fgMode &&\n a.fg === b.fg &&\n a.bgMode === b.bgMode &&\n a.bg === b.bg &&\n a.bold === b.bold &&\n a.dim === b.dim &&\n a.italic === b.italic &&\n a.underline === b.underline &&\n a.blink === b.blink &&\n a.inverse === b.inverse &&\n a.invisible === b.invisible &&\n a.strikethrough === b.strikethrough &&\n a.overline === b.overline\n );\n}\n\nfunction attrsToSgr(a: CellAttrs): string {\n if (\n a.fgMode === 0 &&\n a.bgMode === 0 &&\n !a.bold && !a.dim && !a.italic && !a.underline &&\n !a.blink && !a.inverse && !a.invisible && !a.strikethrough && !a.overline\n ) {\n return '\\x1B[0m';\n }\n\n const params: number[] = [0];\n if (a.bold) params.push(1);\n if (a.dim) params.push(2);\n if (a.italic) params.push(3);\n if (a.underline) params.push(4);\n if (a.blink) params.push(5);\n if (a.inverse) params.push(7);\n if (a.invisible) params.push(8);\n if (a.strikethrough) params.push(9);\n if (a.overline) params.push(53);\n\n pushColorSgr(params, a.fgMode, a.fg, false);\n pushColorSgr(params, a.bgMode, a.bg, true);\n\n return `\\x1B[${params.join(';')}m`;\n}\n\nconst CM_DEFAULT = 0;\nconst CM_P16 = 0x1000000;\nconst CM_P256 = 0x2000000;\nconst CM_RGB = 0x3000000;\n\nfunction pushColorSgr(params: number[], mode: number, color: number, isBg: boolean): void {\n if (mode === CM_DEFAULT) return;\n\n const base = isBg ? 40 : 30;\n\n if (mode === CM_P16 || mode === CM_P256) {\n if (color < 8) {\n params.push(base + color);\n } else if (color < 16) {\n params.push(base + 60 + (color - 8));\n } else {\n params.push(base + 8, 5, color);\n }\n } else if (mode === CM_RGB) {\n const r = (color >> 16) & 0xff;\n const g = (color >> 8) & 0xff;\n const b = color & 0xff;\n params.push(base + 8, 2, r, g, b);\n }\n}\n\n/**\n * Render the visible portion of an xterm buffer as an array of ANSI-styled lines.\n * @param scrollBack - number of lines scrolled back from the bottom (0 = live view)\n */\nexport function renderBufferLines(buffer: any, cols: number, rows: number, scrollBack: number = 0): string[] {\n const lines: string[] = [];\n const nullCell = buffer.getNullCell();\n\n for (let y = 0; y < rows; y++) {\n const lineIdx = buffer.baseY + y - scrollBack;\n const line = buffer.getLine(lineIdx);\n let prevAttrs: CellAttrs | null = null;\n\n if (!line) {\n lines.push(' '.repeat(cols));\n continue;\n }\n\n const parts: string[] = [];\n parts.push('\\x1B[0m');\n\n let col = 0;\n while (col < cols) {\n const cell = line.getCell(col, nullCell);\n if (!cell) {\n parts.push(' ');\n col++;\n continue;\n }\n\n const width = cell.getWidth();\n if (width === 0) {\n col++;\n continue;\n }\n\n const attrs = readAttrs(cell);\n if (!prevAttrs || !attrsEqual(prevAttrs, attrs)) {\n parts.push(attrsToSgr(attrs));\n prevAttrs = attrs;\n }\n\n const ch = cell.getChars();\n parts.push(ch || ' ');\n col += width;\n }\n\n parts.push('\\x1B[0m');\n lines.push(parts.join(''));\n }\n\n return lines;\n}\n","import React from 'react';\nimport { Box, Text } from 'ink';\nimport { timeAgo } from '../utils/format.js';\nimport type { WorktreeSession } from '../core/history.js';\nimport { effectiveLastAccessedAt } from '../core/claude-activity.js';\nimport type { SessionStatus } from '../tui/session.js';\nimport type { PullRequestInfo, BranchPrMap } from '../core/pr.js';\nimport type { JiraIssue } from '../core/jira.js';\nimport type { Task } from '../core/tasks.js';\n\nexport interface SidebarProps {\n sidebarRows: SidebarRow[];\n cursor: number;\n focused: boolean;\n statusMap: Map<string, SessionStatus>;\n conflictCounts: Map<string, number>;\n mergedSet: Set<string>;\n prMap: BranchPrMap;\n activeKey: string | null;\n width: number;\n height: number;\n branchInput?: { value: string } | null;\n}\n\nexport interface PrPaneProps {\n prRows: SidebarRow[];\n cursor: number;\n focused: boolean;\n localBranches: Set<string>;\n width: number;\n height: number;\n}\n\nexport interface JiraPaneProps {\n jiraRows: SidebarRow[];\n cursor: number;\n focused: boolean;\n width: number;\n height: number;\n}\n\nexport interface TaskPaneProps {\n taskRows: SidebarRow[];\n cursor: number;\n focused: boolean;\n width: number;\n height: number;\n taskInput?: string | null;\n}\n\nexport type SidebarRow =\n | { type: 'header'; label: string }\n | { type: 'session'; session: WorktreeSession }\n | { type: 'project'; name: string; isGroup: boolean }\n | { type: 'pr'; pr: PullRequestInfo }\n | { type: 'jira'; issue: JiraIssue }\n | { type: 'task'; task: Task };\n\nexport function buildSessionRows(sessions: WorktreeSession[], statusMap?: Map<string, SessionStatus>): SidebarRow[] {\n if (sessions.length === 0) return [];\n\n const rows: SidebarRow[] = [];\n\n // Split into active (running/idle PTY) and inactive sessions\n const active: WorktreeSession[] = [];\n const inactive: WorktreeSession[] = [];\n for (const s of sessions) {\n const key = `${s.target}:${s.branch}`;\n const status = statusMap?.get(key);\n if (status === 'running' || status === 'idle') {\n active.push(s);\n } else {\n inactive.push(s);\n }\n }\n\n // Active sessions first (flat list, no grouping needed)\n if (active.length > 0) {\n rows.push({ type: 'header', label: 'Active' });\n for (const session of active) {\n rows.push({ type: 'session', session });\n }\n }\n\n // Inactive sessions grouped by target\n if (inactive.length > 0) {\n const groups = new Map<string, WorktreeSession[]>();\n for (const s of inactive) {\n if (!groups.has(s.target)) groups.set(s.target, []);\n groups.get(s.target)!.push(s);\n }\n\n for (const [target, group] of groups) {\n const typeTag = group[0].isGroup ? 'group' : 'repo';\n rows.push({ type: 'header', label: `${target} (${typeTag})` });\n for (const session of group) {\n rows.push({ type: 'session', session });\n }\n }\n }\n\n return rows;\n}\n\nexport function buildProjectRows(projects: Array<{ name: string; isGroup: boolean }>): SidebarRow[] {\n if (projects.length === 0) return [];\n const rows: SidebarRow[] = [];\n rows.push({ type: 'header', label: 'Select project' });\n for (const p of projects) {\n rows.push({ type: 'project', name: p.name, isGroup: p.isGroup });\n }\n return rows;\n}\n\nexport function buildJiraRows(issues: JiraIssue[]): SidebarRow[] {\n if (issues.length === 0) return [{ type: 'header', label: 'No Jira issues' }];\n\n // Group by status\n const byStatus = new Map<string, JiraIssue[]>();\n for (const issue of issues) {\n const status = issue.status || 'Unknown';\n if (!byStatus.has(status)) byStatus.set(status, []);\n byStatus.get(status)!.push(issue);\n }\n\n // Sort statuses: To Do first, then In Progress, then alphabetical\n const statusOrder = (s: string) => {\n const lower = s.toLowerCase();\n if (lower === 'to do') return 0;\n if (lower.includes('progress')) return 1;\n if (lower.includes('review')) return 2;\n return 3;\n };\n const sortedStatuses = [...byStatus.keys()].sort((a, b) => statusOrder(a) - statusOrder(b) || a.localeCompare(b));\n\n const rows: SidebarRow[] = [];\n for (const status of sortedStatuses) {\n rows.push({ type: 'header', label: status });\n for (const issue of byStatus.get(status)!) {\n rows.push({ type: 'jira', issue });\n }\n }\n return rows;\n}\n\nexport function buildTaskRows(tasks: Task[]): SidebarRow[] {\n const open = tasks.filter((t) => !t.done);\n const done = tasks.filter((t) => t.done);\n if (open.length === 0 && done.length === 0) return [{ type: 'header', label: 'No tasks' }];\n\n const rows: SidebarRow[] = [];\n if (open.length > 0) {\n for (const task of open) {\n rows.push({ type: 'task', task });\n }\n }\n if (done.length > 0) {\n rows.push({ type: 'header', label: `Done (${done.length})` });\n for (const task of done) {\n rows.push({ type: 'task', task });\n }\n }\n return rows;\n}\n\nexport function buildPrRows(prMap: BranchPrMap): SidebarRow[] {\n // Collect all PRs, grouped by repoAlias\n const byRepo = new Map<string, PullRequestInfo[]>();\n for (const prs of prMap.values()) {\n for (const pr of prs) {\n if (!byRepo.has(pr.repoAlias)) byRepo.set(pr.repoAlias, []);\n byRepo.get(pr.repoAlias)!.push(pr);\n }\n }\n\n if (byRepo.size === 0) return [{ type: 'header', label: 'No open PRs' }];\n\n const rows: SidebarRow[] = [];\n // Deduplicate PRs by number+repo\n const seen = new Set<string>();\n for (const [repo, prs] of byRepo) {\n rows.push({ type: 'header', label: repo });\n for (const pr of prs) {\n const key = `${repo}:${pr.number}`;\n if (seen.has(key)) continue;\n seen.add(key);\n rows.push({ type: 'pr', pr });\n }\n }\n\n return rows;\n}\n\nexport function countSelectable(rows: SidebarRow[]): number {\n return rows.filter((r) => r.type !== 'header').length;\n}\n\n/** Convert a visual row index (0-based position in the pane) to a selectable cursor index. */\nexport function visualRowToCursor(rows: SidebarRow[], visualRow: number): number {\n let selectableIdx = -1;\n for (let i = 0; i <= visualRow && i < rows.length; i++) {\n if (rows[i].type !== 'header') selectableIdx++;\n }\n return Math.max(0, selectableIdx);\n}\n\nexport function cursorToRow(rows: SidebarRow[], cursor: number): SidebarRow | undefined {\n let idx = 0;\n for (const row of rows) {\n if (row.type === 'header') continue;\n if (idx === cursor) return row;\n idx++;\n }\n return undefined;\n}\n\nexport function sessionKey(s: WorktreeSession): string {\n return `${s.target}:${s.branch}`;\n}\n\nfunction truncate(str: string, max: number): string {\n if (max <= 1) return '';\n return str.length > max ? str.slice(0, max - 1) + '…' : str;\n}\n\n/** Wrap text in an OSC 8 terminal hyperlink. */\nfunction hyperlink(url: string, text: string): string {\n return `\\x1B]8;;${url}\\x07${text}\\x1B]8;;\\x07`;\n}\n\nfunction ownershipLen(pr: PullRequestInfo): number {\n if (pr.isMine) return 2;\n if (pr.myReview !== 'NONE') return 2;\n return 0;\n}\n\nfunction singlePrBadgeLength(pr: PullRequestInfo): number {\n return 1 + String(pr.number).length;\n}\n\nfunction prBadgesLength(prs: PullRequestInfo[]): number {\n if (prs.length === 0) return 0;\n return prs.reduce((sum, pr) => sum + singlePrBadgeLength(pr), 0) + (prs.length - 1);\n}\n\nfunction PrBadge({ pr }: { pr: PullRequestInfo }): React.ReactElement {\n return (\n <Text color=\"blue\">{hyperlink(pr.url, `#${pr.number}`)}</Text>\n );\n}\n\nfunction EmptyRow({ width }: { width: number }): React.ReactElement {\n return <Text>{' '.repeat(width)}</Text>;\n}\n\nfunction HeaderRow({ label, width }: { label: string; width: number }): React.ReactElement {\n const text = ` ${truncate(label, width - 3)} `;\n const lineLen = Math.max(0, width - text.length);\n return (\n <Box width={width}>\n <Text color=\"yellow\" bold>{text}</Text>\n <Text dimColor>{'─'.repeat(lineLen)}</Text>\n </Box>\n );\n}\n\nfunction SessionRow({\n session: s,\n selected,\n focused,\n status,\n conflicts,\n merged,\n prs,\n active,\n width,\n}: {\n session: WorktreeSession;\n selected: boolean;\n focused: boolean;\n status: SessionStatus;\n conflicts: number;\n merged: boolean;\n prs: PullRequestInfo[];\n active: boolean;\n width: number;\n}): React.ReactElement {\n const dotChar = status === 'idle' ? '◆' : status === 'running' ? '●' : '○';\n const dotColor = status === 'idle' ? 'yellow' : status === 'running' ? 'green' : 'gray';\n const agoStr = timeAgo(effectiveLastAccessedAt(s));\n\n const mergedLen = merged ? 8 : 0; // \" merged\"\n const conflictLen = conflicts > 0 ? 2 + String(conflicts).length : 0;\n const prLen = prs.length > 0 ? 1 + prBadgesLength(prs) : 0;\n const fixedOverhead = 5 + agoStr.length + mergedLen + conflictLen + prLen;\n const branchBudget = Math.max(4, width - fixedOverhead);\n\n const cursor = selected && focused ? '›' : active ? '▸' : ' ';\n const cursorColor = selected && focused ? 'cyan' : active ? 'green' : undefined;\n\n return (\n <Box width={width}>\n <Text> </Text>\n <Text color={cursorColor}>{cursor}</Text>\n <Text color={dotColor}>{dotChar}</Text>\n <Text> </Text>\n <Text color=\"green\">{truncate(s.branch, branchBudget)}</Text>\n {merged && <Text color=\"magenta\"> merged</Text>}\n {conflicts > 0 && <Text color=\"red\"> !{conflicts}</Text>}\n {prs.map((pr, i) => (\n <React.Fragment key={i}>\n <Text> </Text>\n <PrBadge pr={pr} />\n </React.Fragment>\n ))}\n <Box flexGrow={1} />\n <Text dimColor>{agoStr}</Text>\n </Box>\n );\n}\n\nfunction ProjectRow({\n name,\n isGroup,\n selected,\n focused,\n branchInput,\n width,\n}: {\n name: string;\n isGroup: boolean;\n selected: boolean;\n focused: boolean;\n branchInput?: { value: string } | null;\n width: number;\n}): React.ReactElement {\n const marker = selected && focused ? '›' : ' ';\n\n if (branchInput && selected) {\n return (\n <Box width={width}>\n <Text> </Text>\n <Text color=\"cyan\">{marker}</Text>\n <Text> </Text>\n <Text color=\"magenta\">{truncate(name, width - 15)}</Text>\n <Text> </Text>\n <Text color=\"cyan\">branch:</Text>\n <Text> {branchInput.value}</Text>\n <Text dimColor>█</Text>\n </Box>\n );\n }\n\n return (\n <Box width={width}>\n <Text> </Text>\n <Text color={selected && focused ? 'cyan' : undefined}>{marker}</Text>\n <Text> </Text>\n <Text color=\"magenta\">{truncate(name, width - 15)}</Text>\n <Text> </Text>\n <Text dimColor>{isGroup ? '(group)' : '(repo)'}</Text>\n </Box>\n );\n}\n\nfunction PrListRow({\n pr,\n selected,\n focused,\n local,\n width,\n}: {\n pr: PullRequestInfo;\n selected: boolean;\n focused: boolean;\n local: boolean;\n width: number;\n}): React.ReactElement {\n const marker = selected && focused ? '›' : ' ';\n const numStr = `#${pr.number}`;\n const checksChar = pr.checksStatus === 'SUCCESS' ? '✓' : pr.checksStatus === 'FAILURE' ? '✗' : pr.checksStatus === 'PENDING' ? '●' : '';\n const checksColor = pr.checksStatus === 'SUCCESS' ? 'green' : pr.checksStatus === 'FAILURE' ? 'red' : 'yellow';\n const checksLen = checksChar ? 2 : 0;\n const rvwLen = ownershipLen(pr);\n const localLen = local ? 6 : 0; // \" local\"\n const fixedOverhead = 4 + numStr.length + checksLen + rvwLen + localLen;\n const branchBudget = Math.max(4, width - fixedOverhead);\n const branchColor = pr.isDraft ? undefined : 'green';\n\n return (\n <Box width={width}>\n <Text> </Text>\n <Text color={selected && focused ? 'cyan' : undefined}>{marker}</Text>\n <Text> </Text>\n <Text color={branchColor} dimColor={pr.isDraft}>{truncate(pr.branch, branchBudget)}</Text>\n {local && <Text color=\"cyan\"> local</Text>}\n <Box flexGrow={1} />\n {pr.isMine && <Text color=\"magenta\">★ </Text>}\n {!pr.isMine && pr.myReview === 'APPROVED' && <Text color=\"green\">✔ </Text>}\n {!pr.isMine && (pr.myReview === 'CHANGES_REQUESTED' || pr.myReview === 'COMMENTED') && <Text color=\"red\">✎ </Text>}\n <Text color={pr.isDraft ? undefined : 'blue'} dimColor={pr.isDraft}>{hyperlink(pr.url, numStr)}</Text>\n {checksChar && <Text color={checksColor}> {checksChar}</Text>}\n </Box>\n );\n}\n\n/** Bordered pane that renders rows inside a box frame. */\nfunction BorderedPane({\n rows: sidebarRows,\n cursor,\n focused,\n borderColor,\n title,\n width,\n height,\n renderRow,\n}: {\n rows: SidebarRow[];\n cursor: number;\n focused: boolean;\n borderColor: string;\n title?: string;\n width: number;\n height: number;\n renderRow: (row: SidebarRow, idx: number, selected: boolean) => React.ReactNode;\n}): React.ReactElement {\n const innerWidth = width - 2;\n const contentHeight = height - 2;\n\n // Find the row index of the cursor-selected item so we can scroll to it\n let cursorRowIdx = 0;\n let selectableCount = 0;\n for (let i = 0; i < sidebarRows.length; i++) {\n if (sidebarRows[i].type !== 'header') {\n if (selectableCount === cursor) {\n cursorRowIdx = i;\n break;\n }\n selectableCount++;\n }\n }\n\n // Compute scroll offset to keep cursor visible (with 1-row margin)\n let scrollOffset = 0;\n if (sidebarRows.length > contentHeight) {\n if (cursorRowIdx >= contentHeight - 1) {\n scrollOffset = Math.min(\n cursorRowIdx - contentHeight + 2,\n sidebarRows.length - contentHeight,\n );\n }\n }\n\n const rendered: React.ReactNode[] = [];\n let selectableIdx = 0;\n // Count selectables before the visible window to get the right index\n for (let i = 0; i < scrollOffset; i++) {\n if (sidebarRows[i].type !== 'header') selectableIdx++;\n }\n for (let i = 0; i < contentHeight; i++) {\n const rowIdx = scrollOffset + i;\n if (rowIdx >= sidebarRows.length) {\n rendered.push(<EmptyRow key={i} width={innerWidth} />);\n continue;\n }\n const row = sidebarRows[rowIdx];\n if (row.type === 'header') {\n rendered.push(<HeaderRow key={i} label={row.label} width={innerWidth} />);\n } else {\n const sel = selectableIdx === cursor;\n selectableIdx++;\n rendered.push(renderRow(row, rowIdx, sel));\n }\n }\n\n const topBorder = title\n ? '┌─ ' + title + ' ' + '─'.repeat(Math.max(0, innerWidth - title.length - 3)) + '┐'\n : '┌' + '─'.repeat(innerWidth) + '┐';\n\n return (\n <Box flexDirection=\"column\" width={width}>\n <Text color={borderColor}>{topBorder}</Text>\n {rendered.map((row, i) => (\n <Box key={i}>\n <Text color={borderColor}>│</Text>\n {row}\n <Text color={borderColor}>│</Text>\n </Box>\n ))}\n <Text color={borderColor}>{'└' + '─'.repeat(innerWidth) + '┘'}</Text>\n </Box>\n );\n}\n\nexport function Sidebar({ sidebarRows, cursor, focused, statusMap, conflictCounts, mergedSet, prMap, activeKey, width, height, branchInput }: SidebarProps) {\n const borderColor = focused ? 'cyan' : 'gray';\n const innerWidth = width - 2;\n\n return (\n <BorderedPane\n rows={sidebarRows}\n cursor={cursor}\n focused={focused}\n borderColor={borderColor}\n title=\"Worktrees\"\n width={width}\n height={height}\n renderRow={(row, i, sel) => {\n if (row.type === 'session') {\n const s = row.session;\n const key = sessionKey(s);\n return (\n <SessionRow\n key={i}\n session={s}\n selected={sel}\n focused={focused}\n status={statusMap.get(key) ?? 'stopped'}\n conflicts={conflictCounts.get(key) ?? 0}\n merged={mergedSet.has(key)}\n prs={prMap.get(s.branch) ?? []}\n active={key === activeKey}\n width={innerWidth}\n />\n );\n }\n if (row.type === 'project') {\n return (\n <ProjectRow\n key={i}\n name={row.name}\n isGroup={row.isGroup}\n selected={sel}\n focused={focused}\n branchInput={branchInput}\n width={innerWidth}\n />\n );\n }\n return <EmptyRow key={i} width={innerWidth} />;\n }}\n />\n );\n}\n\nfunction statusColor(status: string): string | undefined {\n const lower = status.toLowerCase();\n if (lower.includes('progress') || lower.includes('review')) return 'cyan';\n if (lower.includes('done') || lower.includes('closed')) return 'green';\n return 'yellow';\n}\n\nfunction JiraListRow({\n issue,\n selected,\n focused,\n width,\n}: {\n issue: JiraIssue;\n selected: boolean;\n focused: boolean;\n width: number;\n}): React.ReactElement {\n const marker = selected && focused ? '›' : ' ';\n const keyLen = issue.key.length;\n const summaryBudget = Math.max(4, width - keyLen - 5);\n\n return (\n <Box width={width}>\n <Text> </Text>\n <Text color={selected && focused ? 'cyan' : undefined}>{marker}</Text>\n <Text> </Text>\n <Text color={statusColor(issue.status)}>{issue.url ? hyperlink(issue.url, issue.key) : issue.key}</Text>\n <Text> </Text>\n <Text>{truncate(issue.summary, summaryBudget)}</Text>\n </Box>\n );\n}\n\nexport function PrPane({ prRows, cursor, focused, localBranches, width, height }: PrPaneProps) {\n const borderColor = focused ? 'cyan' : 'gray';\n const innerWidth = width - 2;\n\n return (\n <BorderedPane\n rows={prRows}\n cursor={cursor}\n focused={focused}\n borderColor={borderColor}\n title=\"Pull Requests\"\n width={width}\n height={height}\n renderRow={(row, i, sel) => {\n if (row.type === 'pr') {\n return (\n <PrListRow\n key={i}\n pr={row.pr}\n selected={sel}\n focused={focused}\n local={localBranches.has(row.pr.branch)}\n width={innerWidth}\n />\n );\n }\n return <EmptyRow key={i} width={innerWidth} />;\n }}\n />\n );\n}\n\nfunction TaskListRow({\n task,\n selected,\n focused,\n width,\n}: {\n task: Task;\n selected: boolean;\n focused: boolean;\n width: number;\n}): React.ReactElement {\n const marker = selected && focused ? '›' : ' ';\n const check = task.done ? '✓' : '○';\n const checkColor = task.done ? 'green' : 'gray';\n const idStr = `#${task.id}`;\n const textBudget = Math.max(4, width - idStr.length - 7);\n\n return (\n <Box width={width}>\n <Text> </Text>\n <Text color={selected && focused ? 'cyan' : undefined}>{marker}</Text>\n <Text color={checkColor}>{check}</Text>\n <Text> </Text>\n <Text dimColor={task.done} strikethrough={task.done}>{truncate(task.text, textBudget)}</Text>\n <Box flexGrow={1} />\n <Text dimColor>{idStr}</Text>\n </Box>\n );\n}\n\nfunction TaskInputRow({ value, width }: { value: string; width: number }): React.ReactElement {\n const budget = Math.max(4, width - 8);\n return (\n <Box width={width}>\n <Text> </Text>\n <Text color=\"cyan\">+ </Text>\n <Text>{truncate(value, budget)}</Text>\n <Text dimColor>█</Text>\n </Box>\n );\n}\n\nexport function TaskPane({ taskRows, cursor, focused, width, height, taskInput }: TaskPaneProps) {\n const borderColor = focused ? 'cyan' : 'gray';\n const innerWidth = width - 2;\n const contentHeight = height - 2;\n\n // If input is active, render it manually at the top\n if (taskInput !== null && taskInput !== undefined) {\n const rendered: React.ReactNode[] = [];\n rendered.push(<TaskInputRow key=\"input\" value={taskInput} width={innerWidth} />);\n\n let selectableIdx = 0;\n for (let i = 0; i < contentHeight - 1 && i < taskRows.length; i++) {\n const row = taskRows[i];\n if (row.type === 'header') {\n rendered.push(<HeaderRow key={`r${i}`} label={row.label} width={innerWidth} />);\n } else if (row.type === 'task') {\n rendered.push(\n <TaskListRow key={`r${i}`} task={row.task} selected={false} focused={false} width={innerWidth} />,\n );\n selectableIdx++;\n }\n }\n while (rendered.length < contentHeight) {\n rendered.push(<EmptyRow key={`e${rendered.length}`} width={innerWidth} />);\n }\n\n const title = 'Tasks';\n const topBorder = '┌─ ' + title + ' ' + '─'.repeat(Math.max(0, innerWidth - title.length - 3)) + '┐';\n\n return (\n <Box flexDirection=\"column\" width={width}>\n <Text color={borderColor}>{topBorder}</Text>\n {rendered.map((row, i) => (\n <Box key={i}>\n <Text color={borderColor}>│</Text>\n {row}\n <Text color={borderColor}>│</Text>\n </Box>\n ))}\n <Text color={borderColor}>{'└' + '─'.repeat(innerWidth) + '┘'}</Text>\n </Box>\n );\n }\n\n return (\n <BorderedPane\n rows={taskRows}\n cursor={cursor}\n focused={focused}\n borderColor={borderColor}\n title=\"Tasks\"\n width={width}\n height={height}\n renderRow={(row, i, sel) => {\n if (row.type === 'task') {\n return (\n <TaskListRow\n key={i}\n task={row.task}\n selected={sel}\n focused={focused}\n width={innerWidth}\n />\n );\n }\n return <EmptyRow key={i} width={innerWidth} />;\n }}\n />\n );\n}\n\nexport function JiraPane({ jiraRows, cursor, focused, width, height }: JiraPaneProps) {\n const borderColor = focused ? 'cyan' : 'gray';\n const innerWidth = width - 2;\n\n return (\n <BorderedPane\n rows={jiraRows}\n cursor={cursor}\n focused={focused}\n borderColor={borderColor}\n title=\"Jira\"\n width={width}\n height={height}\n renderRow={(row, i, sel) => {\n if (row.type === 'jira') {\n return (\n <JiraListRow\n key={i}\n issue={row.issue}\n selected={sel}\n focused={focused}\n width={innerWidth}\n />\n );\n }\n return <EmptyRow key={i} width={innerWidth} />;\n }}\n />\n );\n}\n","import React from 'react';\nimport { Box, Text } from 'ink';\n\nexport interface TerminalPaneProps {\n lines: string[];\n width: number;\n height: number;\n focused: boolean;\n placeholder?: string;\n title?: string;\n}\n\nexport function TerminalPane({ lines, width, height, focused, placeholder, title }: TerminalPaneProps) {\n const borderColor = focused ? 'cyan' : 'gray';\n const innerWidth = width - 2;\n const contentHeight = height - 2;\n\n const content: React.ReactNode[] = [];\n\n if (lines.length === 0 && placeholder) {\n // Center placeholder message\n for (let i = 0; i < contentHeight; i++) {\n const mid = Math.floor(contentHeight / 2);\n if (i === mid) {\n const pad = Math.max(0, Math.floor((innerWidth - placeholder.length) / 2));\n content.push(\n <Text key={i} dimColor>\n {' '.repeat(pad) + placeholder}\n </Text>,\n );\n } else {\n content.push(<Text key={i}>{' '}</Text>);\n }\n }\n } else {\n for (let i = 0; i < contentHeight; i++) {\n if (i < lines.length) {\n content.push(<Text key={i}>{lines[i]}</Text>);\n } else {\n content.push(<Text key={i}>{' '}</Text>);\n }\n }\n }\n\n return (\n <Box flexDirection=\"column\" width={width}>\n <Text color={borderColor}>{title ? '┌─ ' + title + ' ' + '─'.repeat(Math.max(0, innerWidth - title.length - 3)) + '┐' : '┌' + '─'.repeat(innerWidth) + '┐'}</Text>\n {content.map((row, i) => (\n <Box key={i}>\n <Text color={borderColor}>{'│'}</Text>\n <Box width={innerWidth}>{row}</Box>\n <Text color={borderColor}>{'│'}</Text>\n </Box>\n ))}\n <Text color={borderColor}>{'└' + '─'.repeat(innerWidth) + '┘'}</Text>\n </Box>\n );\n}\n","import React from 'react';\nimport { Box, Text } from 'ink';\n\nexport interface StatusBarProps {\n message: string;\n pane: 'sessions' | 'tasks' | 'prs' | 'jira' | 'terminal';\n syncing?: boolean;\n}\n\nconst PANE_HINTS: Record<StatusBarProps['pane'], string> = {\n sessions: ' tab pane j/k nav enter start n new d remove . editor u rebase g sync G sync all q quit ',\n tasks: ' tab pane j/k nav enter toggle a add e edit w worktree d remove g sync G sync all q quit ',\n prs: ' tab pane j/k nav enter checkout g sync G sync all q quit ',\n jira: ' tab pane j/k nav enter worktree o open g sync G sync all q quit ',\n terminal: ' tab pane ctrl+] detach ',\n};\n\nexport function StatusBar({ message, pane, syncing }: StatusBarProps) {\n if (message) {\n return (\n <Box>\n <Text color=\"yellow\"> {message}</Text>\n {syncing && <Box flexGrow={1} />}\n {syncing && <Text color=\"cyan\"> syncing</Text>}\n </Box>\n );\n }\n\n return (\n <Box>\n <Text dimColor>{PANE_HINTS[pane]}</Text>\n {pane === 'sessions' && (\n <>\n <Text dimColor>{'['}</Text>\n <Text color=\"green\">●</Text>\n <Text dimColor>{' working '}</Text>\n <Text color=\"yellow\">◆</Text>\n <Text dimColor>{' needs input '}</Text>\n <Text dimColor>{'○ stopped]'}</Text>\n </>\n )}\n {syncing && <Box flexGrow={1} />}\n {syncing && <Text color=\"cyan\"> syncing</Text>}\n </Box>\n );\n}\n","import type { CommandModule } from 'yargs';\nimport { ensureConfig } from '../core/config.js';\nimport { startDashboard } from '../tui-ink/index.js';\n\nexport const dashCommand: CommandModule = {\n command: 'dash',\n describe: 'Interactive dashboard for all worktree sessions',\n builder: (yargs) =>\n yargs.option('unsafe', {\n describe: 'Launch the AI tool with its skip-permissions flag',\n type: 'boolean',\n default: false,\n }),\n handler: async (argv) => {\n ensureConfig();\n await startDashboard(argv.unsafe as boolean);\n },\n};\n","import chalk from 'chalk';\r\nimport type { CommandModule } from 'yargs';\r\nimport {\r\n setupCompletions,\r\n printCompletionResults,\r\n printManualInstructions,\r\n} from '../core/setup-completions.js';\r\n\r\nconst POWERSHELL_SCRIPT = `\r\nRegister-ArgumentCompleter -CommandName work -Native -ScriptBlock {\r\n param($wordToComplete, $commandAst, $cursorPosition)\r\n\r\n # When $wordToComplete is non-empty, the last CommandElement IS that word.\r\n # Exclude it so we don't double-pass it.\r\n $endIdx = $commandAst.CommandElements.Count\r\n if ($wordToComplete -ne '') { $endIdx -= 1 }\r\n\r\n $completedArgs = @()\r\n for ($i = 1; $i -lt $endIdx; $i++) {\r\n $el = $commandAst.CommandElements[$i]\r\n if ($el -is [System.Management.Automation.Language.CommandParameterAst]) { continue }\r\n $completedArgs += $el.ToString()\r\n }\r\n\r\n $results = & work --get-yargs-completions work @completedArgs $wordToComplete 2>$null\r\n $results | ForEach-Object {\r\n if ($_.Trim()) {\r\n [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)\r\n }\r\n }\r\n}\r\n`.trim();\r\n\r\nconst BASH_SCRIPT = `\r\n###-begin-work-completions-###\r\n_work_yargs_completions()\r\n{\r\n local cur_word args type_list\r\n cur_word=\"\\${COMP_WORDS[COMP_CWORD]}\"\r\n args=(\"\\${COMP_WORDS[@]}\")\r\n type_list=$(work --get-yargs-completions \"\\${args[@]}\")\r\n COMPREPLY=( $(compgen -W \"\\${type_list}\" -- \\${cur_word}) )\r\n if [ \\${#COMPREPLY[@]} -eq 0 ]; then\r\n COMPREPLY=()\r\n fi\r\n return 0\r\n}\r\ncomplete -o bashdefault -o default -F _work_yargs_completions work\r\n###-end-work-completions-###\r\n`.trim();\r\n\r\nconst ZSH_SCRIPT = `\r\n###-begin-work-completions-###\r\nautoload -Uz bashcompinit 2>/dev/null && bashcompinit 2>/dev/null\r\n_work_yargs_completions()\r\n{\r\n local cur_word args type_list\r\n cur_word=\"\\${COMP_WORDS[COMP_CWORD]}\"\r\n args=(\"\\${COMP_WORDS[@]}\")\r\n type_list=$(work --get-yargs-completions \"\\${args[@]}\")\r\n COMPREPLY=( $(compgen -W \"\\${type_list}\" -- \\${cur_word}) )\r\n if [ \\${#COMPREPLY[@]} -eq 0 ]; then\r\n COMPREPLY=()\r\n fi\r\n return 0\r\n}\r\ncomplete -o default -F _work_yargs_completions work\r\n###-end-work-completions-###\r\n`.trim();\r\n\r\nconst FISH_SCRIPT = `\r\n# work tab completions\r\nfunction __work_complete\r\n set -l cmd (commandline -opc)\r\n set -l cur (commandline -ct)\r\n work --get-yargs-completions $cmd $cur 2>/dev/null\r\nend\r\n\r\ncomplete -c work -f -a '(__work_complete)'\r\n`.trim();\r\n\r\nexport const completionCommand: CommandModule = {\r\n command: 'completion',\r\n describe: 'Generate shell completion script',\r\n builder: (yargs) =>\r\n yargs\r\n .option('shell', {\r\n describe: 'Shell type',\r\n choices: ['bash', 'zsh', 'fish', 'powershell', 'ps'] as const,\r\n type: 'string',\r\n })\r\n .option('install', {\r\n describe: 'Install completions into shell profile(s)',\r\n type: 'boolean',\r\n default: false,\r\n }),\r\n handler: (argv) => {\r\n if (argv.install) {\r\n const results = setupCompletions();\r\n if (results.length > 0) {\r\n printCompletionResults(results);\r\n console.log('');\r\n console.log(\r\n chalk.gray(' Restart your shell for completions to take effect.'),\r\n );\r\n } else {\r\n printManualInstructions();\r\n }\r\n return;\r\n }\r\n\r\n const shell = argv.shell as string | undefined;\r\n\r\n if (shell === 'powershell' || shell === 'ps') {\r\n console.log(POWERSHELL_SCRIPT);\r\n } else if (shell === 'zsh') {\r\n console.log(ZSH_SCRIPT);\r\n } else if (shell === 'fish') {\r\n console.log(FISH_SCRIPT);\r\n } else {\r\n console.log(BASH_SCRIPT);\r\n }\r\n },\r\n};\r\n","import chalk from 'chalk';\r\nimport type { CommandModule } from 'yargs';\r\nimport { getTasks, addTask, completeTask, uncompleteTask, removeTask, editTask } from '../core/tasks.js';\r\n\r\nexport const todoCommand: CommandModule = {\r\n command: 'todo [action]',\r\n describe: 'Manage tasks',\r\n builder: (yargs) =>\r\n yargs\r\n .positional('action', {\r\n describe: 'Action: add, done, undo, rm, edit (omit to list)',\r\n type: 'string',\r\n })\r\n .option('all', {\r\n describe: 'Show completed tasks too',\r\n type: 'boolean',\r\n default: false,\r\n alias: 'a',\r\n })\r\n .strict(false),\r\n handler: async (argv) => {\r\n const action = argv.action as string | undefined;\r\n const rest = (argv._ as string[]).slice(1);\r\n\r\n if (!action || action === 'list') {\r\n const showAll = argv.all as boolean;\r\n const tasks = getTasks();\r\n const filtered = showAll ? tasks : tasks.filter((t) => !t.done);\r\n if (filtered.length === 0) {\r\n console.log(chalk.gray(showAll ? 'No tasks.' : 'No open tasks. Use --all to show completed.'));\r\n return;\r\n }\r\n for (const t of filtered) {\r\n const check = t.done ? chalk.green('✓') : chalk.gray('○');\r\n const text = t.done ? chalk.strikethrough.gray(t.text) : t.text;\r\n const link = t.link ? chalk.blue(` [${t.link}]`) : '';\r\n console.log(` ${check} ${chalk.gray(`#${t.id}`)} ${text}${link}`);\r\n }\r\n return;\r\n }\r\n\r\n if (action === 'add') {\r\n const text = rest.join(' ');\r\n if (!text) {\r\n console.error('Usage: work todo add <text>');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n const task = await addTask(text);\r\n console.log(chalk.green(`Added #${task.id}: ${task.text}`));\r\n return;\r\n }\r\n\r\n if (action === 'done') {\r\n if (!/^\\d+$/.test(rest[0] ?? '')) {\r\n console.error('Invalid task id');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n const id = parseInt(rest[0], 10);\r\n if (isNaN(id)) {\r\n console.error('Usage: work todo done <id>');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n const task = await completeTask(id);\r\n if (!task) {\r\n console.error(`Task #${id} not found`);\r\n process.exitCode = 1;\r\n } else {\r\n console.log(chalk.green(`✓ #${id}: ${task.text}`));\r\n }\r\n return;\r\n }\r\n\r\n if (action === 'undo') {\r\n if (!/^\\d+$/.test(rest[0] ?? '')) {\r\n console.error('Invalid task id');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n const id = parseInt(rest[0], 10);\r\n if (isNaN(id)) {\r\n console.error('Usage: work todo undo <id>');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n const task = await uncompleteTask(id);\r\n if (!task) {\r\n console.error(`Task #${id} not found`);\r\n process.exitCode = 1;\r\n } else {\r\n console.log(chalk.yellow(`○ #${id}: ${task.text}`));\r\n }\r\n return;\r\n }\r\n\r\n if (action === 'rm') {\r\n if (!/^\\d+$/.test(rest[0] ?? '')) {\r\n console.error('Invalid task id');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n const id = parseInt(rest[0], 10);\r\n if (isNaN(id)) {\r\n console.error('Usage: work todo rm <id>');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n const task = await removeTask(id);\r\n if (!task) {\r\n console.error(`Task #${id} not found`);\r\n process.exitCode = 1;\r\n } else {\r\n console.log(chalk.red(`Removed #${id}: ${task.text}`));\r\n }\r\n return;\r\n }\r\n\r\n if (action === 'edit') {\r\n if (!/^\\d+$/.test(rest[0] ?? '')) {\r\n console.error('Invalid task id');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n const id = parseInt(rest[0], 10);\r\n const text = rest.slice(1).join(' ');\r\n if (isNaN(id) || !text) {\r\n console.error('Usage: work todo edit <id> <new text>');\r\n process.exitCode = 1;\r\n return;\r\n }\r\n const task = await editTask(id, text);\r\n if (!task) {\r\n console.error(`Task #${id} not found`);\r\n process.exitCode = 1;\r\n } else {\r\n console.log(chalk.cyan(`Updated #${id}: ${task.text}`));\r\n }\r\n return;\r\n }\r\n\r\n console.error(`Unknown action: ${action}`);\r\n console.log(chalk.yellow('Actions: add, done, undo, rm, edit'));\r\n process.exitCode = 1;\r\n },\r\n};\r\n","import chalk from 'chalk';\nimport type { CommandModule } from 'yargs';\nimport { ensureConfig } from '../core/config.js';\nimport { hydrateHistoryFromDisk } from '../core/hydrate.js';\n\nexport const hydrateCommand: CommandModule = {\n command: 'hydrate',\n describe:\n 'Scan worktreesRoot for existing worktrees and add untracked ones to history',\n builder: (yargs) => yargs,\n handler: async () => {\n const config = ensureConfig();\n const { discovered, added, updated } = await hydrateHistoryFromDisk(config);\n\n console.log('');\n console.log(chalk.cyan(`Discovered ${discovered} worktree(s) on disk.`));\n if (added > 0) {\n console.log(chalk.green(`Added ${added} new session(s) to history.`));\n }\n if (updated > 0) {\n console.log(chalk.yellow(`Updated paths for ${updated} session(s).`));\n }\n if (added === 0 && updated === 0) {\n console.log(chalk.gray('History already in sync with disk.'));\n }\n },\n};\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { WorkConfig } from './config.js';\nimport { parseWorktreeList } from './git.js';\nimport {\n type WorktreeSession,\n mergeHydratedSessions,\n} from './history.js';\n\ninterface HydrateResult {\n discovered: number;\n added: number;\n updated: number;\n}\n\n/**\n * Scan configured repos for worktrees under worktreesRoot and seed history\n * with any that aren't already tracked. Used for recovery from a wiped\n * history.json and to adopt worktrees created outside work.\n */\nexport async function hydrateHistoryFromDisk(\n config: WorkConfig,\n): Promise<HydrateResult> {\n const worktreesRoot = path.resolve(config.worktreesRoot);\n const folderNameToAlias = new Map<string, string>();\n for (const [alias, repoPath] of Object.entries(config.repos)) {\n folderNameToAlias.set(path.basename(repoPath), alias);\n }\n\n // Key: `${target}:${branch}`\n const discovered = new Map<string, WorktreeSession>();\n\n for (const [alias, repoPath] of Object.entries(config.repos)) {\n if (!fs.existsSync(repoPath)) continue;\n\n for (const entry of parseWorktreeList(repoPath)) {\n if (!entry.branch) continue;\n if (path.resolve(entry.path) === path.resolve(repoPath)) continue;\n\n const rel = path.relative(worktreesRoot, entry.path);\n if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) continue;\n\n const parts = rel.split(path.sep).filter(Boolean);\n if (parts.length < 2) continue;\n\n const [first, second] = parts;\n const isGroup = Object.prototype.hasOwnProperty.call(config.groups, first);\n\n if (isGroup) {\n const groupName = first;\n const key = `${groupName}:${entry.branch}`;\n const existing = discovered.get(key);\n if (existing) {\n if (!existing.paths.includes(entry.path)) {\n existing.paths.push(entry.path);\n existing.paths.sort();\n }\n } else {\n const { createdAt, lastAccessedAt } = inferTimestamps(entry.path);\n discovered.set(key, {\n target: groupName,\n isGroup: true,\n branch: entry.branch,\n paths: [entry.path],\n createdAt,\n lastAccessedAt,\n });\n }\n // silence unused warning for `second` — branch-dir segment isn't needed\n void second;\n } else {\n // Single-repo worktree. `first` must be the repo's folder name.\n const aliasFromFolder = folderNameToAlias.get(first);\n if (aliasFromFolder !== alias) continue;\n\n const key = `${alias}:${entry.branch}`;\n if (discovered.has(key)) continue;\n const { createdAt, lastAccessedAt } = inferTimestamps(entry.path);\n discovered.set(key, {\n target: alias,\n isGroup: false,\n branch: entry.branch,\n paths: [entry.path],\n createdAt,\n lastAccessedAt,\n });\n }\n }\n }\n\n const sessions = [...discovered.values()];\n const { added, updated } = await mergeHydratedSessions(sessions);\n return { discovered: sessions.length, added, updated };\n}\n\nfunction inferTimestamps(p: string): {\n createdAt: string;\n lastAccessedAt: string;\n} {\n try {\n const stat = fs.statSync(p);\n const created = stat.birthtime.getTime() > 0 ? stat.birthtime : stat.mtime;\n return {\n createdAt: created.toISOString(),\n lastAccessedAt: stat.mtime.toISOString(),\n };\n } catch {\n const now = new Date().toISOString();\n return { createdAt: now, lastAccessedAt: now };\n }\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { spawn as childSpawn } from 'node:child_process';\nimport chalk from 'chalk';\nimport type { CommandModule } from 'yargs';\nimport { computeDiff } from '../core/diff-pipeline.js';\nimport { stableDiffPath, type RepoSpec } from '../core/repo-spec.js';\nimport {\n buildRepoSpecs,\n findAnyParentBranch,\n resolveBase,\n resolveScope,\n type DiffScope,\n type ResolvedBase,\n} from '../core/diff-scope.js';\nimport {\n formatSingleComment,\n startCommentServer,\n} from '../core/comment-server.js';\nimport { diffReviewSnapshot } from '../core/review-poll.js';\nimport { renderStatic } from '../core/static-renderer.js';\nimport { openUrl } from '../utils/platform.js';\n\n/** Write an informational message to stderr. Keeps stdout clean so it can\n * be piped or captured by callers (notably `wd -c` review mode, where\n * stdout carries the comments markdown payload). */\nfunction info(message: string): void {\n process.stderr.write(message + '\\n');\n}\n\n/** Per-scope stable filesystem stem under ~/.work/diffs/. Used by the\n * static-render path to pick its output filename and (legacy) by the\n * `wd -c` comment-server fallback. */\nfunction scopePathStem(repoSpecs: RepoSpec[]): string {\n return stableDiffPath(repoSpecs.map((r) => r.root));\n}\n\n// mode handlers\ninterface RenderContext {\n scope: DiffScope;\n base: string;\n baseSource: ResolvedBase['source'];\n repoSpecs: RepoSpec[];\n /** Stable filesystem stem (no extension) under ~/.work/diffs/. The\n * static-render path appends `.html` to get its output file. */\n scopeStem: string;\n scopeLabel: string;\n}\n\n/**\n * `wd --stop`: de-register this scope from the running work web. The\n * work web process itself keeps running — it's a singleton serving\n * every scope, so killing the server because one user stopped one\n * review would punish every other open tab. Use `work web --stop`\n * to terminate the server.\n */\nasync function runStop(repoSpecs: RepoSpec[]): Promise<void> {\n const webUrl = readWebUrl();\n if (!webUrl) {\n info(chalk.gray('No work web running — nothing to stop.'));\n return;\n }\n // Compute the same scope hash work web does (sha1 of sorted root\n // paths) and DELETE it. `scope-routes.ts` clears the auto-snapshot\n // subscriber + manifest as part of that handler.\n const hash = stableDiffPath(repoSpecs.map((r) => r.root))\n .split(/[\\\\/]/)\n .pop()!;\n try {\n const res = await fetch(`${webUrl}api/scopes/${hash}`, {\n method: 'DELETE',\n });\n if (res.ok) {\n info(chalk.gray('De-registered this scope from work web.'));\n } else {\n info(\n chalk.yellow(\n `work web responded ${res.status} — scope may not have been registered.`,\n ),\n );\n }\n } catch (err) {\n console.error(\n chalk.red('Could not reach work web:'),\n (err as Error).message,\n );\n }\n}\n\nfunction webUrlFilePath(): string {\n return path.join(os.homedir(), '.work', 'web.url');\n}\n\n/**\n * Resolve the path to the `work` binary given the path of whichever\n * binary `wd`/`work` is currently running as. The `web` subcommand\n * only lives on the `work` binary (`dist/bin.js`); when we're running\n * as the `wd` shim (`dist/wd-bin.js`) we swap to the sibling. Tsup\n * ships both into the same dir so the sibling-swap is always valid.\n *\n * Exported for testing — the autostart spawn relies on this to avoid\n * passing `web --lean` to a binary that only knows `diff`.\n */\nexport function resolveWorkBinPath(selfArgv1: string): string {\n // argv[1] may be a bin symlink, not the real file: a global npm install\n // exposes `wd` as e.g. ~/.../bin/wd -> ../lib/.../dist/wd-bin.js. Resolve\n // it so the wd-bin.js -> bin.js sibling-swap fires for global installs too;\n // otherwise we'd spawn the `wd` shim with `web` args and it'd fail.\n let real = selfArgv1;\n try {\n real = fs.realpathSync(selfArgv1);\n } catch {\n /* synthetic/non-existent path (e.g. unit tests) — use as given */\n }\n if (real.endsWith('wd-bin.js')) {\n return path.join(path.dirname(real), 'bin.js');\n }\n return real;\n}\n\nfunction readWebUrl(): string | null {\n try {\n const v = fs.readFileSync(webUrlFilePath(), 'utf-8').trim();\n return v || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Spawn a detached, lean `work web` instance and wait for its url file\n * to appear. Used when `wd` runs with no existing work web — instead of\n * starting a per-scope standalone daemon, we boot a single shared\n * server in \"lean\" mode (skips the Claude activity watcher + hook\n * installation) and route every subsequent `wd` invocation through it.\n *\n * Returns the discovered URL on success, null on timeout. Safe against\n * a concurrent `wd` racing to do the same thing: the second `work web`\n * detects the singleton via the pid file and exits cleanly; the first\n * winner's url file is what both invocations end up reading.\n */\nasync function ensureWorkWebRunning(): Promise<string | null> {\n const existing = readWebUrl();\n if (existing) return existing;\n\n // Spawn `node <work-bin> web --lean --no-open` detached. We're\n // running as either `dist/bin.js` (the `work` binary) or\n // `dist/wd-bin.js` (the `wd` shim). The `web` subcommand only\n // lives on the `work` binary; `resolveWorkBinPath` does the sibling\n // swap when we're the shim.\n const workBin = resolveWorkBinPath(process.argv[1]);\n const out = fs.openSync(\n path.join(os.homedir(), '.work', 'web-autostart.log'),\n 'a',\n );\n const child = childSpawn(\n process.execPath,\n [workBin, 'web', '--lean', '--no-open'],\n {\n detached: true,\n stdio: ['ignore', out, out],\n windowsHide: true,\n // Inherit cwd doesn't matter for work web — its file-watches use\n // ~/.work paths exclusively.\n },\n );\n child.unref();\n fs.closeSync(out);\n\n // Poll the url file. Generous-ish timeout because cold startup\n // includes resolving the SPA dist + binding a port + writing the\n // file; 5 s leaves headroom for slow disks / antivirus on Windows.\n const url = await waitForUrlFile(webUrlFilePath(), 5000);\n return url;\n}\n\n/**\n * Try to register this scope with a running (or just-spawned) `work\n * web` server. Returns the browser URL to open\n * (e.g. `http://127.0.0.1:54321/diff/abc123`) on success, null when\n * everything (existing instance + autostart) failed.\n */\nasync function tryRegisterWithWorkWeb(\n ctx: RenderContext,\n routeKind: 'diff' | 'review',\n): Promise<string | null> {\n const webUrl = await ensureWorkWebRunning();\n if (!webUrl) return null;\n try {\n const res = await fetch(`${webUrl}api/scopes`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n paths: ctx.repoSpecs.map((r) => r.root),\n label: ctx.scopeLabel,\n }),\n });\n if (!res.ok) return null;\n const body = (await res.json()) as { hash: string };\n const route = routeKind === 'diff' ? 'diff' : 'review';\n return `${webUrl}${route}/${body.hash}`;\n } catch {\n return null;\n }\n}\n\n/** Foreground launcher: opens a diff in the browser via `work web`. When\n * no work web is running, we auto-start a lean one (`tryRegisterWithWorkWeb`\n * handles the spawn + wait) — a single server backs every `wd` invocation\n * across all worktrees, no per-scope daemon class. */\nasync function runLauncher(ctx: RenderContext): Promise<void> {\n const webRouteUrl = await tryRegisterWithWorkWeb(ctx, 'diff');\n if (webRouteUrl) {\n info(chalk.gray(`Opening: ${webRouteUrl}`));\n openUrl(webRouteUrl);\n return;\n }\n // Reached only on autostart failure (port refused, dist/web missing,\n // 5 s wait elapsed). Surface a clear error rather than silently\n // falling back to a per-scope process the user would then leak.\n console.error(\n chalk.red('Could not start or reach work web.'),\n );\n console.error(\n chalk.gray(\n `Tail ~/.work/web-autostart.log for diagnostics, or run \\`work web\\` in another shell to inspect startup directly.`,\n ),\n );\n process.exitCode = 1;\n}\n\n/**\n * Static-file mode (the default for `wd`). Renders the React SPA shell\n * with the diff data inlined, writes it to ~/.work/diffs/<hash>.html,\n * opens the browser at file://…, and exits. No server, no daemon, no\n * port — the file works forever (modulo your filesystem). Same React\n * components and screens as the live server: file tree, scrollspy,\n * viewed checkboxes, syntax highlighting, intra-line diff.\n *\n * Trade-off: no live reload, no comments. Use `wd --server` (or `wd -c`\n * for review mode) when you need those.\n */\nfunction runStatic(ctx: RenderContext, initialBranch: boolean): void {\n // Always compute the uncommitted scope (cheap, the user expects it as\n // the default tab).\n const uncommitted = buildRepoSpecs(ctx.scope, 'HEAD');\n\n // Try to resolve a parent branch for each repo. If we find one, compute\n // the \"since branch\" scope too so the SPA can offer the toggle. If not,\n // the tab simply doesn't appear.\n //\n // Group worktrees: each sub-repo may have a different parent. We pick a\n // representative resolvedBase from the active repo (or the first one)\n // for the badge label; per-repo merge-base lookups still happen inside\n // buildRepoSpecs.\n const primaryRoot =\n ctx.scope.repos.find((r) => r.name === ctx.scope.activeRepoName)?.root ??\n ctx.scope.repos[0].root;\n // Per-repo fork points (group worktrees forked with different bases). Each\n // repo's spec resolves against its own base inside buildRepoSpecs.\n const perRepoBase = ctx.scope.session?.baseBranches;\n // Representative parent for the badge label: the active repo's recorded\n // base, else the session default, else lenient auto-detect. The lenient\n // finder keeps the toggle available even when the branch has no commits\n // past its parent yet (the diff just renders empty — better than no tab).\n const parent =\n perRepoBase?.[primaryRoot] ??\n ctx.scope.session?.baseBranch ??\n findAnyParentBranch(primaryRoot);\n const branch =\n parent === null\n ? undefined\n : {\n specs: buildRepoSpecs(ctx.scope, parent, perRepoBase),\n resolvedBase: parent,\n };\n\n // Don't write a file when both views are empty — `wd` is a viewer,\n // not a generator of empty pages.\n const uncommittedTotal = uncommitted.reduce(\n (s, r) => s + computeDiff({ root: r.root, diffArg: r.diffArg }).length,\n 0,\n );\n const branchTotal = branch\n ? branch.specs.reduce(\n (s, r) => s + computeDiff({ root: r.root, diffArg: r.diffArg }).length,\n 0,\n )\n : 0;\n if (uncommittedTotal === 0 && branchTotal === 0) {\n info(chalk.gray('No changes to show.'));\n return;\n }\n\n const html = renderStatic({\n scopeLabel: ctx.scopeLabel,\n uncommitted,\n branch,\n initialBase: initialBranch && branch ? 'branch' : 'uncommitted',\n });\n const filePath = `${ctx.scopeStem}.html`;\n fs.writeFileSync(filePath, html, 'utf-8');\n info(chalk.gray(`Wrote ${filePath}`));\n openUrl(`file:///${filePath.replace(/\\\\/g, '/')}`);\n}\n\nfunction waitForUrlFile(filePath: string, timeoutMs: number): Promise<string | null> {\n return new Promise((resolve) => {\n const start = Date.now();\n const tick = () => {\n try {\n const v = fs.readFileSync(filePath, 'utf-8').trim();\n if (v) return resolve(v);\n } catch { /* not yet */ }\n if (Date.now() - start > timeoutMs) return resolve(null);\n setTimeout(tick, 75);\n };\n tick();\n });\n}\n\n/**\n * Try to route `wd -c` through a running `work web`. Registers the scope\n * as a reviewable view, opens the browser at /review/<hash>, then polls\n * the scope's comments and proxies them to stdout as the markers the\n * `wd-review` skill consumes (`--- review started ---`,\n * `--- comment ---`, etc.). Returns true if work web handled the review;\n * the caller falls back to a standalone server when this returns false.\n */\nasync function tryReviewViaWorkWeb(ctx: RenderContext): Promise<boolean> {\n const webUrl = await ensureWorkWebRunning();\n if (!webUrl) return false;\n\n let hash: string;\n try {\n const res = await fetch(`${webUrl}api/scopes`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n paths: ctx.repoSpecs.map((r) => r.root),\n label: ctx.scopeLabel,\n }),\n });\n if (!res.ok) return false;\n hash = ((await res.json()) as { hash: string }).hash;\n } catch {\n return false;\n }\n\n const initialTotal = ctx.repoSpecs.reduce(\n (s, r) => s + computeDiff({ root: r.root, diffArg: r.diffArg }).length,\n 0,\n );\n const reviewUrl = `${webUrl}review/${hash}`;\n info(chalk.gray(`Opening review in work web: ${reviewUrl}`));\n info(chalk.gray('Comments stream below. Ctrl+C to detach.'));\n process.stdout.write(\n `--- review started ---\\nrepos: ${ctx.repoSpecs.map((r) => r.name).join(', ')}\\nfiles: ${initialTotal}\\nurl: ${reviewUrl}\\n\\n`,\n );\n openUrl(reviewUrl);\n\n // Poll the scope's comments. Cheap (one localhost JSON GET) and\n // simpler than reading SSE chunks in Node. The skill latency budget\n // is generous so 1s is fine.\n const seen = new Set<string>();\n let exiting = false;\n let interval: NodeJS.Timeout | null = null;\n // Reentrancy guard. `setInterval(poll, 1000)` fires every second\n // regardless of whether the previous tick has resolved. A slow `work\n // web` response (>1s) would otherwise let two `poll()`s mutate `seen`\n // concurrently. JS being single-threaded makes interleaving impossible\n // for synchronous blocks but the await points do let two flows\n // interleave — duplicate `--- review done ---` markers if `ended:true`\n // races with a slow prior poll.\n let polling = false;\n\n type SnapshotComment = import('../core/comment-types.js').Comment;\n\n async function poll(): Promise<void> {\n if (exiting || polling) return;\n polling = true;\n try {\n const res = await fetch(`${webUrl}api/scopes/${hash}/comments`);\n if (res.status === 404) {\n // Scope is gone (user explicitly removed it). Treat like\n // End Review for the marker stream.\n process.stdout.write(`--- review done ---\\ntotal: ${seen.size}\\n`);\n cleanup();\n process.exit(0);\n }\n if (!res.ok) return;\n const { comments, ended } = (await res.json()) as {\n comments: SnapshotComment[];\n ended?: boolean;\n };\n // Compute deltas before checking `ended` so any comments posted\n // in the same batch as End Review still flush through.\n const { newComments, deleted } = diffReviewSnapshot(comments, seen);\n for (const id of deleted) {\n process.stdout.write(`--- comment deleted ---\\nid: ${id}\\n\\n`);\n }\n for (const c of newComments) {\n process.stdout.write(formatSingleComment(c));\n }\n // End Review fired: emit the done marker and exit. The scope\n // (and the browser tab) stays viewable — only the CLI proxy\n // stops.\n if (ended) {\n process.stdout.write(\n `--- review done ---\\ntotal: ${seen.size}\\n`,\n );\n cleanup();\n process.exit(0);\n }\n } catch { /* transient — retry next tick */ }\n finally { polling = false; }\n }\n\n function cleanup(): void {\n exiting = true;\n if (interval) clearInterval(interval);\n // Don't deregister — the browser tab and URL should keep working\n // after the CLI exits. Scopes live in work web's memory until it\n // restarts or the user removes them from the dashboard.\n }\n\n const onSignal = () => {\n process.stdout.write(`--- review aborted (signal) ---\\n`);\n cleanup();\n process.exit(0);\n };\n process.on('SIGINT', onSignal);\n process.on('SIGTERM', onSignal);\n\n // Arm the interval BEFORE the first poll. If the first poll triggers\n // cleanup (e.g. scope already ended, 404), `cleanup()` clears this\n // interval correctly — otherwise the previous shape left a small window\n // where setInterval would be assigned AFTER cleanup ran, which mocked\n // tests reproduced (the real runtime survives because process.exit is\n // synchronous, but tests that stub process.exit would leak the timer).\n interval = setInterval(poll, 1000);\n await poll();\n // Block forever — cleanup happens via signal or 404 detection.\n await new Promise(() => {});\n return true;\n}\n\nasync function runReview(ctx: RenderContext): Promise<void> {\n // Quick check — early-exit if nothing to review. (Server will recompute\n // on each /api/diff request thereafter, which is what's served live.)\n const initialTotal = ctx.repoSpecs.reduce(\n (s, r) => s + computeDiff({ root: r.root, diffArg: r.diffArg }).length,\n 0,\n );\n if (initialTotal === 0) {\n info(chalk.gray('No changes to review.'));\n return;\n }\n\n // Prefer work web when it's running — same consolidation pattern as\n // `wd` (read-only). The CLI proxies the marker stream so the\n // wd-review skill flow is unchanged. Falls back to a standalone\n // comment-server when work web isn't up.\n if (await tryReviewViaWorkWeb(ctx)) return;\n\n const onComment = (c: import('../core/comment-server.js').Comment) => {\n process.stdout.write(formatSingleComment(c));\n };\n const onCommentDeleted = (id: string) => {\n process.stdout.write(`--- comment deleted ---\\nid: ${id}\\n\\n`);\n };\n const onSubmitReviewStart = (info: {\n count: number;\n summary: import('../core/comment-server.js').Comment | null;\n }) => {\n const head = `--- review submitted ---\\ncount: ${info.count}${info.summary ? `\\nsummary-id: ${info.summary.id}` : ''}\\n\\n`;\n process.stdout.write(head);\n };\n const onSubmitReviewEnd = () => {\n process.stdout.write(`--- review batch end ---\\n\\n`);\n };\n\n const handle = await startCommentServer({\n repos: ctx.repoSpecs,\n scopeLabel: ctx.scopeLabel,\n sessionBaseBranch: ctx.scope.session?.baseBranch,\n sessionBaseBranches: ctx.scope.session?.baseBranches,\n onComment,\n onCommentDeleted,\n onSubmitReviewStart,\n onSubmitReviewEnd,\n });\n\n // URL discovery is intentionally not persisted to disk. The only\n // consumer (Claude via the wd-review skill) reads it from the\n // `--- review started ---` marker on stdout below. Skipping the\n // file write removes the only way a stale or wrong URL could leak\n // into a different Claude session.\n info(chalk.gray('Opening browser for review. Comments stream as you save them.'));\n info(chalk.gray('Page reloads automatically when you save a file. Click \"End review\" (or Ctrl+C) when finished.'));\n process.stdout.write(\n `--- review started ---\\nrepos: ${ctx.repoSpecs.map((r) => r.name).join(', ')}\\nfiles: ${initialTotal}\\nurl: ${handle.url}\\n\\n`,\n );\n openUrl(handle.url);\n\n const cleanup = () => {\n handle.stop();\n };\n\n const onSignal = () => {\n process.stdout.write(`--- review aborted (signal) ---\\n`);\n cleanup();\n process.exit(0);\n };\n process.on('SIGINT', onSignal);\n process.on('SIGTERM', onSignal);\n\n const comments = await handle.waitForDone();\n await new Promise((r) => setTimeout(r, 100));\n cleanup();\n process.stdout.write(`--- review done ---\\ntotal: ${comments.length}\\n`);\n}\n\n// command export\nexport const diffCommand: CommandModule = {\n command: 'diff [base]',\n describe: 'Open a GitHub-PR-style diff overview in your browser',\n builder: (yargs) =>\n yargs\n .positional('base', {\n describe:\n 'Base ref to compare against. Default: HEAD (uncommitted only). Use --branch for a full PR-style diff vs the parent branch.',\n type: 'string',\n })\n .option('branch', {\n type: 'boolean',\n default: false,\n describe:\n 'Open the \"Since branch\" tab by default (still shows uncommitted as the other tab — toggle in the browser).',\n })\n .option('static', {\n type: 'boolean',\n default: false,\n describe:\n 'Write a self-contained HTML file with the current diff inlined (no server, no live reload). The default is a live server you can refresh.',\n })\n .option('server', {\n type: 'boolean',\n default: false,\n hidden: true,\n describe:\n 'Run a live server (now the default; flag kept for back-compat).',\n })\n .option('watch', {\n type: 'boolean',\n default: false,\n hidden: true,\n describe: 'Alias for --server. Kept for back-compat.',\n })\n .option('stop', {\n type: 'boolean',\n default: false,\n describe:\n \"De-register this scope from work web. The work web server itself keeps running; use `work web --stop` to terminate the server.\",\n })\n .option('comments', {\n type: 'boolean',\n alias: 'c',\n default: false,\n describe:\n 'Review mode: open the diff in a browser with a comment UI; block until you click \"Done & Send\", then print all comments to stdout as markdown.',\n }),\n handler: async (argv) => {\n const scope = resolveScope(process.cwd());\n if (!scope) {\n console.error(chalk.red('Not inside a git repository or known worktree.'));\n process.exit(1);\n }\n\n const { base, source: baseSource } = resolveBase(scope, {\n base: argv.base as string | undefined,\n branch: argv.branch as boolean | undefined,\n });\n const repoSpecs = buildRepoSpecs(scope, base);\n const scopeStem = scopePathStem(repoSpecs);\n\n // `wd --stop` doesn't need the full ctx — it only needs the repo\n // specs to derive the scope hash. Short-circuit before the\n // status-message block so a stop call stays quiet.\n if (argv.stop) return runStop(repoSpecs);\n\n // Use stderr for status messages so review mode's stdout stays clean\n // (the comments markdown is the only data wd writes to stdout).\n if (base === 'HEAD') {\n info(chalk.gray('Showing uncommitted changes vs HEAD.'));\n } else {\n info(\n chalk.gray(\n `Showing diff vs ${base} [${baseSource}]${scope.isGroup ? `, across ${scope.repos.length} repos` : ''}.`,\n ),\n );\n }\n\n const scopeLabel = `${scope.repos.map((r) => r.name).join(', ')} · ${base}`;\n const ctx: RenderContext = {\n scope,\n base,\n baseSource,\n repoSpecs,\n scopeStem,\n scopeLabel,\n };\n\n if (argv.comments) return runReview(ctx);\n if (argv.static) return runStatic(ctx, !!argv.branch);\n // Default: live server via work web. `runLauncher` registers the\n // scope (auto-spawning a lean work web when one isn't running).\n return runLauncher(ctx);\n },\n};\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport spawn from 'cross-spawn';\nimport { git } from './git.js';\nimport {\n parseGitDiff,\n type MarkdownContent,\n type ParsedFile,\n} from './diff-parse.js';\nimport { coverageLookup } from './lcov.js';\nimport { writeTempTree } from './git-tree-snapshot.js';\n\n/** `git`'s rename-detection flag. Centralised so the three diff invocations\n * (HEAD-vs-working, checkpoint-vs-working, checkpoint-vs-checkpoint) stay in\n * lockstep — tuning the threshold (e.g. `-M40%`) or disabling it is a\n * one-line change here. */\nconst RENAME_DETECT = '-M';\n\nconst MARKDOWN_EXT_RE = /\\.(md|markdown|mdx)$/i;\n\n/** Per-side cap on markdown content embedded in the diff payload. A\n * 256 KiB ceiling covers normal docs (README, CHANGELOG up to a year\n * of releases, design docs) while preventing auto-generated multi-MB\n * markdown (API refs, vendored docs) from ballooning SSE reload\n * traffic and the browser heap. When either side hits the cap, both\n * sides are dropped and `tooLarge: true` is set so the SPA hides the\n * Preview/Split toggle. */\nconst MARKDOWN_SIZE_CAP = 256 * 1024;\n\nfunction isMarkdownPath(p: string): boolean {\n return p !== '/dev/null' && MARKDOWN_EXT_RE.test(p);\n}\n\n/**\n * Reject paths that would resolve outside the repo root. Diff output is\n * parsed text — a pathological commit (or a manipulated diff stream) could\n * carry `../../etc/passwd`-style entries. Without this check, the markdown\n * \"after\" read would happily exfiltrate arbitrary local files into the\n * SPA's diff payload.\n *\n * Exported for unit testing — also useful to other call sites that\n * consume `ParsedFile.path` for filesystem access.\n */\nexport function isInsideRoot(root: string, rel: string): boolean {\n const resolvedRoot = path.resolve(root);\n const resolvedTarget = path.resolve(resolvedRoot, rel);\n // path.relative returns \"\" for the same dir, \"subdir/x\" for nested, or\n // a \"../\" path for escapes — easiest portable check.\n const r = path.relative(resolvedRoot, resolvedTarget);\n if (r === '') return true;\n if (r.startsWith('..')) return false;\n if (path.isAbsolute(r)) return false;\n return true;\n}\n\n/**\n * For markdown files, fetch the \"before\" content via `git show <fromRef>:<oldPath>`\n * and the \"after\" content either from the working tree (when comparing\n * against the working tree) or from `git show <toRef>:<newPath>` (when\n * comparing two committed snapshots). Either side may be absent for added\n * / deleted / failed fetches.\n *\n * Uses `cross-spawn` directly (not the `git()` helper) because that helper\n * trims stdout, which would silently drop trailing newlines in the source.\n */\nfunction readMarkdownContent(\n root: string,\n file: ParsedFile,\n fromRef: string,\n toRef: string | 'working',\n): MarkdownContent | undefined {\n if (file.isBinary) return undefined;\n if (!isMarkdownPath(file.oldPath) && !isMarkdownPath(file.newPath)) {\n return undefined;\n }\n\n const showAt = (ref: string, p: string): string | undefined => {\n const r = spawn.sync('git', ['show', `${ref}:${p}`], {\n cwd: root,\n encoding: 'utf-8',\n maxBuffer: 16 * 1024 * 1024,\n windowsHide: true,\n });\n if (r.status === 0 && typeof r.stdout === 'string') return r.stdout;\n return undefined;\n };\n\n const result: MarkdownContent = {};\n\n if (\n file.status !== 'added' &&\n isMarkdownPath(file.oldPath) &&\n isInsideRoot(root, file.oldPath)\n ) {\n result.before = showAt(fromRef, file.oldPath);\n }\n\n if (\n file.status !== 'deleted' &&\n isMarkdownPath(file.newPath) &&\n isInsideRoot(root, file.newPath)\n ) {\n if (toRef === 'working') {\n try {\n const absPath = path.join(root, file.newPath);\n // Resolve symlinks BEFORE reading. The `isInsideRoot` check\n // above blocks `../`-style traversal in the textual path, but\n // a repo could legitimately contain a symlink whose target\n // sits outside the worktree (e.g. `notes.md -> /etc/passwd`).\n // Without this, opening a clone of an adversarial repo would\n // exfiltrate the target into the diff payload.\n //\n // Both sides of the containment comparison MUST be canonical:\n // on macOS `/tmp` and `/var` are themselves symlinks (to\n // `/private/tmp` and `/private/var`), and `path.resolve` does\n // NOT follow symlinks — only `realpath` does. Without\n // canonicalising `root` too, every working-tree path under a\n // symlinked ancestor (default `/var/folders/...` TMPDIR target\n // on macOS, plus Linux mounts and Windows directory junctions)\n // resolves to a different namespace from `root`, the relative\n // path comes out `..`-prefixed, and the guard silently rejects\n // legitimate in-repo files.\n const realRoot = fs.realpathSync(path.resolve(root));\n const realPath = fs.realpathSync(absPath);\n const sep = path.sep;\n if (\n realPath === realRoot ||\n realPath.startsWith(realRoot + sep)\n ) {\n result.after = fs.readFileSync(absPath, 'utf-8');\n }\n } catch {\n // Working-tree file missing OR realpath failed — no after-content.\n }\n } else {\n result.after = showAt(toRef, file.newPath);\n }\n }\n\n if (result.before === undefined && result.after === undefined) {\n return undefined;\n }\n // Drop both sides when either exceeds the cap. We compare byte length\n // (Buffer.byteLength) rather than `.length`, which counts UTF-16\n // code units in JS and undercounts multi-byte characters — the\n // browser pays for bytes, not chars.\n const tooBig =\n (result.before !== undefined &&\n Buffer.byteLength(result.before, 'utf-8') > MARKDOWN_SIZE_CAP) ||\n (result.after !== undefined &&\n Buffer.byteLength(result.after, 'utf-8') > MARKDOWN_SIZE_CAP);\n if (tooBig) {\n return { tooLarge: true };\n }\n return result;\n}\n\nexport interface ComputeDiffOptions {\n /** Git toplevel working directory. */\n root: string;\n /** Argument to `git diff` (ref, sha, or \"HEAD\"). */\n diffArg: string;\n}\n\n/**\n * Tree-sha snapshot of the current working tree (incl. untracked, subject to\n * .gitignore) without disturbing the real git index. Used by\n * `computeRangeDiff` to do a true tree-vs-tree diff against a checkpoint\n * commit: `git diff <ref>` would otherwise produce phantom \"deleted\" entries\n * for files that were untracked-at-snapshot-time, because git's working-tree\n * diff goes through the real index and untracked files appear \"absent\".\n *\n * Thin wrapper over the shared `writeTempTree` helper (also used by\n * `checkpoint.ts::snapshotRepo`).\n */\nfunction workingTreeTreeSha(root: string): string | null {\n return writeTempTree(root, { includeWorkingTree: true })?.treeSha ?? null;\n}\n\n/**\n * Detect binary content by scanning the first 8 KiB for null bytes. Matches\n * what most text-handling tools (git itself, less, vim) use as a heuristic.\n */\nfunction isBinaryContent(buffer: Buffer): boolean {\n const len = Math.min(buffer.length, 8192);\n for (let i = 0; i < len; i++) {\n if (buffer[i] === 0) return true;\n }\n return false;\n}\n\n/**\n * Render a unified-diff block for a single untracked file as if it were\n * being added. Avoids mutating the git index (which the previous\n * `add --intent-to-add` / `reset` approach did, with risk of leaving stale\n * index entries on crash).\n */\nfunction synthesizeUntrackedDiff(root: string, relPath: string): string {\n const absPath = path.join(root, relPath);\n let buffer: Buffer;\n try {\n buffer = fs.readFileSync(absPath);\n } catch {\n return '';\n }\n\n const header = `diff --git a/${relPath} b/${relPath}\\nnew file mode 100644\\n`;\n\n if (isBinaryContent(buffer)) {\n return `${header}Binary files /dev/null and b/${relPath} differ\\n`;\n }\n\n const content = buffer.toString('utf-8');\n if (content.length === 0) {\n // Empty file — no hunks, just the headers.\n return `${header}--- /dev/null\\n+++ b/${relPath}\\n`;\n }\n\n const hasTrailingNewline = content.endsWith('\\n');\n const lines = content.split('\\n');\n if (hasTrailingNewline) lines.pop();\n\n let out = `${header}--- /dev/null\\n+++ b/${relPath}\\n@@ -0,0 +1,${lines.length} @@\\n`;\n for (const line of lines) {\n out += `+${line}\\n`;\n }\n if (!hasTrailingNewline) {\n out += `\\\\\n`;\n }\n return out;\n}\n\n/**\n * Run `git diff <ref>` against the working tree, then synthesize unified-diff\n * blocks for any untracked files (so they show up as new). Read-only — no\n * changes to the git index, safe under concurrent invocations and crash.\n */\nexport function computeDiff(opts: ComputeDiffOptions): ParsedFile[] {\n const { root, diffArg } = opts;\n\n const trackedResult = spawn.sync(\n 'git',\n // -w (ignore-all-space) hides pure whitespace changes so a reformat\n // of indentation doesn't drown out the real changes. RENAME_DETECT turns\n // on rename detection so a `git mv` (+ edits) shows as one renamed entry\n // with its inline diff instead of a separate delete + add pair.\n ['diff', '--no-color', '--no-ext-diff', '-w', RENAME_DETECT, diffArg],\n {\n cwd: root,\n encoding: 'utf-8',\n maxBuffer: 500 * 1024 * 1024,\n windowsHide: true,\n },\n );\n if (trackedResult.status !== 0) {\n if (trackedResult.stderr) console.error(trackedResult.stderr);\n return [];\n }\n\n let combined = trackedResult.stdout;\n\n // Append synthetic diffs for untracked files (respecting .gitignore).\n const untrackedResult = git(\n ['ls-files', '--others', '--exclude-standard'],\n root,\n );\n if (untrackedResult.exitCode === 0 && untrackedResult.stdout) {\n const files = untrackedResult.stdout.split('\\n').filter(Boolean);\n for (const file of files) {\n const block = synthesizeUntrackedDiff(root, file);\n if (block) combined += block;\n }\n }\n\n const files = parseGitDiff(combined);\n\n // Attach per-file line-coverage from an lcov.info if the repo has one.\n // Conservative: only files with a confident repo-relative path match get a\n // coverage value; everything else is left undefined (no badge rendered).\n // The lcov parse is memoized by (path, mtimeMs) inside `coverageLookup`, so\n // a multi-MB lcov is NOT re-read on every SSE / chokidar refresh tick.\n attachCoverage(root, files);\n\n // Augment markdown files with full before/after content so the SPA can\n // render a preview alongside the diff. Cheap (one `git show` + one fs\n // read per .md file), skipped entirely for non-markdown files.\n for (const file of files) {\n const md = readMarkdownContent(root, file, diffArg, 'working');\n if (md) file.mdContent = md;\n }\n\n return files;\n}\n\n/**\n * Attach line-coverage (+ lcov mtime + staleness) to each file in place.\n * Staleness: a file whose working-tree source `mtimeMs` is NEWER than the\n * lcov.info it was measured against has been edited since coverage was last\n * recorded, so its percent is no longer authoritative. We flag it\n * (`coverageStale`) rather than dropping the number outright — the SPA\n * de-emphasizes / suppresses the badge and the tooltip carries the lcov\n * timestamp so stale coverage is never presented as current.\n */\nfunction attachCoverage(root: string, files: ParsedFile[]): void {\n const { byPath, lcovMtimeMs } = coverageLookup(\n root,\n files.map((f) => f.path),\n );\n if (byPath.size === 0) return;\n for (const f of files) {\n const pct = byPath.get(f.path);\n if (typeof pct !== 'number') continue;\n f.coverage = pct;\n if (lcovMtimeMs != null) {\n f.coverageMtimeMs = lcovMtimeMs;\n let srcMtimeMs: number | null = null;\n try {\n srcMtimeMs = fs.statSync(path.join(root, f.path)).mtimeMs;\n } catch {\n srcMtimeMs = null;\n }\n if (srcMtimeMs != null && srcMtimeMs > lcovMtimeMs) {\n f.coverageStale = true;\n }\n }\n }\n}\n\nexport interface ComputeRangeDiffOptions {\n /** Git toplevel working directory. */\n root: string;\n /** Commit sha (or ref) for the left side. */\n fromRef: string;\n /** Commit sha (or ref) for the right side, or `'working'` for the\n * working tree (in which case behaviour matches `computeDiff` and\n * untracked files are synthesised). */\n toRef: string | 'working';\n}\n\n/**\n * Diff between two snapshot points. When `toRef === 'working'` this is\n * `computeDiff` with the same diffArg — the only path that needs untracked\n * synthesis. When `toRef` is a real commit, both sides are committed trees\n * (checkpoints already include what was untracked at capture time via the\n * temp-index `git add -A`), so a plain `git diff <from> <to>` covers it.\n *\n * Markdown content is fetched from the appropriate ref on each side so the\n * Preview/Split view works for historical range views too.\n */\nexport function computeRangeDiff(opts: ComputeRangeDiffOptions): ParsedFile[] {\n const { root, fromRef, toRef } = opts;\n\n if (toRef === 'working') {\n if (fromRef === 'HEAD') {\n // Legacy HEAD-vs-working path — keep the existing untracked\n // synthesis. This is what `wd` (no checkpoints) has always done.\n return computeDiff({ root, diffArg: fromRef });\n }\n // Range against a real checkpoint commit. `git diff <ref>` can't be\n // used directly here: it routes through the real index, so any file\n // that was untracked-at-snapshot-time (and captured into the\n // checkpoint's tree via the temp-index `add -A`) appears DELETED\n // from the working tree — a phantom that would render as\n // \"removed\" on every range view. Build a tree-sha snapshot of the\n // live working tree (same temp-index dance) and do a clean\n // tree-vs-tree diff. No untracked synth needed; the working-tree\n // tree already contains everything.\n const wtTreeSha = workingTreeTreeSha(root);\n if (!wtTreeSha) return [];\n const result = spawn.sync(\n 'git',\n // RENAME_DETECT: detect renames between the checkpoint tree and the\n // working tree so a rename (with or without edits) renders as one\n // entry, matching the HEAD-vs-working path above.\n ['diff-tree', '-r', '-p', RENAME_DETECT, '--no-color', '--no-ext-diff', fromRef, wtTreeSha],\n {\n cwd: root,\n encoding: 'utf-8',\n maxBuffer: 500 * 1024 * 1024,\n windowsHide: true,\n },\n );\n if (result.status !== 0) {\n if (result.stderr) console.error(result.stderr);\n return [];\n }\n const parsed = parseGitDiff(result.stdout);\n for (const file of parsed) {\n const md = readMarkdownContent(root, file, fromRef, 'working');\n if (md) file.mdContent = md;\n }\n return parsed;\n }\n\n // No `-w` here. `computeDiff` (HEAD-vs-working-tree) uses `-w` to hide\n // reformatting noise — but checkpoint-to-checkpoint diffs are user-\n // selected ranges where a whitespace-only change between snapshots is\n // a legitimate change the user explicitly asked to see. Suppressing it\n // would make the strip silently show \"no changes\" for a turn that did\n // exactly one reformatting pass.\n const result = spawn.sync(\n 'git',\n // RENAME_DETECT: rename detection between two checkpoint commits (no -w\n // here — see the note above on why range diffs keep whitespace changes).\n ['diff', '--no-color', '--no-ext-diff', RENAME_DETECT, fromRef, toRef],\n {\n cwd: root,\n encoding: 'utf-8',\n maxBuffer: 500 * 1024 * 1024,\n windowsHide: true,\n },\n );\n if (result.status !== 0) {\n if (result.stderr) console.error(result.stderr);\n return [];\n }\n\n const files = parseGitDiff(result.stdout);\n for (const file of files) {\n const md = readMarkdownContent(root, file, fromRef, toRef);\n if (md) file.mdContent = md;\n }\n return files;\n}\n","export type FileStatus = 'added' | 'deleted' | 'modified' | 'renamed' | 'binary';\n\nexport interface ParsedFile {\n /** Path used by GitHub-style anchors / sidebar tree. For renames, this is the new path. */\n path: string;\n oldPath: string;\n newPath: string;\n status: FileStatus;\n isBinary: boolean;\n added: number;\n deleted: number;\n hunks: Hunk[];\n /** Line-coverage percent for this file (from lcov). undefined when no lcov data is available. */\n coverage?: number;\n /** Epoch-ms mtime of the lcov.info the `coverage` value came from. Lets the\n * SPA show *when* the coverage was measured. undefined when no lcov data. */\n coverageMtimeMs?: number;\n /** True when this file's working-tree source is newer than the lcov.info the\n * coverage came from — i.e. the percent is stale and MUST NOT be presented\n * as authoritative. The SPA suppresses / de-emphasizes the badge. */\n coverageStale?: boolean;\n /** Full file contents for markdown rendering. Populated only for `.md` /\n * `.markdown` / `.mdx` files by the diff pipeline so the SPA can show a\n * rendered preview. `before` is from `git show <diffArg>:<oldPath>`,\n * `after` is the working-tree file. Either side may be absent for\n * added / deleted files. */\n mdContent?: MarkdownContent;\n}\n\nexport interface MarkdownContent {\n before?: string;\n after?: string;\n /** Set when either side exceeded the per-side size cap. The SPA uses\n * this to hide the Preview/Split toggle — rendering a 10 MB\n * markdown blob in the diff payload would balloon SSE reloads and\n * blow the browser heap. */\n tooLarge?: boolean;\n}\n\nexport interface Hunk {\n oldStart: number;\n oldLines: number;\n newStart: number;\n newLines: number;\n /** Trailing text after \"@@ ... @@\" — typically the enclosing function/class. */\n context: string;\n lines: HunkLine[];\n}\n\nexport type LineKind = 'context' | 'add' | 'delete' | 'no-newline';\n\nexport interface HunkLine {\n kind: LineKind;\n content: string;\n oldNum: number | null;\n newNum: number | null;\n}\n\nconst HUNK_RE = /^@@ -(\\d+)(?:,(\\d+))? \\+(\\d+)(?:,(\\d+))? @@(.*)$/;\n\n/**\n * Parse the output of `git diff` (or `git diff <ref>`) into a list of files\n * with hunks. The input is assumed to be in unified diff format with the\n * default git headers (`diff --git`, `--- a/...`, `+++ b/...`).\n */\nexport function parseGitDiff(input: string): ParsedFile[] {\n const lines = input.split(/\\r?\\n/);\n const files: ParsedFile[] = [];\n let current: ParsedFile | null = null;\n let currentHunk: Hunk | null = null;\n let oldLineNum = 0;\n let newLineNum = 0;\n\n function startFile(oldPath: string, newPath: string): ParsedFile {\n const f: ParsedFile = {\n path: newPath !== '/dev/null' ? newPath : oldPath,\n oldPath,\n newPath,\n status: 'modified',\n isBinary: false,\n added: 0,\n deleted: 0,\n hunks: [],\n };\n files.push(f);\n return f;\n }\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n\n // `diff --git a/path b/path` — start of a new file. We may not learn\n // the real paths until the `---`/`+++` lines below, so just close out\n // the previous file here.\n if (line.startsWith('diff --git ')) {\n current = null;\n currentHunk = null;\n const match = line.match(/^diff --git \"?a\\/(.+?)\"? \"?b\\/(.+?)\"?$/);\n if (match) {\n current = startFile(match[1], match[2]);\n }\n continue;\n }\n\n if (!current) continue;\n\n if (line.startsWith('new file mode')) {\n current.status = 'added';\n continue;\n }\n if (line.startsWith('deleted file mode')) {\n current.status = 'deleted';\n continue;\n }\n if (line.startsWith('rename from ')) {\n current.status = 'renamed';\n current.oldPath = line.slice('rename from '.length);\n continue;\n }\n if (line.startsWith('rename to ')) {\n current.newPath = line.slice('rename to '.length);\n current.path = current.newPath;\n continue;\n }\n if (line.startsWith('Binary files ')) {\n current.isBinary = true;\n continue;\n }\n if (line.startsWith('--- ')) {\n const p = stripPathPrefix(line.slice(4));\n if (p === '/dev/null') {\n current.status = 'added';\n } else {\n current.oldPath = p;\n }\n continue;\n }\n if (line.startsWith('+++ ')) {\n const p = stripPathPrefix(line.slice(4));\n if (p === '/dev/null') {\n current.status = 'deleted';\n } else {\n current.newPath = p;\n current.path = p;\n }\n continue;\n }\n\n const hunkMatch = line.match(HUNK_RE);\n if (hunkMatch) {\n currentHunk = {\n oldStart: Number(hunkMatch[1]),\n oldLines: hunkMatch[2] ? Number(hunkMatch[2]) : 1,\n newStart: Number(hunkMatch[3]),\n newLines: hunkMatch[4] ? Number(hunkMatch[4]) : 1,\n context: hunkMatch[5].trim(),\n lines: [],\n };\n oldLineNum = currentHunk.oldStart;\n newLineNum = currentHunk.newStart;\n current.hunks.push(currentHunk);\n continue;\n }\n\n if (!currentHunk) continue;\n\n // Inside a hunk: the first character of each line says what kind it is.\n // We accept empty lines defensively (some diffs trim trailing space).\n const marker = line[0] ?? ' ';\n const body = line.slice(1);\n\n if (marker === '\\\\') {\n // \"\\" — attach to previous line, don't count.\n currentHunk.lines.push({\n kind: 'no-newline',\n content: line,\n oldNum: null,\n newNum: null,\n });\n continue;\n }\n\n if (marker === '+') {\n currentHunk.lines.push({\n kind: 'add',\n content: body,\n oldNum: null,\n newNum: newLineNum++,\n });\n current.added++;\n } else if (marker === '-') {\n currentHunk.lines.push({\n kind: 'delete',\n content: body,\n oldNum: oldLineNum++,\n newNum: null,\n });\n current.deleted++;\n } else {\n // Context (space) and anything else gets treated as context.\n currentHunk.lines.push({\n kind: 'context',\n content: body,\n oldNum: oldLineNum++,\n newNum: newLineNum++,\n });\n }\n }\n\n // Derive status for renames where the diff omitted /dev/null markers.\n for (const f of files) {\n if (f.status === 'modified' && f.oldPath !== f.newPath) {\n f.status = 'renamed';\n }\n }\n\n return files;\n}\n\nfunction stripPathPrefix(p: string): string {\n // `--- a/path/to/file` / `+++ b/path/to/file` / `--- /dev/null`\n const trimmed = p.trim().replace(/^\"|\"$/g, '');\n if (trimmed === '/dev/null') return trimmed;\n if (trimmed.startsWith('a/') || trimmed.startsWith('b/')) {\n return trimmed.slice(2);\n }\n return trimmed;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\n/**\n * Parse standard lcov.info content into a map of file path → line-coverage\n * percent (a float in [0, 100]). Keys are the `SF:` paths exactly as written\n * in the file (may be absolute or repo-relative depending on the tool).\n *\n * Per-file percent prefers the `LF`/`LH` summary lines (lines found / lines\n * hit) when present; otherwise it is derived from the individual `DA:` line\n * records (`DA:<line>,<hits>`). A file with zero lines found yields 0.\n */\nexport function parseLcov(content: string): Map<string, number> {\n const result = new Map<string, number>();\n\n let sf: string | null = null;\n let lf: number | null = null;\n let lh: number | null = null;\n let daTotal = 0;\n let daHit = 0;\n\n const finish = () => {\n if (sf === null) return;\n let pct: number;\n if (lf !== null && lf > 0) {\n pct = ((lh ?? 0) / lf) * 100;\n } else if (lf === 0) {\n pct = 0;\n } else if (daTotal > 0) {\n pct = (daHit / daTotal) * 100;\n } else {\n pct = 0;\n }\n result.set(sf, pct);\n };\n\n const reset = () => {\n sf = null;\n lf = null;\n lh = null;\n daTotal = 0;\n daHit = 0;\n };\n\n for (const raw of content.split('\\n')) {\n const line = raw.trim();\n if (line.startsWith('SF:')) {\n sf = line.slice(3).trim();\n } else if (line.startsWith('DA:')) {\n const rest = line.slice(3);\n const comma = rest.indexOf(',');\n if (comma !== -1) {\n const hits = Number(rest.slice(comma + 1).split(',')[0]);\n if (Number.isFinite(hits)) {\n daTotal += 1;\n if (hits > 0) daHit += 1;\n }\n }\n } else if (line.startsWith('LF:')) {\n const n = Number(line.slice(3).trim());\n if (Number.isFinite(n)) lf = n;\n } else if (line.startsWith('LH:')) {\n const n = Number(line.slice(3).trim());\n if (Number.isFinite(n)) lh = n;\n } else if (line === 'end_of_record') {\n finish();\n reset();\n }\n }\n // Tolerate a trailing record with no terminating `end_of_record`.\n finish();\n\n return result;\n}\n\n/**\n * Locate an lcov.info file for a repo root. Checks `<root>/coverage/lcov.info`\n * then `<root>/lcov.info`. Returns the first that exists, else null.\n */\nexport function findLcov(root: string): string | null {\n const candidates = [\n path.join(root, 'coverage', 'lcov.info'),\n path.join(root, 'lcov.info'),\n ];\n for (const c of candidates) {\n try {\n if (fs.statSync(c).isFile()) return c;\n } catch {\n // not present — try next\n }\n }\n return null;\n}\n\n/**\n * Cache of parsed lcov files keyed by absolute lcov path. `computeDiff` runs\n * on every SSE / chokidar tick; an lcov.info can be multiple MB, so we MUST\n * NOT re-read + re-parse it each refresh. The cache is invalidated when the\n * file's `mtimeMs` changes (a fresh `npm test -- --coverage` run rewrites it).\n */\ninterface LcovCacheEntry {\n mtimeMs: number;\n parsed: Map<string, number>;\n}\nconst lcovCache = new Map<string, LcovCacheEntry>();\n\n/**\n * Read + parse the lcov at `lcovPath`, memoizing by `(path, mtimeMs)`. Returns\n * the parsed `SF: → percent` map and the lcov's mtime (ms-since-epoch), or\n * null when the file can't be stat'd / read. A multi-MB lcov is parsed once\n * and reused until it is rewritten.\n *\n * Exported for testing the cache behavior.\n */\nexport function readParsedLcov(\n lcovPath: string,\n): { parsed: Map<string, number>; mtimeMs: number } | null {\n let mtimeMs: number;\n try {\n mtimeMs = fs.statSync(lcovPath).mtimeMs;\n } catch {\n lcovCache.delete(lcovPath);\n return null;\n }\n\n const cached = lcovCache.get(lcovPath);\n if (cached && cached.mtimeMs === mtimeMs) {\n return { parsed: cached.parsed, mtimeMs };\n }\n\n let content: string;\n try {\n content = fs.readFileSync(lcovPath, 'utf-8');\n } catch {\n lcovCache.delete(lcovPath);\n return null;\n }\n\n const parsed = parseLcov(content);\n lcovCache.set(lcovPath, { mtimeMs, parsed });\n return { parsed, mtimeMs };\n}\n\n/** Test hook: drop the in-memory parse cache. */\nexport function clearLcovCache(): void {\n lcovCache.clear();\n}\n\n/** Normalize a path for comparison: forward slashes, drop a leading `./`. */\nfunction normRel(p: string): string {\n return path.normalize(p).replace(/\\\\/g, '/').replace(/^\\.\\//, '');\n}\n\n/**\n * Canonicalize `root` once so SF:-path matching survives symlink / realpath\n * divergence (macOS `/tmp` → `/private/tmp`, Linux bind mounts, Windows\n * junctions) and case-insensitive filesystems. An absolute `SF:` path emitted\n * by the coverage tool is resolved through `realpath` too, so both sides share\n * a canonical namespace before `path.relative` runs — otherwise the relative\n * path comes out `..`-prefixed and the match is silently lost.\n */\nfunction realRoot(root: string): string {\n try {\n return fs.realpathSync(path.resolve(root));\n } catch {\n return path.resolve(root);\n }\n}\n\n/**\n * Canonicalize an absolute path by realpath-ing its deepest EXISTING ancestor\n * and re-appending the not-yet-existing tail. `fs.realpathSync(absSf)` throws\n * when the leaf doesn\\'t exist on disk (lcov records files that may since have\n * moved, or simply aren\\'t present in a sparse checkout); a plain\n * `path.resolve` fallback would leave the path in a different symlink\n * namespace from `canonRoot` (macOS `/var/folders` TMPDIR, etc.) and the\n * relative path would come out `..`-prefixed — silently dropping the match.\n * Resolving the existing prefix keeps both sides in one canonical namespace.\n */\nfunction canonicalize(absPath: string): string {\n const resolved = path.resolve(absPath);\n let dir = resolved;\n const tail: string[] = [];\n // Walk up until we hit a path that exists (realpath-able) or the root.\n for (;;) {\n try {\n const realDir = fs.realpathSync(dir);\n return tail.length ? path.join(realDir, ...tail.reverse()) : realDir;\n } catch {\n const parent = path.dirname(dir);\n if (parent === dir) return resolved; // reached fs root, nothing realpath-able\n tail.push(path.basename(dir));\n dir = parent;\n }\n }\n}\n\n/** Resolve an absolute `SF:` path to repo-relative against the canonical root,\n * following symlinks (even when the leaf file is absent) so realpath\n * divergence and case-insensitive filesystems don\\'t break the match. */\nfunction relForAbsSf(absSf: string, canonRoot: string): string {\n return path.relative(canonRoot, canonicalize(absSf));\n}\n\n/**\n * Result of a per-repo coverage lookup. `byPath` maps each requested\n * repo-relative path that matched to its line-coverage percent. `lcovMtimeMs`\n * is the mtime of the lcov.info the data came from (so callers can surface\n * staleness), or null when no lcov was found.\n */\nexport interface CoverageLookup {\n byPath: Map<string, number>;\n lcovMtimeMs: number | null;\n}\n\n/**\n * Read and parse the repo's lcov (if any) and return a map of repo-relative\n * path → line-coverage percent for the requested files only, plus the lcov\n * mtime. lcov `SF:` paths may be absolute or relative; both sides are\n * normalized to a canonical repo-relative path and compared. Entries are only\n * included on a confident path match.\n *\n * The underlying lcov parse is memoized by `(path, mtimeMs)` (see\n * `readParsedLcov`) so a multi-MB file isn't re-read on every diff refresh.\n */\nexport function coverageLookup(\n root: string,\n relPaths: string[],\n): CoverageLookup {\n const out = new Map<string, number>();\n const lcovPath = findLcov(root);\n if (!lcovPath) return { byPath: out, lcovMtimeMs: null };\n\n const read = readParsedLcov(lcovPath);\n if (!read || read.parsed.size === 0) {\n return { byPath: out, lcovMtimeMs: read?.mtimeMs ?? null };\n }\n\n // Build a lookup keyed by normalized repo-relative SF path.\n const canonRoot = realRoot(root);\n const byRel = new Map<string, number>();\n for (const [sf, pct] of read.parsed) {\n const rel = path.isAbsolute(sf) ? relForAbsSf(sf, canonRoot) : sf;\n byRel.set(normRel(rel), pct);\n }\n\n for (const rp of relPaths) {\n const pct = byRel.get(normRel(rp));\n if (typeof pct === 'number') out.set(rp, pct);\n }\n\n return { byPath: out, lcovMtimeMs: read.mtimeMs };\n}\n\n/**\n * Back-compat thin wrapper: returns just the `repo-relative path → percent`\n * map (drops the lcov mtime). Prefer `coverageLookup` when you need staleness\n * information.\n */\nexport function coverageForFiles(\n root: string,\n relPaths: string[],\n): Map<string, number> {\n return coverageLookup(root, relPaths).byPath;\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport crypto from 'node:crypto';\nimport spawn from 'cross-spawn';\n\nexport interface TempTreeResult {\n /** Tree sha written from the temp index. */\n treeSha: string;\n /** HEAD commit sha, or null when the repo has no commits yet. Callers that\n * build a commit from `treeSha` use this as the parent. */\n headSha: string | null;\n}\n\n/**\n * Build a git tree sha for a repo WITHOUT disturbing the real index, using a\n * throwaway `GIT_INDEX_FILE`. Shared by `checkpoint.ts` (which commits the\n * tree and pins it behind a ref) and `diff-pipeline.ts` (which diffs the tree\n * against a checkpoint commit) — both previously hand-rolled this same\n * read-tree / add / write-tree dance.\n *\n * GIT_INDEX_FILE=<tmp> git read-tree HEAD (when HEAD exists)\n * GIT_INDEX_FILE=<tmp> git add -A (when includeWorkingTree)\n * GIT_INDEX_FILE=<tmp> git write-tree → treeSha\n *\n * With `includeWorkingTree` (default), `add -A` promotes every working-tree\n * change including untracked files (honoring `.gitignore`), so the tree is a\n * full snapshot of the working tree. Without it, the tree is HEAD's tree\n * verbatim (the empty tree on a repo with no commits) — used for the\n * \"Initial\" checkpoint baseline so a diff of Initial→working reproduces the\n * full uncommitted diff instead of hiding pre-existing changes.\n *\n * Returns null on any git failure (caller should skip this repo). The temp\n * index file is always unlinked.\n */\nexport function writeTempTree(\n repoRoot: string,\n opts: { includeWorkingTree?: boolean } = {},\n): TempTreeResult | null {\n const includeWorkingTree = opts.includeWorkingTree ?? true;\n const tmpIndex = path.join(\n os.tmpdir(),\n `wd-tree-${process.pid}-${crypto.randomBytes(6).toString('hex')}.idx`,\n );\n const env: NodeJS.ProcessEnv = { ...process.env, GIT_INDEX_FILE: tmpIndex };\n const run = (args: string[]) =>\n spawn.sync('git', args, {\n cwd: repoRoot,\n encoding: 'utf-8',\n env,\n windowsHide: true,\n maxBuffer: 64 * 1024 * 1024,\n });\n\n try {\n const headSha =\n (\n spawn.sync('git', ['rev-parse', '--verify', 'HEAD'], {\n cwd: repoRoot,\n encoding: 'utf-8',\n windowsHide: true,\n }).stdout ?? ''\n ).trim() || null;\n\n if (headSha) {\n const r = run(['read-tree', 'HEAD']);\n if (r.status !== 0) return null;\n }\n\n if (includeWorkingTree) {\n // `-A` against a temp index seeded from HEAD (or empty) captures the\n // full working-tree state, subject to .gitignore.\n const add = run(['add', '-A']);\n if (add.status !== 0) return null;\n }\n\n const wt = run(['write-tree']);\n if (wt.status !== 0 || !wt.stdout) return null;\n return { treeSha: wt.stdout.trim(), headSha };\n } finally {\n try {\n if (fs.existsSync(tmpIndex)) fs.unlinkSync(tmpIndex);\n } catch {\n // Temp-file leftover isn't fatal — the OS cleans it eventually.\n }\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\nimport crypto from 'node:crypto';\n\nexport interface RepoSpec {\n /** Display name (becomes tab label / repo slug). */\n name: string;\n /** Git working tree root for this repo. */\n root: string;\n /** Argument to `git diff` (sha or ref like HEAD). */\n diffArg: string;\n}\n\n/** The 12-char scope hash for a set of repo roots. One root for a single\n * repo, all roots for a group. Sorting makes it order-independent, so the\n * same directory set always maps to the same scope across processes — this\n * is the single source of truth shared by `stableDiffPath`,\n * `scope-manager`, and the comment-delivery bridge in `pending-delivery`. */\nexport function scopeHashFor(keyPaths: string[]): string {\n const key = keyPaths.slice().sort().join('|');\n return crypto.createHash('sha1').update(key).digest('hex').slice(0, 12);\n}\n\n/** Stable per-scope path stem under ~/.work/diffs/. Pass one root for a\n * single repo, all roots (sorted) for a group. The hash gives each scope\n * its own stem; callers append `.pid`, `.url`, `.log` as needed. */\nexport function stableDiffPath(keyPaths: string[]): string {\n const dir = path.join(os.homedir(), '.work', 'diffs');\n fs.mkdirSync(dir, { recursive: true });\n return path.join(dir, scopeHashFor(keyPaths));\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport { git } from './git.js';\nimport { loadHistory, type WorktreeSession } from './history.js';\nimport type { RepoSpec } from './repo-spec.js';\n\nexport interface DiffScope {\n isGroup: boolean;\n session: WorktreeSession | null;\n /** Repos to diff (1 for single, N for group). */\n repos: { name: string; root: string }[];\n /** Name of the repo whose subtree the user is in (initial active tab). */\n activeRepoName: string | null;\n}\n\nexport interface ResolvedBase {\n base: string;\n source: 'arg' | 'session' | 'auto-detected' | 'default';\n}\n\nfunction normPath(p: string): string {\n return path.resolve(p).replace(/\\\\/g, '/').toLowerCase();\n}\n\n/**\n * Like {@link normPath} but resolves symlinks. `git rev-parse --show-toplevel`\n * returns the realpath (e.g. macOS `/var` → `/private/var`), so comparing it\n * to a stored session path must go through realpath on both sides. Falls back\n * to {@link normPath} for paths that don't exist (e.g. unit-test fixtures).\n */\nfunction realNorm(p: string): string {\n try {\n return normPath(fs.realpathSync(p));\n } catch {\n return normPath(p);\n }\n}\n\n/**\n * Resolve what scope to diff based on cwd. Handles single-repo worktrees,\n * group worktrees (cwd at group root or anywhere inside a sub-repo), and\n * \"random\" git repos not managed by `work`.\n */\nexport function resolveScope(cwd: string): DiffScope | null {\n const normCwd = normPath(cwd);\n const sessions = loadHistory();\n\n // cwd's own git worktree root. Used to reject session-path matches that are\n // really a *parent* repo: linked worktrees often live physically inside\n // another repo (e.g. `<repo>/.claude/worktrees/<branch>`), so a naive prefix\n // match collapses every nested worktree onto the parent's scope — they'd\n // share one daemon and show each other's diff.\n const top = git(['rev-parse', '--show-toplevel'], cwd);\n const toplevel = top.exitCode === 0 && top.stdout ? top.stdout : null;\n const realTop = toplevel ? realNorm(toplevel) : null;\n\n // 1. cwd is at or inside one of a session's repo paths.\n for (const s of sessions) {\n for (const p of s.paths) {\n const np = normPath(p);\n if (normCwd === np || normCwd.startsWith(np + '/')) {\n // Only honour the match if this session path is cwd's actual worktree\n // root. When cwd is in a nested worktree, its toplevel is deeper than\n // `np` — skip so we resolve the real (nested) worktree below.\n if (realTop && realNorm(p) !== realTop) continue;\n if (s.isGroup) {\n return {\n isGroup: true,\n session: s,\n repos: s.paths.map((rp) => ({ name: path.basename(rp), root: rp })),\n activeRepoName: path.basename(p),\n };\n }\n return {\n isGroup: false,\n session: s,\n repos: [{ name: path.basename(p), root: p }],\n activeRepoName: path.basename(p),\n };\n }\n }\n }\n\n // 2. cwd is at the group root (parent of all of a group's repo paths).\n for (const s of sessions) {\n if (!s.isGroup || s.paths.length === 0) continue;\n const parents = s.paths.map((p) => normPath(path.dirname(p)));\n const groupRoot = parents[0];\n if (!parents.every((par) => par === groupRoot)) continue;\n if (normCwd === groupRoot || normCwd.startsWith(groupRoot + '/')) {\n return {\n isGroup: true,\n session: s,\n repos: s.paths.map((rp) => ({ name: path.basename(rp), root: rp })),\n activeRepoName: null,\n };\n }\n }\n\n // 3. Fall back to git rev-parse for repos not managed by `work`.\n if (!toplevel) return null;\n return {\n isGroup: false,\n session: null,\n repos: [{ name: path.basename(toplevel), root: toplevel }],\n activeRepoName: path.basename(toplevel),\n };\n}\n\n/**\n * Find any plausible parent branch — same candidate list as\n * `detectParentBranch`, but DOESN'T require the parent to have commits\n * past HEAD. Returns the candidate with the most-recent merge-base (or\n * null only if no candidates exist at all).\n *\n * Used by the static renderer and the diff server's `?base=branch`\n * route to decide whether to offer the \"Since branch\" tab. `detectParentBranch`\n * skips parents where merge-base == HEAD (correct for the CLI's \"show\n * me what I added past parent\" semantic — empty diff), but the toggle\n * should still appear in that case so the user can confirm \"no, there's\n * nothing committed yet\".\n */\nexport function findAnyParentBranch(cwd: string): string | null {\n return findParent(cwd, false);\n}\n\n/** Walk candidate base branches and pick the one with the most recent merge-base. */\nexport function detectParentBranch(cwd: string): string | null {\n return findParent(cwd, true);\n}\n\nfunction findParent(cwd: string, requireAheadOfParent: boolean): string | null {\n const currentResult = git(['rev-parse', '--abbrev-ref', 'HEAD'], cwd);\n const currentBranch = currentResult.exitCode === 0 ? currentResult.stdout : '';\n\n const candidates = ['main', 'master', 'dev', 'develop'].flatMap((name) => [\n name,\n `origin/${name}`,\n ]);\n\n let best: { ref: string; sha: string; time: number } | null = null;\n\n for (const ref of candidates) {\n if (ref === currentBranch) continue;\n const exists = git(['rev-parse', '--verify', '--quiet', ref], cwd);\n if (exists.exitCode !== 0 || !exists.stdout) continue;\n\n const mb = git(['merge-base', ref, 'HEAD'], cwd);\n if (mb.exitCode !== 0 || !mb.stdout) continue;\n\n // Skip parents where the merge-base IS HEAD only when the caller\n // asked us to (CLI \"show me what I added\" semantics). When called\n // for the toggle UI, we want to surface the parent even though the\n // diff will be empty.\n if (requireAheadOfParent) {\n const headSha = git(['rev-parse', 'HEAD'], cwd).stdout;\n if (mb.stdout === headSha) continue;\n }\n\n const timeResult = git(['show', '-s', '--format=%ct', mb.stdout], cwd);\n if (timeResult.exitCode !== 0) continue;\n const time = Number(timeResult.stdout);\n if (!Number.isFinite(time)) continue;\n\n if (!best || time > best.time) {\n best = { ref, sha: mb.stdout, time };\n }\n }\n\n return best?.ref ?? null;\n}\n\nexport function resolveBase(\n scope: DiffScope,\n argv: { base?: string; branch?: boolean },\n): ResolvedBase {\n if (argv.base) return { base: argv.base, source: 'arg' };\n\n if (argv.branch) {\n if (scope.session?.baseBranch) {\n return { base: scope.session.baseBranch, source: 'session' };\n }\n const primaryRoot =\n scope.repos.find((r) => r.name === scope.activeRepoName)?.root ??\n scope.repos[0].root;\n const detected = detectParentBranch(primaryRoot);\n if (detected) return { base: detected, source: 'auto-detected' };\n\n console.error(\n chalk.red('Could not determine a parent branch for this worktree.'),\n );\n console.error(chalk.gray('Pass one explicitly: diff <ref>'));\n process.exit(1);\n }\n\n return { base: 'HEAD', source: 'default' };\n}\n\n/**\n * Compute per-repo merge-base when comparing against a non-HEAD ref.\n *\n * `perRepoBase` (keyed by repo root) overrides `base` for individual repos —\n * used by group worktrees forked with different bases per repo\n * (`work tree --base backend=dev --base frontend=feat/x`). A repo without an\n * override falls back to the shared `base`.\n */\nexport function buildRepoSpecs(\n scope: DiffScope,\n base: string,\n perRepoBase?: Record<string, string>,\n): RepoSpec[] {\n return scope.repos.map((r) => {\n const repoBase = perRepoBase?.[r.root] ?? base;\n let diffArg = repoBase;\n if (repoBase !== 'HEAD') {\n const mb = git(['merge-base', repoBase, 'HEAD'], r.root);\n if (mb.exitCode === 0 && mb.stdout) diffArg = mb.stdout;\n }\n return { name: r.name, root: r.root, diffArg };\n });\n}\n\nexport interface ResolvedRepoDiff {\n /** The git ref the diff was computed against. `HEAD` for uncommitted,\n * the resolved parent (e.g. `origin/main`) for branch mode. */\n resolvedBase: string;\n /** The actual argument passed to `git diff` — typically the merge-base\n * sha for branch mode, or `HEAD` for uncommitted. */\n diffArg: string;\n}\n\n/**\n * Resolve the diff target for a single repo. Centralises the\n * \"find-parent → merge-base → fall back to HEAD\" dance that previously\n * lived in three places (scope-routes, web-server, the static renderer).\n *\n * For `base === 'uncommitted'` this is trivially `HEAD`. For\n * `base === 'branch'` it prefers an explicit `sessionBaseBranch` (the\n * value the user recorded with `work tree --base`), falling back to\n * `findAnyParentBranch` (so the toggle stays available even when the\n * branch has no commits past parent yet — diff just renders empty).\n */\nexport function resolveRepoDiff(\n root: string,\n base: 'uncommitted' | 'branch',\n sessionBaseBranch?: string | null,\n): ResolvedRepoDiff {\n if (base === 'uncommitted') {\n return { resolvedBase: 'HEAD', diffArg: 'HEAD' };\n }\n // Prefer a parent we're actually *ahead of* (`detectParentBranch` skips\n // candidates whose merge-base is HEAD, e.g. a `dev` that already contains\n // this branch). `findAnyParentBranch` would pick the most-recent merge-base\n // even when it's HEAD — yielding a useless 0-commit \"since branch\" diff.\n // Fall back to it only so the toggle still resolves to *something*.\n const parent =\n sessionBaseBranch ?? detectParentBranch(root) ?? findAnyParentBranch(root);\n if (!parent) return { resolvedBase: 'HEAD', diffArg: 'HEAD' };\n let diffArg = 'HEAD';\n const mb = git(['merge-base', parent, 'HEAD'], root);\n if (mb.exitCode === 0 && mb.stdout) diffArg = mb.stdout;\n return { resolvedBase: parent, diffArg };\n}\n\n/**\n * Look up the recorded fork point for a worktree path from session history.\n * Prefers the per-repo base (`baseBranches`) for that exact path, falling\n * back to the session's representative `baseBranch`. Returns undefined when\n * the path isn't a known worktree — callers then auto-detect.\n *\n * Used by the scope diff route, which only carries repo paths (no session),\n * so a `wd` forked with `--base feat/x` still diffs against `feat/x` even\n * though auto-detection wouldn't find a non-mainline parent.\n */\nexport function sessionBaseForPath(root: string): string | undefined {\n const norm = normPath(root);\n for (const s of loadHistory()) {\n for (const p of s.paths) {\n if (normPath(p) === norm) {\n return s.baseBranches?.[p] ?? s.baseBranch;\n }\n }\n }\n return undefined;\n}\n","import { Hono } from 'hono';\nimport { zValidator } from '@hono/zod-validator';\nimport { createCommentStore } from './comment-store.js';\nimport {\n startDiffServer,\n type DiffServerApi,\n type DiffServerHandle,\n} from './diff-server.js';\nimport type { RepoSpec } from './repo-spec.js';\nimport type { Comment } from './comment-types.js';\nimport { commentInputSchema, submitReviewSchema } from './comment-schemas.js';\n\nexport type {\n Comment,\n CommentAuthor,\n CommentSide,\n CommentStatus,\n} from './comment-types.js';\n\nexport interface CommentServerOptions {\n repos: RepoSpec[];\n scopeLabel: string;\n /** Session baseBranch — threaded to the diff server so `?base=branch`\n * honours the user's recorded base instead of auto-detecting. */\n sessionBaseBranch?: string;\n /** Per-repo fork points keyed by repo root (group worktrees forked with\n * different bases per repo). Overrides `sessionBaseBranch` per repo. */\n sessionBaseBranches?: Record<string, string>;\n onComment?: (comment: Comment) => void;\n onCommentDeleted?: (id: string) => void;\n onSubmitReviewStart?: (info: { count: number; summary: Comment | null }) => void;\n onSubmitReviewEnd?: () => void;\n watchDebounceMs?: number;\n}\n\nexport interface CommentServerHandle {\n url: string;\n waitForDone(): Promise<Comment[]>;\n snapshot(): Comment[];\n stop(): Promise<void>;\n}\n\nexport async function startCommentServer(\n opts: CommentServerOptions,\n): Promise<CommentServerHandle> {\n const store = createCommentStore();\n let resolveDone: ((comments: Comment[]) => void) | null = null;\n const donePromise = new Promise<Comment[]>((resolve) => {\n resolveDone = resolve;\n });\n\n function attachRoutes(api: DiffServerApi): void {\n const routes = new Hono();\n\n routes.get('/api/comments', (c) => c.json({ comments: store.snapshot() }));\n\n routes.post(\n '/api/comments',\n zValidator('json', commentInputSchema),\n (c) => {\n try {\n const input = c.req.valid('json');\n const comment = store.post(input);\n if (\n comment.status === 'published' &&\n comment.author === 'user' &&\n opts.onComment\n ) {\n opts.onComment(comment);\n }\n if (comment.author === 'claude') {\n api.broadcast('comments-changed', { id: comment.id });\n }\n return c.json({ comment, comments: store.snapshot() });\n } catch (err) {\n return c.json({ error: (err as Error).message }, 400);\n }\n },\n );\n\n routes.delete('/api/comments/:id', (c) => {\n const id = c.req.param('id');\n const removed = store.remove(id);\n if (removed && opts.onCommentDeleted) opts.onCommentDeleted(id);\n return c.json({ comments: store.snapshot() });\n });\n\n routes.post(\n '/api/submit-review',\n zValidator('json', submitReviewSchema),\n (c) => {\n const body = c.req.valid('json');\n const result = store.submit(body.summary);\n if (opts.onSubmitReviewStart) {\n opts.onSubmitReviewStart({\n count: result.drafts.length,\n summary: result.summary,\n });\n }\n if (result.summary && opts.onComment) opts.onComment(result.summary);\n if (opts.onComment) {\n for (const d of result.drafts) opts.onComment(d);\n }\n if (opts.onSubmitReviewEnd) opts.onSubmitReviewEnd();\n return c.json({\n count: result.drafts.length,\n comments: store.snapshot(),\n });\n },\n );\n\n routes.post('/api/discard-review', (c) => {\n const discarded = store.discardDrafts();\n return c.json({ discarded, comments: store.snapshot() });\n });\n\n routes.post('/api/done', (c) => {\n const out = c.json({ ok: true, count: store.list().length });\n if (resolveDone) {\n resolveDone(store.snapshot());\n resolveDone = null;\n }\n return out;\n });\n\n api.route('/', routes);\n }\n\n const server: DiffServerHandle = await startDiffServer({\n repos: opts.repos,\n scopeLabel: opts.scopeLabel,\n sessionBaseBranch: opts.sessionBaseBranch,\n sessionBaseBranches: opts.sessionBaseBranches,\n watchDebounceMs: opts.watchDebounceMs,\n attachRoutes,\n });\n\n return {\n url: server.url,\n waitForDone: () => donePromise,\n snapshot: () => store.snapshot(),\n stop: () => server.stop(),\n };\n}\n\n/** Read-only variant: diff + watch + SSE only. Used by `wd` / `wd --watch`. */\nexport async function startReadOnlyDiffServer(opts: {\n repos: RepoSpec[];\n scopeLabel: string;\n sessionBaseBranch?: string;\n watchDebounceMs?: number;\n}): Promise<{ url: string; stop: () => Promise<void> }> {\n const server = await startDiffServer({\n repos: opts.repos,\n scopeLabel: opts.scopeLabel,\n sessionBaseBranch: opts.sessionBaseBranch,\n watchDebounceMs: opts.watchDebounceMs,\n readOnly: true,\n });\n return { url: server.url, stop: () => server.stop() };\n}\n\n/** Format a single comment as a markdown chunk for stdout. */\nexport function formatSingleComment(c: Comment): string {\n const bodyLines = c.body.split('\\n').map((l) => `> ${l}`).join('\\n');\n const header =\n c.side === 'general'\n ? `**General review comment**`\n : `**${c.repo}/${c.file}** : line ${c.line} (${c.side})`;\n const meta: string[] = [];\n if (c.author === 'claude') meta.push('author: claude');\n if (c.parentId) meta.push(`reply-to: ${c.parentId}`);\n meta.push(`id: ${c.id}`);\n return [`--- comment ---`, header, meta.join(' · '), bodyLines, ``].join('\\n');\n}\n","import crypto from 'node:crypto';\nimport type {\n Comment,\n CommentAuthor,\n CommentSide,\n CommentStatus,\n} from './comment-types.js';\n\nexport interface CommentInput {\n repo?: string;\n file?: string;\n line?: number;\n side?: CommentSide;\n body: string;\n status?: CommentStatus;\n lineContent?: string;\n parentId?: string;\n author?: CommentAuthor;\n}\n\nexport interface SubmitInfo {\n /** Number of drafts promoted to published, in chronological order. */\n drafts: Comment[];\n /** Optional summary comment created from `summary` text. */\n summary: Comment | null;\n}\n\n/**\n * In-memory comment store. Pure model — no I/O, no HTTP, no SSE. The server\n * wires hooks (`onPost`, `onSubmit`, etc.) so its routes can stream events\n * to clients and stdout without the store knowing how delivery happens.\n */\nexport interface CommentStore {\n list(): Comment[];\n /** Add a comment. Throws if parentId is given but not found. Returns the\n * newly-created comment. Does NOT fire onPost — the caller decides\n * whether to stream this immediately (published+user) or hold it\n * (drafts, claude echoes). */\n post(input: CommentInput): Comment;\n /** Remove a comment by id. Returns true if anything was removed. */\n remove(id: string): boolean;\n /** Promote all drafts to published, optionally creating a summary comment\n * from `summary` text. Returns the batch info. */\n submit(summary: string | undefined): SubmitInfo;\n /** Drop every draft comment. Returns the number removed. */\n discardDrafts(): number;\n /** Snapshot — a defensive copy. */\n snapshot(): Comment[];\n}\n\nexport function createCommentStore(): CommentStore {\n const comments: Comment[] = [];\n\n function findParent(parentId: string | undefined): Comment | undefined {\n if (typeof parentId !== 'string') return undefined;\n const parent = comments.find((c) => c.id === parentId);\n if (!parent) throw new Error('parent comment not found');\n return parent;\n }\n\n return {\n list: () => comments,\n snapshot: () => [...comments],\n\n post(input) {\n if (typeof input.body !== 'string' || !input.body.trim()) {\n throw new Error('comment body is required');\n }\n const parent = findParent(input.parentId);\n const side = input.side ?? parent?.side ?? 'general';\n if (side !== 'left' && side !== 'right' && side !== 'general') {\n throw new Error('invalid side');\n }\n const c: Comment = {\n id: crypto.randomBytes(8).toString('hex'),\n repo:\n (typeof input.repo === 'string' ? input.repo : parent?.repo) ?? '',\n file:\n (typeof input.file === 'string' ? input.file : parent?.file) ?? '',\n line: typeof input.line === 'number' ? input.line : (parent?.line ?? 0),\n side,\n body: input.body.trim(),\n createdAt: new Date().toISOString(),\n lineContent:\n typeof input.lineContent === 'string'\n ? input.lineContent\n : parent?.lineContent,\n author: input.author === 'claude' ? 'claude' : 'user',\n parentId: parent?.id,\n status: input.status === 'draft' ? 'draft' : 'published',\n };\n comments.push(c);\n return c;\n },\n\n remove(id) {\n const idx = comments.findIndex((c) => c.id === id);\n if (idx < 0) return false;\n comments.splice(idx, 1);\n return true;\n },\n\n submit(summary) {\n const drafts = comments\n .filter((c) => c.status === 'draft')\n .sort((a, b) => a.createdAt.localeCompare(b.createdAt));\n let summaryComment: Comment | null = null;\n if (typeof summary === 'string' && summary.trim()) {\n summaryComment = {\n id: crypto.randomBytes(8).toString('hex'),\n repo: '',\n file: '',\n line: 0,\n side: 'general',\n body: summary.trim(),\n createdAt: new Date().toISOString(),\n author: 'user',\n status: 'published',\n };\n comments.push(summaryComment);\n }\n for (const d of drafts) {\n d.status = 'published';\n }\n return { drafts, summary: summaryComment };\n },\n\n discardDrafts() {\n const before = comments.length;\n for (let i = comments.length - 1; i >= 0; i--) {\n if (comments[i].status === 'draft') comments.splice(i, 1);\n }\n return before - comments.length;\n },\n };\n}\n","import chalk from 'chalk';\nimport { Hono, type Context } from 'hono';\nimport { serve, type ServerType } from '@hono/node-server';\nimport { streamSSE } from 'hono/streaming';\nimport { computeDiff } from './diff-pipeline.js';\nimport { readContextLines } from './file-context.js';\nimport { createFsWatcher } from './fs-watcher.js';\nimport { resolveRepoDiff } from './diff-scope.js';\nimport { git } from './git.js';\nimport { resolveWebRoot } from './web-static.js';\nimport { serveSpa } from './spa-handler.js';\nimport type { RepoSpec } from './repo-spec.js';\n\nexport interface DiffServerOptions {\n /** Repos this server scopes to. */\n repos: RepoSpec[];\n /** Short human-readable label for /api/context. */\n scopeLabel: string;\n /** Optional session baseBranch (the value captured at `work tree --base`).\n * When set, the `?base=branch` route uses it instead of auto-detection,\n * so the diff matches what the user actually declared. */\n sessionBaseBranch?: string;\n /** Per-repo fork points keyed by repo root. Overrides `sessionBaseBranch`\n * for individual repos in a group forked with different bases per repo. */\n sessionBaseBranches?: Record<string, string>;\n /** Debounce for fs.watch events before broadcasting diff-changed. */\n watchDebounceMs?: number;\n /** When true, /api/context advertises readOnly=true and the SPA hides\n * the comment UI. Default false. */\n readOnly?: boolean;\n /** Called once the base app is built so feature apps (e.g. comments) can\n * mount themselves and emit events via the SSE hub. */\n attachRoutes?: (api: DiffServerApi) => void;\n}\n\nexport interface DiffServerHandle {\n url: string;\n port: number;\n stop(): Promise<void>;\n}\n\n/** A typed SSE event broadcast to every connected client. */\nexport interface SseEvent {\n event: string;\n data: unknown;\n}\n\nexport interface DiffServerApi {\n /** Mount a Hono sub-app at the given path prefix. */\n route(prefix: string, app: Hono): void;\n /** Broadcast on the shared SSE hub. Fan-out is one-shot per client. */\n broadcast(event: string, data: unknown): void;\n}\n\n/**\n * The shared local HTTP server used by both `wd` (read-only) and `wd -c`\n * (comments). Owns the fs watcher, the SSE hub, and the three routes every\n * mode needs: GET /api/context, GET /api/diff, GET /events. Comment routes\n * register themselves via `attachRoutes`.\n */\nexport async function startDiffServer(\n opts: DiffServerOptions,\n): Promise<DiffServerHandle> {\n const webRoot = resolveWebRoot();\n if (!webRoot) {\n throw new Error('Could not find dist/web/. Run `npm run build` first.');\n }\n\n const sseListeners = new Set<(e: SseEvent) => void>();\n const watcher = createFsWatcher({\n roots: opts.repos.map((r) => r.root),\n debounceMs: opts.watchDebounceMs,\n onChange: () => broadcast('diff-changed', { ts: Date.now() }),\n });\n\n function broadcast(event: string, data: unknown): void {\n const payload: SseEvent = { event, data };\n for (const cb of sseListeners) cb(payload);\n }\n\n const app = new Hono();\n // No CORS headers and no OPTIONS preflight handler — the SPA is served\n // same-origin so its requests don't trigger preflight. The Host guard in\n // `launch` rejects cross-origin requests before they reach any handler.\n\n app.get('/api/context', (c) => {\n // Current branch of the primary repo, for the \"<branch> vs <base>\" title.\n const primaryRoot = opts.repos[0]?.root;\n let headBranch: string | undefined;\n if (primaryRoot) {\n const head = git(['rev-parse', '--abbrev-ref', 'HEAD'], primaryRoot);\n if (head.exitCode === 0 && head.stdout && head.stdout !== 'HEAD') {\n headBranch = head.stdout;\n }\n }\n return c.json({\n mode: 'review',\n scopeLabel: opts.scopeLabel,\n repos: opts.repos.map((r) => ({ name: r.name })),\n readOnly: !!opts.readOnly,\n headBranch,\n });\n });\n\n app.get('/api/diff', (c) => {\n const base = c.req.query('base') === 'branch' ? 'branch' : 'uncommitted';\n try {\n // For 'uncommitted' we honour the configured `diffArg` (typically\n // HEAD) so single-repo callers preserve whatever ref they\n // configured. For 'branch' we delegate to `resolveRepoDiff` per\n // repo — each one resolves its own parent and merge-base. The\n // top-level `resolvedBase` is the primary repo's value (matches\n // scope-routes and web-server behavior for the badge label).\n const resolved = opts.repos.map((r) =>\n base === 'uncommitted'\n ? { resolvedBase: 'HEAD', diffArg: r.diffArg }\n : resolveRepoDiff(\n r.root,\n 'branch',\n opts.sessionBaseBranches?.[r.root] ?? opts.sessionBaseBranch,\n ),\n );\n const repos = opts.repos.map((r, i) => ({\n name: r.name,\n root: r.root,\n resolvedBase: resolved[i].resolvedBase,\n files: computeDiff({ root: r.root, diffArg: resolved[i].diffArg }),\n }));\n const resolvedBase = resolved[0]?.resolvedBase ?? 'HEAD';\n return c.json({ repos, base, resolvedBase });\n } catch (err) {\n return c.json({ error: (err as Error).message }, 500);\n }\n });\n\n // Reveal unchanged context lines around a hunk (\"expand lines\"). Maps the\n // SPA's repo name back to its RepoSpec root; single-repo scopes (the\n // common standalone case) skip the lookup.\n app.get('/api/file-lines', (c) => {\n const repoName = c.req.query('repo') ?? '';\n const relPath = c.req.query('path') ?? '';\n const start = Number(c.req.query('start'));\n const end = Number(c.req.query('end'));\n const ref = c.req.query('ref') || undefined;\n if (!relPath || !Number.isInteger(start) || !Number.isInteger(end)) {\n return c.json({ error: 'bad path/start/end' }, 400);\n }\n const root =\n opts.repos.length === 1\n ? opts.repos[0].root\n : opts.repos.find((r) => r.name === repoName)?.root;\n if (!root) return c.json({ error: 'unknown repo' }, 404);\n const result = readContextLines({ root, relPath, start, end, ref });\n if (!result) return c.json({ error: 'cannot read file' }, 400);\n return c.json(result);\n });\n\n app.get('/events', (c) =>\n streamSSE(c, async (stream) => {\n const listener = (e: SseEvent) => {\n stream\n .writeSSE({ event: e.event, data: JSON.stringify(e.data) })\n .catch(() => { /* client gone */ });\n };\n sseListeners.add(listener);\n await stream.writeSSE({ event: 'connected', data: '' });\n // Keep the stream open until the client disconnects.\n await new Promise<void>((resolve) => {\n stream.onAbort(() => {\n sseListeners.delete(listener);\n resolve();\n });\n });\n }),\n );\n\n // attachRoutes can mount Hono sub-apps with their own routes.\n if (opts.attachRoutes) {\n opts.attachRoutes({\n route: (prefix, sub) => app.route(prefix, sub),\n broadcast,\n });\n }\n\n // SPA fallback for the bundled web assets.\n app.get('*', (c: Context) => serveSpa(c, webRoot));\n\n // Wrap the handle so server shutdown also tears down our chokidar watcher.\n // Without this the watcher keeps Node's event loop open after `stop()` —\n // a real test/teardown leak (vitest hangs after a suite that exercises\n // the server) and a wasteful long-running ghost in dev.\n const handle = await launch(app);\n const baseStop = handle.stop;\n return {\n ...handle,\n stop: async () => {\n try { watcher.stop(); } catch { /* */ }\n await baseStop();\n },\n };\n}\n\n/** Start a Hono app on a random port; resolve when it's listening. The\n * raw Node server is exposed via `httpServer` so callers can attach\n * WebSocket upgrade handlers (the terminal bridge).\n *\n * Installs a Host-header guard so DNS-rebinding attacks can't trick a\n * browser into POSTing to our local server from an attacker-controlled\n * origin: only `127.0.0.1:<port>` and `localhost:<port>` are accepted.\n * The SPA is served same-origin so legitimate requests always carry one\n * of those Host headers.\n */\nexport function launch(app: Hono): Promise<DiffServerHandle & { httpServer: ServerType }> {\n return new Promise((resolve) => {\n // Captured before serve() resolves; first guarded request runs after\n // this is set because serve() doesn't accept connections until it's\n // bound, and bind precedes the info-callback.\n let listenPort = 0;\n const guard = new Hono();\n guard.use('*', async (c, next) => {\n const host = c.req.header('host');\n if (\n host !== `127.0.0.1:${listenPort}` &&\n host !== `localhost:${listenPort}`\n ) {\n return c.text('Forbidden', 403);\n }\n await next();\n });\n guard.route('/', app);\n\n const server: ServerType = serve(\n { fetch: guard.fetch, port: 0, hostname: '127.0.0.1' },\n (info) => {\n listenPort = info.port;\n const url = `http://127.0.0.1:${info.port}/`;\n process.stderr.write(chalk.gray(`[server] listening at ${url}\\n`));\n resolve({\n url,\n port: info.port,\n httpServer: server,\n stop: () =>\n new Promise<void>((res) => {\n server.close(() => res());\n }),\n });\n },\n );\n });\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport spawn from 'cross-spawn';\nimport { isInsideRoot } from './diff-pipeline.js';\n\nexport interface ContextLinesOptions {\n /** Git toplevel (repo root) the file lives under. */\n root: string;\n /** Repo-relative path of the file. */\n relPath: string;\n /** 1-based inclusive first line to return. */\n start: number;\n /** 1-based inclusive last line to return. */\n end: number;\n /** When omitted or `'working'`, read the working-tree file. Otherwise the\n * content comes from `git show <ref>:<relPath>` — used when the diff's\n * \"new\" side is a committed snapshot (checkpoint range) rather than the\n * live tree. */\n ref?: string;\n}\n\nexport interface ContextLinesResult {\n /** The requested slice. Shorter than `end - start + 1` when the file ends\n * before `end`. */\n lines: string[];\n /** Echoed 1-based first line of `lines` (clamped to >= 1). */\n start: number;\n /** Total line count of the file at this ref. */\n totalLines: number;\n /** True when `end` reached (or passed) the last line — there is nothing\n * further below to expand. */\n eof: boolean;\n}\n\n/**\n * Read a contiguous range of lines from a file so the diff viewer can\n * reveal the unchanged context around hunks (\"expand lines\" in GitHub's\n * UI). The expandable region is, by definition, identical on both diff\n * sides, so a single read of the appropriate side is enough — the caller\n * maps the old line numbers via the constant gap offset.\n *\n * Path safety mirrors `readMarkdownContent` in diff-pipeline: the textual\n * `relPath` is rejected if it escapes `root`, and the working-tree read is\n * symlink-canonicalised so a `notes.md -> /etc/passwd` symlink can't\n * exfiltrate an out-of-tree file into the payload.\n *\n * Returns `null` when the path is rejected or the content can't be read\n * (missing file, unknown ref, binary). Callers surface that as a 404/400.\n */\nexport function readContextLines(\n opts: ContextLinesOptions,\n): ContextLinesResult | null {\n const { root, relPath, ref } = opts;\n if (!relPath || !isInsideRoot(root, relPath)) return null;\n\n const start = Math.max(1, Math.floor(opts.start));\n const end = Math.max(start, Math.floor(opts.end));\n\n const content =\n !ref || ref === 'working'\n ? readWorkingTree(root, relPath)\n : readAtRef(root, ref, relPath);\n if (content === null) return null;\n\n // Split on either line ending; drop the trailing empty element a final\n // newline produces so `totalLines` matches the line count git/editors\n // report. Matches how `parseGitDiff` strips line endings from hunk text,\n // so revealed context lines render identically to diff context lines.\n const all = content.split(/\\r?\\n/);\n if (all.length > 0 && all[all.length - 1] === '') all.pop();\n\n const totalLines = all.length;\n // start past EOF → empty slice, eof true.\n const slice = all.slice(start - 1, end);\n return {\n lines: slice,\n start,\n totalLines,\n eof: end >= totalLines,\n };\n}\n\n/** Symlink-safe working-tree read. Returns null on any failure. */\nfunction readWorkingTree(root: string, relPath: string): string | null {\n try {\n const absPath = path.join(root, relPath);\n const realRoot = fs.realpathSync(path.resolve(root));\n const realPath = fs.realpathSync(absPath);\n const sep = path.sep;\n if (realPath !== realRoot && !realPath.startsWith(realRoot + sep)) {\n return null;\n }\n return fs.readFileSync(absPath, 'utf-8');\n } catch {\n return null;\n }\n}\n\n/** `git show <ref>:<relPath>`. Uses cross-spawn directly (not the trimming\n * `git()` helper) so trailing newlines survive. Returns null on failure. */\nfunction readAtRef(root: string, ref: string, relPath: string): string | null {\n const r = spawn.sync('git', ['show', `${ref}:${relPath}`], {\n cwd: root,\n encoding: 'utf-8',\n maxBuffer: 64 * 1024 * 1024,\n windowsHide: true,\n });\n if (r.status === 0 && typeof r.stdout === 'string') return r.stdout;\n return null;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport chokidar from 'chokidar';\n\nexport interface FsWatcherOptions {\n /** Working-tree roots to watch. .git/ subdirs are filtered out. */\n roots: string[];\n /** Debounce window for fs events before firing `onChange`. */\n debounceMs?: number;\n /** Fires once per debounce window when anything under `roots` changed. */\n onChange: () => void;\n}\n\nexport interface FsWatcher {\n stop(): void;\n}\n\n/**\n * Directory names the watcher must never descend into. chokidar (v4+, no\n * fsevents) opens one OS watch per directory, so recursively watching a\n * dependency install or build-output tree exhausts the process's file\n * descriptors (EMFILE) and wedges the server. These are all git-ignored in\n * practice, so changes there never affect the diff — at worst a manual\n * browser refresh is needed for an edit inside one.\n */\nconst IGNORED_DIRS = new Set([\n '.git',\n 'node_modules',\n 'bin', // .NET build output\n 'obj', // .NET build output\n 'dist',\n 'build',\n 'out',\n 'target', // Rust / JVM\n '.next',\n '.nuxt',\n '.svelte-kit',\n '.turbo',\n '.gradle',\n 'coverage',\n '.vs',\n '.idea',\n]);\n\n/**\n * Whether chokidar should skip a path. A path is ignored when any segment\n * *below one of the watched roots* is in {@link IGNORED_DIRS}. Matching\n * relative to the root (not the absolute path) avoids false positives when an\n * ancestor directory happens to be named e.g. `build`.\n */\nexport function isIgnoredWatchPath(roots: string[], filePath: string): boolean {\n for (const root of roots) {\n const rel = path.relative(root, filePath).replace(/\\\\/g, '/');\n if (rel === '' || rel.startsWith('../')) continue; // not under this root\n if (rel.split('/').some((seg) => IGNORED_DIRS.has(seg))) return true;\n }\n return false;\n}\n\n// macOS and Windows support recursive fs.watch (backed by FSEvents /\n// ReadDirectoryChangesW) — a single OS handle covers the whole tree with\n// O(1) file descriptors. Linux has no recursive fs.watch, so we keep\n// chokidar there.\nconst SUPPORTS_RECURSIVE_WATCH =\n process.platform === 'darwin' || process.platform === 'win32';\n\nfunction logWatchError(err: unknown): void {\n process.stderr.write(\n chalk.yellow('[watcher] fs error: ') + (err as Error).message + '\\n',\n );\n}\n\n/**\n * Debounced recursive watcher over one-or-more repo roots, filtering out\n * `.git/`, `node_modules/`, and build output. Shared between `wd` (the diff\n * server's reload trigger) and any other live file-watcher need.\n *\n * On macOS/Windows this uses one recursive `fs.watch` per root: O(1) fds no\n * matter how big the tree. chokidar (v4 — no bundled fsevents) instead opens\n * one watch per directory, which on a large repo exhausts the process's file\n * descriptors and starves the `git` subprocesses the diff server spawns\n * (branch detection then silently returns nothing). Linux keeps chokidar.\n */\nexport function createFsWatcher(opts: FsWatcherOptions): FsWatcher {\n const debounceMs = opts.debounceMs ?? 150;\n let debounceTimer: NodeJS.Timeout | null = null;\n\n const fire = (): void => {\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n debounceTimer = null;\n opts.onChange();\n }, debounceMs);\n };\n\n if (SUPPORTS_RECURSIVE_WATCH) {\n const watchers = opts.roots.map((root) => {\n const w = fs.watch(root, { recursive: true }, (_event, filename) => {\n // `filename` is relative to `root` (and may be null on some events).\n // The single FSEvents handle still reports ignored dirs, so filter\n // here to avoid spurious reloads from node_modules / build churn.\n if (\n filename &&\n isIgnoredWatchPath([root], path.join(root, filename.toString()))\n ) {\n return;\n }\n fire();\n });\n w.on('error', logWatchError);\n return w;\n });\n return {\n stop() {\n if (debounceTimer) clearTimeout(debounceTimer);\n for (const w of watchers) {\n try {\n w.close();\n } catch {\n /* already closed */\n }\n }\n },\n };\n }\n\n const watcher = chokidar.watch(opts.roots, {\n ignored: (filePath) => isIgnoredWatchPath(opts.roots, filePath),\n ignoreInitial: true,\n persistent: true,\n awaitWriteFinish: { stabilityThreshold: 50, pollInterval: 20 },\n });\n watcher.on('all', fire);\n watcher.on('error', logWatchError);\n\n return {\n stop() {\n if (debounceTimer) clearTimeout(debounceTimer);\n watcher.close().catch(() => { /* */ });\n },\n };\n}\n","import fs from 'node:fs';\nimport http from 'node:http';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst MIME: Record<string, string> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'application/javascript; charset=utf-8',\n '.mjs': 'application/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.ico': 'image/x-icon',\n '.map': 'application/json; charset=utf-8',\n};\n\n/** Resolve the directory shipped by the Vite build. */\nexport function resolveWebRoot(): string | null {\n const entryDir = path.dirname(process.argv[1] ?? '');\n const moduleDir = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.join(entryDir, 'web'),\n // bundled: this module is inlined into dist/<bin>.js, so dist/web is a\n // sibling of the bundle. Works even when argv[1] is an npm bin symlink\n // (which is not realpath'd, so the entryDir candidate above misses).\n path.join(moduleDir, 'web'),\n // dev/tsx fallback: walk up from src/core to repo root then into dist/web.\n path.resolve(moduleDir, '../../dist/web'),\n ];\n for (const c of candidates) {\n if (fs.existsSync(path.join(c, 'index.html'))) return c;\n }\n return null;\n}\n\n/**\n * Serve a static file under `root`. Returns true if it served a file. Does\n * NOT do SPA fallback — caller decides whether to re-try with /index.html\n * on miss.\n */\nexport function serveStatic(\n root: string,\n urlPath: string,\n res: http.ServerResponse,\n): boolean {\n const clean = urlPath.split('?')[0];\n const requested = clean === '/' ? '/index.html' : clean;\n const filePath = path.join(root, requested);\n const norm = path.normalize(filePath);\n const base = path.normalize(root);\n if (norm !== base && !norm.startsWith(base + path.sep)) return false;\n let stat: fs.Stats;\n try {\n stat = fs.statSync(norm);\n } catch {\n return false;\n }\n if (!stat.isFile()) return false;\n const ext = path.extname(norm).toLowerCase();\n res.writeHead(200, {\n 'Content-Type': MIME[ext] ?? 'application/octet-stream',\n 'Cache-Control': 'no-cache',\n });\n fs.createReadStream(norm).pipe(res);\n return true;\n}\n\n/** Convenience: serve the file if it exists, else fall back to index.html. */\nexport function serveStaticOrShell(\n root: string,\n urlPath: string,\n res: http.ServerResponse,\n): boolean {\n if (serveStatic(root, urlPath, res)) return true;\n if (serveStatic(root, '/index.html', res)) return true;\n return false;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { Context } from 'hono';\n\nconst MIME: Record<string, string> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'application/javascript; charset=utf-8',\n '.mjs': 'application/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.ico': 'image/x-icon',\n '.map': 'application/json; charset=utf-8',\n};\n\nfunction readFile(root: string, relPath: string): { body: Buffer; ext: string } | null {\n const clean = relPath.split('?')[0];\n const requested = clean === '/' ? '/index.html' : clean;\n const filePath = path.join(root, requested);\n const norm = path.normalize(filePath);\n if (!norm.startsWith(path.normalize(root))) return null;\n let stat: fs.Stats;\n try {\n stat = fs.statSync(norm);\n } catch {\n return null;\n }\n if (!stat.isFile()) return null;\n const ext = path.extname(norm).toLowerCase();\n return { body: fs.readFileSync(norm), ext };\n}\n\n/**\n * Hono handler for the bundled SPA. Tries to serve the requested file; if it\n * doesn't exist, falls back to index.html so client-side routing works on\n * deep links. Skips /api/* paths entirely (those should be handled by\n * earlier routes; if we reach here it's a 404).\n */\nexport function serveSpa(c: Context, webRoot: string): Response {\n const url = new URL(c.req.url);\n if (url.pathname.startsWith('/api/')) {\n return c.json({ error: 'not found' }, 404);\n }\n const hit = readFile(webRoot, url.pathname) ?? readFile(webRoot, '/index.html');\n if (!hit) return c.json({ error: 'not found' }, 404);\n return new Response(new Uint8Array(hit.body), {\n status: 200,\n headers: {\n 'Content-Type': MIME[hit.ext] ?? 'application/octet-stream',\n 'Cache-Control': 'no-cache',\n },\n });\n}\n","/**\n * Shared zod schemas for the comment HTTP surface. Kept separate from\n * `comment-types.ts` because the SPA imports `comment-types.ts` for the\n * type-only exports and pulling zod into the SPA bundle is wasteful —\n * the schemas are server-side validation only.\n */\n\nimport { z } from 'zod';\n\nexport const commentInputSchema = z.object({\n repo: z.string().optional(),\n file: z.string().optional(),\n line: z.number().int().optional(),\n side: z.enum(['left', 'right', 'general']).optional(),\n body: z.string().min(1),\n status: z.enum(['published', 'draft']).optional(),\n lineContent: z.string().optional(),\n parentId: z.string().optional(),\n author: z.enum(['user', 'claude']).optional(),\n});\n\nexport const submitReviewSchema = z.object({\n summary: z.string().optional(),\n});\n","/**\n * Pure helper for the `wd -c` polling loop in `commands/diff.ts`.\n *\n * Given the previous-tick set of \"seen\" comment ids and the current\n * snapshot, return:\n * - new published comments to emit (in snapshot order)\n * - deleted ids to emit\n * - the next `seen` set to track going into the following tick\n *\n * The function MUTATES `seen` in place by adding new ids and removing\n * deleted ones. Callers can read `seen.size` after this returns to get\n * the running total — that's what's reported as `--- review done --- total`.\n *\n * Extracted from inline logic in `tryReviewViaWorkWeb` so it can be\n * tested without standing up a fake HTTP server or mocking process.exit.\n * The C-1 review finding (deleted ids never pruned, inflating the\n * reported total) lived here.\n */\n\nimport type { Comment } from './comment-types.js';\n\nexport interface SnapshotDiff {\n /** Published comments not already in `seen`, in snapshot order. */\n newComments: Comment[];\n /** Ids present in `seen` but missing from the current snapshot. */\n deleted: string[];\n}\n\nexport function diffReviewSnapshot(\n snapshot: Comment[],\n seen: Set<string>,\n): SnapshotDiff {\n const now = new Set(snapshot.map((c) => c.id));\n\n const deleted: string[] = [];\n for (const id of seen) {\n if (!now.has(id)) deleted.push(id);\n }\n for (const id of deleted) seen.delete(id);\n\n const newComments: Comment[] = [];\n for (const c of snapshot) {\n if (seen.has(c.id)) continue;\n if (c.status === 'draft') continue;\n newComments.push(c);\n }\n for (const c of snapshot) {\n if (c.status === 'published') seen.add(c.id);\n }\n\n return { newComments, deleted };\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { computeDiff } from './diff-pipeline.js';\nimport { resolveWebRoot } from './web-static.js';\nimport type { RepoSpec } from './repo-spec.js';\n\nexport type DiffBase = 'uncommitted' | 'branch';\n\nexport interface DiffPayload {\n repos: {\n name: string;\n root: string;\n files: ReturnType<typeof computeDiff>;\n }[];\n /** The ref the diff was computed against (HEAD for uncommitted, e.g.\n * `origin/main` for branch). */\n resolvedBase: string;\n}\n\nexport interface StaticBootData {\n context: {\n mode: 'review';\n scopeLabel: string;\n repos: { name: string }[];\n readOnly: true;\n staticMode: true;\n /** Which tab the SPA opens on. The user can toggle to the other one\n * in-browser; both diffs are inlined. */\n initialBase: DiffBase;\n };\n /** Both scopes, computed ahead of time. The SPA picks one based on\n * the toggle and re-uses the other when the user clicks across. */\n diffs: {\n uncommitted: DiffPayload;\n branch?: DiffPayload;\n };\n /** Legacy single-scope payload for backward compatibility with any\n * earlier static HTML. Mirrors diffs[initialBase] so an older client\n * that only looks at `diff` still renders. */\n diff: DiffPayload;\n}\n\nexport interface RenderStaticOptions {\n scopeLabel: string;\n /** Always computed (default tab). */\n uncommitted: RepoSpec[];\n /** Optional branch-scope specs. When provided, the SPA gains a\n * \"Since branch\" tab. The resolvedBase shows up next to the file\n * count and in the empty state. */\n branch?: { specs: RepoSpec[]; resolvedBase: string };\n /** Which tab opens by default. Defaults to 'uncommitted' or, when\n * branch specs are provided AND uncommitted has nothing to show,\n * the caller can set this to 'branch'. */\n initialBase?: DiffBase;\n}\n\nfunction buildDiff(specs: RepoSpec[], resolvedBase: string): DiffPayload {\n return {\n repos: specs.map((r) => ({\n name: r.name,\n root: r.root,\n files: computeDiff({ root: r.root, diffArg: r.diffArg }),\n })),\n resolvedBase,\n };\n}\n\n/**\n * Build a self-contained HTML file from the React SPA bundle. Reads the\n * built `dist/web/index.html`, inlines the JS/CSS as <script>/<style>, and\n * injects the boot data so the SPA can render without a server.\n *\n * Computes both diff scopes (Uncommitted vs Since branch) when branch\n * specs are provided so the toggle works entirely client-side — no\n * fetch needed, file:// works.\n */\nexport function renderStatic(opts: RenderStaticOptions): string {\n const webRoot = resolveWebRoot();\n if (!webRoot) {\n throw new Error('Could not find dist/web/. Run `npm run build` first.');\n }\n const shellPath = path.join(webRoot, 'index.html');\n let shell = fs.readFileSync(shellPath, 'utf-8');\n\n const uncommitted = buildDiff(opts.uncommitted, 'HEAD');\n const branch = opts.branch\n ? buildDiff(opts.branch.specs, opts.branch.resolvedBase)\n : undefined;\n const initialBase: DiffBase = opts.initialBase ?? 'uncommitted';\n const initial = initialBase === 'branch' && branch ? branch : uncommitted;\n\n const boot: StaticBootData = {\n context: {\n mode: 'review',\n scopeLabel: opts.scopeLabel,\n repos: opts.uncommitted.map((r) => ({ name: r.name })),\n readOnly: true,\n staticMode: true,\n initialBase: branch ? initialBase : 'uncommitted',\n },\n diffs: { uncommitted, branch },\n diff: initial,\n };\n\n // ORDER MATTERS: inject the boot script BEFORE inlining the bundle. The\n // SPA bundle contains literal `</head>` substrings (HTML template helpers\n // inside react-dom / marked / etc.), so replacing `</head>` *after*\n // inlining matches the wrong occurrence and splices JSON into JavaScript.\n // See tests/core/static-renderer.test.ts for the regression test.\n const bootJson = escapeForScriptTag(JSON.stringify(boot));\n const bootScript = `<script>window.__WD_BOOT__=${bootJson};</script>`;\n // String.prototype.replace interprets `$&`, `$'`, `$\\``, `$1`, `$$` in\n // the replacement string. The boot JSON can absolutely contain `$'` —\n // e.g. a shell regex like `'^(None)?$'` — which would silently splice\n // the substring AFTER `</head>` (the SPA's body) into the middle of\n // our JSON. Use a function callback so the replacement is taken\n // verbatim, no $-substitution. (This bit us in real CI logs.)\n shell = shell.replace(/<\\/head>/i, () => `${bootScript}</head>`);\n\n // Inline external CSS/JS so the file works over file:// with no server.\n // Same `</tag` escape rule as the JS inliner — a literal `</style` in\n // the CSS bundle (CSS-in-JS comment strings, minifier quirks) would\n // close the wrapping <style> tag early and dump the rest into HTML.\n shell = shell.replace(\n /<link rel=\"stylesheet\"[^>]*href=\"([^\"]+)\"[^>]*>/g,\n (_match, href) => {\n const css = readAsset(webRoot, href);\n if (!css) return '';\n const escaped = css.replace(/<\\/style/gi, '<\\\\/style');\n return `<style>${escaped}</style>`;\n },\n );\n shell = shell.replace(\n /<script\\b[^>]*\\bsrc=\"([^\"]+)\"[^>]*><\\/script>/g,\n (_match, src) => {\n const js = readAsset(webRoot, src);\n if (!js) return '';\n // Any literal `</script>` inside the bundle would terminate the outer\n // <script> tag early. Browsers match `</script` case-insensitively\n // with optional whitespace, so escape that form.\n const escaped = js.replace(/<\\/script/gi, '<\\\\/script');\n return `<script type=\"module\">${escaped}</script>`;\n },\n );\n\n return shell;\n}\n\n/**\n * Make a JSON payload safe to embed as a literal value inside a `<script>`\n * tag in HTML. JSON itself is fine, but several characters break when it's\n * inlined as JS source:\n *\n * 1. `</script` (case-insensitive) — closes the wrapping tag.\n * 2. `<!--` — flips the HTML parser into \"script data escaped\" state.\n * Combined with a later `<script` / `-->` it can hide code from the\n * parser; a defensive escape is cheaper than reasoning about it.\n * 3. U+2028 (LINE SEPARATOR) and U+2029 (PARAGRAPH SEPARATOR) — legal\n * inside JSON strings, but treated as line terminators inside JS\n * string literals on older engines. JSON.stringify emits them raw.\n *\n * Patterns use \\uXXXX escapes (not literal characters) so esbuild doesn't\n * interpret them as regex line terminators at parse time.\n */\nexport function escapeForScriptTag(json: string): string {\n // We escape the leading `<` of any HTML-sensitive opener (`</script`,\n // `<!--`) as `<` instead of `\\/` or `\\!`. `<` is the\n // strict-JSON form of `<`, so the output is parsable as JSON AND safe\n // to embed in a <script> tag.\n //\n // U+2028/U+2029: legal in JSON strings but treated as line terminators\n // inside legacy JS string literals. Escape both. RegExp constructor\n // avoids putting a literal line-separator in the source file (esbuild\n // refuses, and they're invisible in diffs).\n //\n // Raw control chars (LF/CR/NUL/etc.): we've seen JSON.stringify output\n // in the wild that somehow contains raw newlines mid-string (cause\n // still under investigation — likely something upstream that hands us\n // a pre-built string masquerading as a JSON fragment). JSON.parse\n // rejects raw control chars in strings, so the SPA crashes with\n // \"Bad control character\" or \"failed to fetch\". Belt-and-suspenders:\n // re-escape every raw control char so the output is always valid JSON\n // regardless of what produced it.\n const LS_RE = new RegExp('\\\\u2028', 'g');\n const PS_RE = new RegExp('\\\\u2029', 'g');\n return json\n .replace(/<(\\/script|!--)/gi, '\\\\u003c$1')\n .replace(LS_RE, '\\\\u2028')\n .replace(PS_RE, '\\\\u2029')\n .replace(/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f]/g, (c) => {\n // Skip \\t (0x09), \\n (0x0a), \\r (0x0d) — those are handled below\n // as proper JSON escapes so the parser keeps the line breaks\n // visible if they were intentional.\n return '\\\\u' + c.charCodeAt(0).toString(16).padStart(4, '0');\n })\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r');\n}\n\nfunction readAsset(webRoot: string, urlPath: string): string | null {\n // Strip query string and leading slash; resolve under webRoot.\n const clean = urlPath.split('?')[0].replace(/^\\//, '');\n const full = path.join(webRoot, clean);\n // Guard against path traversal even though the shell controls the URLs.\n if (!path.normalize(full).startsWith(path.normalize(webRoot))) return null;\n try {\n return fs.readFileSync(full, 'utf-8');\n } catch {\n return null;\n }\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport type { CommandModule } from 'yargs';\nimport { startWebServer } from '../core/web-server.js';\nimport {\n installCommandHook,\n removeCommandHookSync,\n} from '../core/command-hook-installer.js';\nimport { openUrl } from '../utils/platform.js';\n\nfunction info(message: string): void {\n process.stderr.write(message + '\\n');\n}\n\nfunction urlFilePath(): string {\n return path.join(os.homedir(), '.work', 'web.url');\n}\nfunction pidFilePath(): string {\n return path.join(os.homedir(), '.work', 'web.pid');\n}\n\nfunction isPidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction readPid(): number | null {\n try {\n const raw = fs.readFileSync(pidFilePath(), 'utf-8').trim();\n const n = Number(raw);\n return Number.isFinite(n) && n > 0 ? n : null;\n } catch {\n return null;\n }\n}\n\nfunction readUrl(): string | null {\n try {\n const v = fs.readFileSync(urlFilePath(), 'utf-8').trim();\n return v || null;\n } catch {\n return null;\n }\n}\n\n/** Best-effort ping. We don't strictly need it — the PID check above is\n * authoritative — but a 200 from /api/context confirms the server is\n * actually serving, not just a stale process holding the port. */\nasync function pingsAlive(url: string, timeoutMs = 500): Promise<boolean> {\n try {\n const ctrl = new AbortController();\n const timer = setTimeout(() => ctrl.abort(), timeoutMs);\n const res = await fetch(url + 'api/context', { signal: ctrl.signal });\n clearTimeout(timer);\n return res.ok;\n } catch {\n return false;\n }\n}\n\nfunction stopExisting(): boolean {\n const pid = readPid();\n if (!pid) {\n info(chalk.gray('No work web running.'));\n return false;\n }\n if (!isPidAlive(pid)) {\n info(chalk.gray(`Stale PID ${pid} — cleaning up.`));\n try { fs.unlinkSync(pidFilePath()); } catch { /* */ }\n try { fs.unlinkSync(urlFilePath()); } catch { /* */ }\n return false;\n }\n try {\n process.kill(pid);\n info(chalk.gray(`Stopped work web (PID ${pid}).`));\n try { fs.unlinkSync(pidFilePath()); } catch { /* */ }\n try { fs.unlinkSync(urlFilePath()); } catch { /* */ }\n return true;\n } catch (err) {\n info(chalk.red(`Failed to stop PID ${pid}: ${(err as Error).message}`));\n return false;\n }\n}\n\nexport const webCommand: CommandModule = {\n command: 'web',\n describe:\n 'Open the browser dashboard: every worktree session in one tab. Singleton — one process per user.',\n builder: (yargs) =>\n yargs\n .option('open', {\n type: 'boolean',\n default: true,\n describe: 'Auto-open the dashboard in the default browser. Use --no-open to skip.',\n })\n .option('stop', {\n type: 'boolean',\n default: false,\n describe: 'Stop a running work web instance and exit.',\n })\n .option('lean', {\n type: 'boolean',\n default: false,\n hidden: true,\n describe:\n 'Internal: start without dashboard-only features (Claude activity watcher + hooks). Used by `wd` when it auto-starts work web for a diff-only session.',\n }),\n handler: async (argv) => {\n if (argv.stop) {\n stopExisting();\n process.exit(0);\n }\n\n // Singleton enforcement. Two work web servers running at once is\n // strictly bad: they fight over `~/.work/settings.json` hooks,\n // each one holds a port, and only the most-recently-started is\n // discoverable via `web.url`. Detect a live previous instance and\n // either reuse it (open browser) or refuse to start.\n const existingPid = readPid();\n if (existingPid && isPidAlive(existingPid)) {\n const url = readUrl();\n if (url && (await pingsAlive(url))) {\n info(\n chalk.gray(\n `work web already running at ${url} (PID ${existingPid}). Opening browser.`,\n ),\n );\n if (argv.open) openUrl(url);\n process.exit(0);\n }\n info(\n chalk.yellow(\n `PID ${existingPid} is alive but not responding at ${url ?? '<unknown>'}. Use \\`work web --stop\\` to kill it, then re-run.`,\n ),\n );\n process.exit(1);\n }\n // Stale files from a crashed previous run — wipe before we write\n // our own.\n try { fs.unlinkSync(pidFilePath()); } catch { /* */ }\n try { fs.unlinkSync(urlFilePath()); } catch { /* */ }\n\n const lean = !!argv.lean || process.env.WORK_WEB_LEAN === '1';\n const handle = await startWebServer({ lean });\n try {\n fs.mkdirSync(path.dirname(urlFilePath()), { recursive: true });\n fs.writeFileSync(urlFilePath(), handle.url);\n fs.writeFileSync(pidFilePath(), String(process.pid));\n } catch { /* */ }\n\n info(\n chalk.gray(\n `work web running at ${handle.url}${lean ? ' (lean — diff-only mode)' : ''}`,\n ),\n );\n info(chalk.gray('Press Ctrl+C to stop. Or: `work web --stop` from another shell.'));\n if (argv.open) openUrl(handle.url);\n\n // Install Claude hooks so any live Claude in a worktree we know\n // about picks up pending review comments without the user having\n // to type. Both are no-ops when nothing's pending. Removed cleanly\n // on shutdown. Skipped in lean mode — a diff-only session doesn't\n // need to mutate the user's ~/.claude/settings.json.\n if (!lean) {\n await Promise.all([\n installCommandHook({\n owner: 'web',\n event: 'UserPromptSubmit',\n command: 'work hook prompt-submit',\n timeoutSec: 5,\n }),\n installCommandHook({\n owner: 'web',\n event: 'Stop',\n command: 'work hook stop',\n timeoutSec: 5,\n }),\n ]).catch(() => { /* best-effort */ });\n }\n\n const shutdown = () => {\n info(chalk.gray('\\nStopping work web.'));\n try { fs.unlinkSync(urlFilePath()); } catch { /* */ }\n try { fs.unlinkSync(pidFilePath()); } catch { /* */ }\n if (!lean) {\n try { removeCommandHookSync('web', 'UserPromptSubmit'); } catch { /* */ }\n try { removeCommandHookSync('web', 'Stop'); } catch { /* */ }\n }\n handle.stop();\n process.exit(0);\n };\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n // Windows doesn't deliver SIGTERM reliably; trap exit too so we\n // best-effort clean up our pid/url files even on abrupt deaths.\n process.on('exit', () => {\n try { fs.unlinkSync(pidFilePath()); } catch { /* */ }\n try { fs.unlinkSync(urlFilePath()); } catch { /* */ }\n });\n await new Promise(() => {});\n },\n};\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport { Hono } from 'hono';\nimport { streamSSE } from 'hono/streaming';\nimport { computeDiff } from './diff-pipeline.js';\nimport { resolveRepoDiff } from './diff-scope.js';\nimport { loadHistory, type WorktreeSession } from './history.js';\nimport {\n disposeAllWatchers,\n findSession,\n sessionIdFor,\n subscribeSession,\n} from './web-state.js';\nimport { readSessionMeta } from './session-meta.js';\nimport { claudeProjectsRoot } from './claude-activity.js';\nimport { createFsWatcher } from './fs-watcher.js';\nimport { mountSessionCommentRoutes } from './session-comment-routes.js';\nimport { mountPanesRoutes } from './panes-routes.js';\nimport { mountWorktreeRoutes } from './worktree-routes.js';\nimport { mountScopeRoutes } from './scope-routes.js';\nimport { mountTerminalRoutes } from './terminal-routes.js';\nimport { disposeAllScopes, listScopes } from './scope-manager.js';\nimport { clearCheckpoints } from './checkpoint.js';\nimport { attachTerminalWs } from './terminal-ws.js';\nimport { disposeAllPtys } from './pty-pool.js';\nimport { resolveWebRoot } from './web-static.js';\nimport { serveSpa } from './spa-handler.js';\nimport { launch, type DiffServerHandle, type SseEvent } from './diff-server.js';\nimport type { ParsedFile } from './diff-parse.js';\n\nexport type WebServerHandle = DiffServerHandle;\n\nfunction sessionToWire(s: WorktreeSession) {\n const id = sessionIdFor(s);\n const meta = readSessionMeta(id, s);\n return {\n id,\n target: s.target,\n branch: s.branch,\n isGroup: s.isGroup,\n paths: s.paths,\n baseBranch: s.baseBranch,\n jiraKey: s.jiraKey,\n createdAt: s.createdAt,\n lastAccessedAt: s.lastAccessedAt,\n draftCount: meta.draftCount,\n commentCount: meta.commentCount,\n claudeCount: meta.claudeCount,\n ptyStatus: meta.ptyStatus,\n lastActivity: meta.lastActivity,\n activityState: meta.activityState,\n pendingForClaudeCount: meta.pendingForClaudeCount,\n };\n}\n\nexport interface RepoData {\n name: string;\n root: string;\n files: ParsedFile[];\n /** Per-repo resolved parent for branch-mode diffs. `HEAD` for uncommitted. */\n resolvedBase: string;\n}\n\nexport type DiffBase = 'uncommitted' | 'branch';\n\ninterface SessionDiffResult {\n repos: RepoData[];\n /** Primary repo's resolved parent — used for the single-line \"vs X\"\n * badge. For groups this is `paths[0]`'s parent. */\n resolvedBase: string;\n}\n\n/**\n * Compute the diff for a session under one of two scopes:\n * - 'uncommitted' — `git diff HEAD` (default). Just the working-tree\n * deltas — what's not committed yet.\n * - 'branch' — everything since this worktree was forked. Uses the\n * session's recorded `baseBranch` when known, falls back to\n * auto-detection against main/master/dev/develop.\n *\n * Per-repo `resolvedBase` is included on each entry so the UI can label\n * per-repo if it wants to (groups may have different parents per repo).\n */\nfunction computeSessionDiff(s: WorktreeSession, base: DiffBase): SessionDiffResult {\n const resolved = s.paths.map((p) =>\n resolveRepoDiff(p, base, s.baseBranches?.[p] ?? s.baseBranch),\n );\n const repos = s.paths.map((p, i) => ({\n name: path.basename(p),\n root: p,\n resolvedBase: resolved[i].resolvedBase,\n files: computeDiff({ root: p, diffArg: resolved[i].diffArg }),\n }));\n return { repos, resolvedBase: resolved[0]?.resolvedBase ?? 'HEAD' };\n}\n\nexport interface WebServerOptions {\n /** When true, skip features that are only useful for the full\n * dashboard view (Claude transcript activity watcher, etc.). Used\n * when `wd` auto-starts work web on demand: the user opened a diff,\n * not the dashboard, so paying for activity tracking + Claude hooks\n * is wasted setup. Defaults to false (full dashboard).\n *\n * Lean mode keeps:\n * - the SPA + diff/scope routes (the reason wd needs the server)\n * - history.json + tasks.json file-watches (cheap, polling)\n * - the broadcast / SSE infra\n * Lean mode skips:\n * - chokidar watcher over `~/.claude/projects` (activity feed)\n * - the 10s decay tick (sessions-changed re-broadcast)\n * Claude-hooks installation is gated separately in `work web`'s\n * command handler — it's not in `startWebServer`. */\n lean?: boolean;\n}\n\nexport async function startWebServer(\n opts: WebServerOptions = {},\n): Promise<WebServerHandle> {\n const { lean = false } = opts;\n const webRoot = resolveWebRoot();\n if (!webRoot) {\n throw new Error(\n 'Could not find dist/web/. Run `npm run build:web` (or `npm run build`) first.',\n );\n }\n\n const sseListeners = new Set<(e: SseEvent) => void>();\n const broadcast = (event: string, data: unknown) => {\n for (const cb of sseListeners) cb({ event, data });\n };\n\n // Watch ~/.work/history.json so the sidebar reflects worktrees created\n // (or removed) by other terminals in real time.\n const home = os.homedir();\n const historyPath = path.join(home, '.work', 'history.json');\n const onHistoryChange = () =>\n broadcast('sessions-changed', { ts: Date.now() });\n fs.watchFile(historyPath, { interval: 1000 }, onHistoryChange);\n\n // Same idea for tasks — `work todo add` from a separate terminal\n // should refresh the dashboard's Tasks pane without a manual reload.\n const tasksPath = path.join(home, '.work', 'tasks.json');\n const onTasksChange = () => broadcast('tasks-changed', { ts: Date.now() });\n fs.watchFile(tasksPath, { interval: 1000 }, onTasksChange);\n\n // Watch Claude's per-project transcripts so the dashboard sees external\n // terminals coming alive. Claude writes constantly while it's thinking;\n // the watcher debounces to 250 ms so we don't spam the sidebar 100×/s\n // mid-turn. The same broadcast also covers our own PTYs writing here.\n const projectsRoot = claudeProjectsRoot();\n let activityWatcher: { stop(): void } | null = null;\n if (!lean) {\n try {\n if (fs.existsSync(projectsRoot)) {\n activityWatcher = createFsWatcher({\n roots: [projectsRoot],\n debounceMs: 250,\n onChange: () => broadcast('sessions-changed', { ts: Date.now() }),\n });\n }\n } catch { /* watcher startup is best-effort */ }\n }\n\n // Decay tick: even when nothing writes, sessions transition active → open\n // → stale purely by elapsed time. Re-broadcast every 10 s so the badges\n // catch up. Cheap — the client just refetches /api/sessions. Skipped\n // in lean mode (no dashboard consumer).\n const decayTick = lean\n ? null\n : setInterval(\n () => broadcast('sessions-changed', { ts: Date.now() }),\n 10_000,\n );\n\n const app = new Hono();\n\n app.get('/api/context', (c) => c.json({ mode: 'dashboard' }));\n\n app.get('/api/sessions', (c) =>\n c.json({ sessions: loadHistory().map(sessionToWire) }),\n );\n\n app.get('/api/sessions/:id/diff', (c) => {\n const id = c.req.param('id');\n const session = findSession(id);\n if (!session) return c.json({ error: 'unknown session' }, 404);\n const baseParam = c.req.query('base') ?? 'uncommitted';\n const base: DiffBase =\n baseParam === 'branch' ? 'branch' : 'uncommitted';\n try {\n const { repos, resolvedBase } = computeSessionDiff(session, base);\n return c.json({ sessionId: id, base, resolvedBase, repos });\n } catch (err) {\n return c.json({ error: (err as Error).message }, 500);\n }\n });\n\n // Per-session comments (file-backed). Emits comments-changed via broadcast.\n mountSessionCommentRoutes(app, { broadcast });\n\n // PRs / Jira / Tasks read endpoints + tasks CRUD. Emits tasks-changed.\n mountPanesRoutes(app, { broadcast });\n\n // Worktree mutations (create/remove/sync/rebase/open-editor). Each\n // emits sessions-changed so the sidebar refreshes.\n mountWorktreeRoutes(app, { broadcast });\n\n // Ad-hoc scopes registered by `wd` invocations — gives the dashboard\n // an addressable URL per scope (/diff/<hash>, /review/<hash>) so we\n // can collapse the standalone wd-server/wd -c daemons into this\n // single process.\n mountScopeRoutes(app, { broadcast });\n\n // PTY upgrade endpoint. Returns a noop response — the upgrade is handled\n // by the server's `upgrade` event below.\n mountTerminalRoutes(app);\n\n app.get('/events', (c) => {\n const wantedSession = c.req.query('session');\n return streamSSE(c, async (stream) => {\n const listener = (e: SseEvent) => {\n stream\n .writeSSE({ event: e.event, data: JSON.stringify(e.data) })\n .catch(() => { /* */ });\n };\n sseListeners.add(listener);\n await stream.writeSSE({ event: 'connected', data: '' });\n\n let unsubscribe: (() => void) | null = null;\n if (wantedSession) {\n unsubscribe = subscribeSession(wantedSession, () => {\n stream\n .writeSSE({\n event: 'diff-changed',\n data: JSON.stringify({ sessionId: wantedSession }),\n })\n .catch(() => { /* */ });\n });\n }\n await new Promise<void>((resolve) => {\n stream.onAbort(() => {\n sseListeners.delete(listener);\n if (unsubscribe) unsubscribe();\n resolve();\n });\n });\n });\n });\n\n // SPA fallback last.\n app.get('*', (c) => serveSpa(c, webRoot));\n\n const handle = await launch(app);\n const wsBridge = attachTerminalWs(handle.httpServer, handle.port);\n process.stderr.write(chalk.gray(`[web] dashboard at ${handle.url}\\n`));\n\n return {\n url: handle.url,\n port: handle.port,\n stop: async () => {\n fs.unwatchFile(historyPath, onHistoryChange);\n fs.unwatchFile(tasksPath, onTasksChange);\n if (decayTick) clearInterval(decayTick);\n activityWatcher?.stop();\n disposeAllWatchers();\n // Sweep checkpoint refs + manifests for every active scope BEFORE\n // wiping the registry — otherwise `refs/wd/<hash>/*` refs leak\n // across `work web` restarts and accumulate without bound in\n // every repo the user has reviewed.\n for (const scope of listScopes()) {\n try {\n clearCheckpoints(scope.hash, scope.paths);\n } catch {\n // Best-effort — partial cleanup is fine, log only matters for\n // diagnosis and doesn't change the shutdown outcome.\n }\n }\n disposeAllScopes();\n disposeAllPtys();\n wsBridge.close();\n await handle.stop();\n },\n };\n}\n","import path from 'node:path';\nimport crypto from 'node:crypto';\nimport chokidar, { type FSWatcher } from 'chokidar';\nimport { loadHistory, type WorktreeSession } from './history.js';\n\n/** Stable per-session id, same algorithm as web-server. */\nexport function sessionIdFor(s: WorktreeSession): string {\n return crypto\n .createHash('sha1')\n .update(`${s.target}:${s.branch}`)\n .digest('hex')\n .slice(0, 12);\n}\n\nexport function findSession(sessionId: string): WorktreeSession | null {\n return loadHistory().find((s) => sessionIdFor(s) === sessionId) ?? null;\n}\n\ninterface WatcherEntry {\n watcher: FSWatcher;\n subscribers: Set<() => void>;\n debounce: NodeJS.Timeout | null;\n}\n\nconst sessionWatchers = new Map<string, WatcherEntry>();\nconst DEBOUNCE_MS = 150;\n\n/**\n * Subscribe to filesystem changes for a session's worktree(s). chokidar is\n * started on first subscriber and stopped when the last one leaves —\n * reference-counted so the cost stays proportional to what's actually being\n * viewed in the browser.\n *\n * Returns an unsubscribe function. Safe to call multiple times.\n */\nexport function subscribeSession(\n sessionId: string,\n onChange: () => void,\n): () => void {\n const session = findSession(sessionId);\n if (!session) return () => { /* unknown session */ };\n\n let entry = sessionWatchers.get(sessionId);\n if (!entry) {\n const roots = session.paths;\n const watcher = chokidar.watch(roots, {\n ignored: (filePath) => {\n for (const r of roots) {\n const rel = path.relative(r, filePath).replace(/\\\\/g, '/');\n if (rel === '.git' || rel.startsWith('.git/')) return true;\n }\n return false;\n },\n ignoreInitial: true,\n persistent: true,\n awaitWriteFinish: { stabilityThreshold: 50, pollInterval: 20 },\n });\n const newEntry: WatcherEntry = {\n watcher,\n subscribers: new Set(),\n debounce: null,\n };\n watcher.on('all', () => {\n if (newEntry.debounce) clearTimeout(newEntry.debounce);\n newEntry.debounce = setTimeout(() => {\n newEntry.debounce = null;\n for (const cb of newEntry.subscribers) {\n try { cb(); } catch { /* swallow */ }\n }\n }, DEBOUNCE_MS);\n });\n sessionWatchers.set(sessionId, newEntry);\n entry = newEntry;\n }\n\n entry.subscribers.add(onChange);\n\n let released = false;\n return () => {\n if (released) return;\n released = true;\n entry!.subscribers.delete(onChange);\n if (entry!.subscribers.size === 0) {\n if (entry!.debounce) clearTimeout(entry!.debounce);\n entry!.watcher.close().catch(() => { /* */ });\n sessionWatchers.delete(sessionId);\n }\n };\n}\n\n/** Stop every active session watcher. Called on server shutdown. */\nexport function disposeAllWatchers(): void {\n for (const [, entry] of sessionWatchers) {\n if (entry.debounce) clearTimeout(entry.debounce);\n entry.watcher.close().catch(() => { /* */ });\n }\n sessionWatchers.clear();\n}\n","import { PtySession } from '../tui/session.js';\nimport { findSession } from './web-state.js';\nimport { loadConfig } from './config.js';\nimport { getAiTool } from './ai-launcher.js';\n\nexport interface PooledPty {\n session: PtySession;\n readonly sessionId: string;\n /** Replay buffer of recent PTY output, sent to each new attaching client. */\n replay(): string;\n write(data: string): void;\n resize(cols: number, rows: number): void;\n /** Subscribe to live PTY output. Returns an unsubscribe disposer. */\n subscribe(cb: (data: string) => void): () => void;\n dispose(): void;\n isExited(): boolean;\n}\n\n/**\n * Per-session PTY pool. Lazily spawns Claude (or the configured AI tool)\n * for a session the first time someone attaches a terminal. PTYs survive\n * browser disconnects — only `dispose()` (server shutdown) kills them.\n *\n * Each PTY has an in-memory replay buffer; a fresh WS attach replays it so\n * the user sees the existing scrollback rather than a blank screen.\n */\nconst pool = new Map<string, PooledPty>();\nconst REPLAY_MAX = 64 * 1024;\n\nexport function getOrCreatePty(sessionId: string): PooledPty | null {\n const existing = pool.get(sessionId);\n if (existing && !existing.isExited()) return existing;\n if (existing) pool.delete(sessionId);\n\n const session = findSession(sessionId);\n if (!session) return null;\n\n const cwd = session.paths[0];\n if (!cwd) return null;\n\n const config = loadConfig() ?? {};\n const tool = getAiTool(config);\n const pty = new PtySession(cwd, 120, 32, undefined, {\n tool,\n resume: true,\n port: session.port,\n });\n\n const subscribers = new Set<(data: string) => void>();\n let replayBuf = '';\n pty.setOutputHandler((data) => {\n replayBuf += data;\n if (replayBuf.length > REPLAY_MAX) {\n replayBuf = replayBuf.slice(replayBuf.length - REPLAY_MAX);\n }\n for (const cb of subscribers) {\n try { cb(data); } catch { /* */ }\n }\n });\n\n const entry: PooledPty = {\n session: pty,\n sessionId,\n replay: () => replayBuf,\n write: (data) => pty.write(data),\n resize: (cols, rows) => pty.resize(cols, rows),\n subscribe(cb) {\n subscribers.add(cb);\n return () => subscribers.delete(cb);\n },\n dispose: () => {\n subscribers.clear();\n pty.dispose();\n pool.delete(sessionId);\n },\n isExited: () => pty.exited,\n };\n pool.set(sessionId, entry);\n return entry;\n}\n\n/** Side-effect-free check: does an active (non-exited) PTY exist for this\n * session? Used by session-meta to compute the \"running/idle\" badge. */\nexport function peekPty(sessionId: string): boolean {\n const existing = pool.get(sessionId);\n return !!existing && !existing.isExited();\n}\n\nexport function disposeAllPtys(): void {\n for (const p of pool.values()) p.dispose();\n pool.clear();\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\nimport { createCommentStore, type CommentInput, type CommentStore } from './comment-store.js';\nimport { ensureFile, withFileLockSync, atomicWriteFile } from './fs-safe.js';\nimport type { Comment } from './comment-types.js';\n\n/**\n * File-backed comment store. One JSON file per session at\n * `~/.work/comments/<sessionId>.json`. Mutations are serialized in-process\n * via a simple in-memory queue so two browser tabs writing at the same\n * instant cannot interleave atomic-write attempts.\n *\n * Atomicity: every save writes a tmp file in the same dir then renames.\n * Crash-resistant for the common case (single mutator at a time).\n */\nexport interface CommentFileStore extends CommentStore {\n /** Drop the cached in-memory store and reload from disk. */\n reload(): void;\n}\n\n/** Canonical location of all per-session comment files. A function so it\n * re-resolves `os.homedir()` on every call — tests that mock `homedir()`\n * need this, and the cost is one `path.join` per access. */\nexport function commentsDir(): string {\n return path.join(os.homedir(), '.work', 'comments');\n}\n\nfunction ensureDir(): void {\n fs.mkdirSync(commentsDir(), { recursive: true });\n}\n\n/** File path for one session's comment store. Use this rather than rolling\n * your own concat. */\nexport function commentsFileFor(sessionId: string): string {\n return path.join(commentsDir(), `${sessionId}.json`);\n}\n\nfunction pathFor(sessionId: string): string {\n return commentsFileFor(sessionId);\n}\n\nfunction readDisk(sessionId: string): Comment[] {\n try {\n const raw = fs.readFileSync(pathFor(sessionId), 'utf-8');\n const parsed = JSON.parse(raw);\n return Array.isArray(parsed) ? (parsed as Comment[]) : [];\n } catch {\n return [];\n }\n}\n\nfunction writeDisk(sessionId: string, comments: Comment[]): void {\n ensureDir();\n atomicWriteFile(pathFor(sessionId), JSON.stringify(comments, null, 2));\n}\n\nconst cache = new Map<string, CommentFileStore>();\n\nexport function getCommentFileStore(sessionId: string): CommentFileStore {\n const existing = cache.get(sessionId);\n if (existing) return existing;\n\n const inner = createCommentStore();\n\n /** Replace the in-memory contents with the current on-disk contents.\n * Used to reconcile before a mutation so concurrent writers (e.g. a\n * `work broadcast` process appending under the same lock) aren't lost. */\n function reloadInner(): void {\n const list = inner.list() as Comment[];\n list.length = 0;\n for (const c of readDisk(sessionId)) list.push(c);\n }\n\n // Seed from disk.\n reloadInner();\n\n /**\n * Run a read-modify-write of this session's comment file under a\n * cross-process lock (§5.2): acquire the lock, reload the in-memory store\n * from disk so it reflects any concurrent appends, apply `mutate`, persist\n * atomically, then release. `persisted` reports whether anything changed so\n * a no-op (e.g. removing a missing id) can skip the write.\n */\n function lockedMutate<T>(\n mutate: () => { value: T; persisted: boolean },\n ): T {\n const file = pathFor(sessionId);\n ensureDir();\n ensureFile(file, '[]');\n return withFileLockSync(file, () => {\n reloadInner();\n const { value, persisted } = mutate();\n if (persisted) writeDisk(sessionId, inner.snapshot());\n return value;\n });\n }\n\n const store: CommentFileStore = {\n list: () => inner.list(),\n snapshot: () => inner.snapshot(),\n post(input: CommentInput) {\n return lockedMutate(() => {\n const c = inner.post(input);\n return { value: c, persisted: true };\n });\n },\n remove(id: string) {\n return lockedMutate(() => {\n const r = inner.remove(id);\n return { value: r, persisted: r };\n });\n },\n submit(summary: string | undefined) {\n return lockedMutate(() => {\n const result = inner.submit(summary);\n return { value: result, persisted: true };\n });\n },\n discardDrafts() {\n return lockedMutate(() => {\n const n = inner.discardDrafts();\n return { value: n, persisted: n > 0 };\n });\n },\n reload() {\n reloadInner();\n },\n };\n cache.set(sessionId, store);\n return store;\n}\n\n/** Clear the in-memory cache. Test/server-shutdown hook. */\nexport function clearCommentStoreCache(): void {\n cache.clear();\n}\n","/**\n * Bridge between `work web` review comments and any live Claude session\n * running in the same worktree. Storage layout:\n *\n * ~/.work/comments/<sessionId>.json — full comment store (existing)\n * ~/.work/comments/<sessionId>.delivered.json — array of comment ids that\n * have been surfaced to Claude\n *\n * \"Pending\" = published, user-authored, not in the delivered list. Replies\n * authored by Claude (author === 'claude') are excluded — Claude wrote\n * them; we don't need to echo them back.\n *\n * Delivery is lazy: when a Claude in the matching worktree submits a\n * prompt, the hook reads pending comments via this module, prints them as\n * a system-reminder block to stdout (which Claude Code injects into the\n * conversation), and marks them delivered.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { loadHistory, type WorktreeSession } from './history.js';\nimport { sessionIdFor } from './web-state.js';\nimport {\n commentsDir,\n commentsFileFor,\n getCommentFileStore,\n} from './comment-file-store.js';\nimport { scopeHashFor } from './repo-spec.js';\nimport type { Comment } from './comment-types.js';\n\nfunction pathFor(sessionId: string): {\n comments: string;\n delivered: string;\n} {\n return {\n comments: commentsFileFor(sessionId),\n delivered: path.join(commentsDir(), `${sessionId}.delivered.json`),\n };\n}\n\nfunction readJson<T>(filePath: string, fallback: T): T {\n try {\n return JSON.parse(fs.readFileSync(filePath, 'utf8')) as T;\n } catch {\n return fallback;\n }\n}\n\nfunction writeAtomic(filePath: string, content: string): void {\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;\n fs.writeFileSync(tmp, content, 'utf-8');\n fs.renameSync(tmp, filePath);\n}\n\n/** Norm-path comparison that mirrors what we do server-side. */\nfunction normalize(p: string): string {\n return path.resolve(p).replace(/\\\\/g, '/').toLowerCase();\n}\n\n/** Map a Claude cwd back to a session. Tries direct-match against any\n * session's path first, then ancestor match (so cwd inside a subdir of a\n * worktree still resolves to the worktree's session). */\nexport function findSessionForCwd(cwd: string): WorktreeSession | null {\n const norm = normalize(cwd);\n const sessions = loadHistory();\n // Direct match: cwd == one of the session's paths.\n for (const s of sessions) {\n for (const p of s.paths) {\n if (normalize(p) === norm) return s;\n }\n }\n // Ancestor match: cwd starts with one of the session's paths + sep.\n // Pick the longest matching prefix so nested worktrees disambiguate.\n let best: { session: WorktreeSession; len: number } | null = null;\n for (const s of sessions) {\n for (const p of s.paths) {\n const np = normalize(p);\n if (norm.startsWith(np + '/') && (!best || np.length > best.len)) {\n best = { session: s, len: np.length };\n }\n }\n }\n return best?.session ?? null;\n}\n\nfunction isPendingFor(delivered: Set<string>) {\n return (c: Comment) =>\n c.status === 'published' && c.author === 'user' && !delivered.has(c.id);\n}\n\n/** Returns published user comments that haven't been delivered yet.\n * Reads through the file-store cache so we see in-flight writes\n * (`session-meta.ts` and other readers couldn't, when they re-read the\n * disk directly). */\nexport function readPendingForSession(sessionId: string): Comment[] {\n const paths = pathFor(sessionId);\n const comments = getCommentFileStore(sessionId).snapshot();\n const delivered = new Set(readJson<string[]>(paths.delivered, []));\n return comments.filter(isPendingFor(delivered));\n}\n\n/** Comment-store ids for every `wd` scope that could cover this worktree.\n * A scope is keyed by `sha1` of its sorted repo roots (see\n * `repo-spec.scopeHashFor`), so from a session's paths we can rebuild the\n * exact ids without consulting `work web`'s in-memory scope registry:\n * - the whole-set hash → a group `wd` opened at the group root, or the\n * single-repo `wd` (one path);\n * - each individual path's hash → a `wd` opened inside one sub-repo of a\n * group worktree.\n * `registerScope` hashes `path.resolve()`d roots, so we resolve here too or\n * the hashes won't line up. Mirrors `scope-manager.commentStoreIdForScope`\n * (`scope-<hash>`). */\nfunction scopeStoreIdsForPaths(paths: string[]): string[] {\n const resolved = paths.map((p) => path.resolve(p));\n const ids = new Set<string>();\n ids.add(`scope-${scopeHashFor(resolved)}`);\n for (const p of resolved) ids.add(`scope-${scopeHashFor([p])}`);\n return [...ids];\n}\n\n/**\n * Pending comments for a whole worktree: the session's own comment store\n * PLUS any `wd` / `wd -c` scope review store covering the same paths.\n *\n * `wd` registers its review under a scope-hash comment store, not the\n * session store the hook reads — without this merge, comments left in the\n * `wd` review UI would never reach the Claude running in that worktree.\n * Delivered-tracking stays per-session (one `<sessionId>.delivered.json`),\n * so a comment surfaced here won't be re-delivered regardless of which\n * store it came from.\n */\nexport function readPendingForWorktree(session: WorktreeSession): Comment[] {\n const sessionId = sessionIdFor(session);\n const delivered = new Set(\n readJson<string[]>(pathFor(sessionId).delivered, []),\n );\n const pending = isPendingFor(delivered);\n const seen = new Set<string>();\n const out: Comment[] = [];\n const collect = (storeId: string) => {\n for (const c of getCommentFileStore(storeId).snapshot()) {\n if (seen.has(c.id) || !pending(c)) continue;\n seen.add(c.id);\n out.push(c);\n }\n };\n collect(sessionId);\n for (const storeId of scopeStoreIdsForPaths(session.paths)) collect(storeId);\n return out;\n}\n\n/** Persist a delivery batch. Adds these ids to the delivered set so they\n * never get re-surfaced. */\nexport function markDelivered(sessionId: string, ids: string[]): void {\n if (ids.length === 0) return;\n const paths = pathFor(sessionId);\n const delivered = new Set(readJson<string[]>(paths.delivered, []));\n for (const id of ids) delivered.add(id);\n writeAtomic(paths.delivered, JSON.stringify(Array.from(delivered), null, 2));\n}\n\n/** Cap the size of one comment body we surface to Claude. A pathologically\n * long comment shouldn't blow out Claude's context — we truncate, then\n * hint at the rest via \"(truncated)\". 4 KB is generous for a code-review\n * note while leaving headroom for batches. */\nconst MAX_BODY_BYTES = 4 * 1024;\n/** Overall cap on the whole system-reminder payload. Multiple long\n * comments at once still get bounded. */\nconst MAX_TOTAL_BYTES = 32 * 1024;\n\n/**\n * Format pending comments as a system-reminder block suitable for stdout.\n * Returns empty string when there's nothing pending — the caller (the\n * `work hook` CLI) just exits silently in that case.\n *\n * Truncates pathologically long bodies and caps the overall payload so\n * one runaway comment can't displace the rest of Claude's context.\n */\nexport function formatPendingForPrompt(pending: Comment[]): string {\n if (pending.length === 0) return '';\n\n const sorted = [...pending].sort((a, b) =>\n a.createdAt.localeCompare(b.createdAt),\n );\n\n const general = sorted.filter((c) => c.side === 'general' && !c.parentId);\n const inline = sorted.filter((c) => c.side !== 'general' && !c.parentId);\n const replies = sorted.filter((c) => c.parentId);\n\n const lines: string[] = [];\n lines.push('<system-reminder>');\n lines.push(\n `New review comments from \\`work web\\` (${pending.length} item${pending.length === 1 ? '' : 's'}):`,\n );\n lines.push('');\n\n if (general.length > 0) {\n lines.push('## General notes');\n for (const c of general) {\n // General notes (this is where `work broadcast` lands) may be multi-line\n // prompts — deliver the whole body, not just line 1. Only the byte cap\n // applies. Inline/reply comments still use the one-line `formatBody`.\n lines.push(formatFullBody('-', c));\n }\n lines.push('');\n }\n\n if (inline.length > 0) {\n lines.push('## Inline comments');\n for (const c of inline) {\n const where = `${c.repo}/${c.file}:${c.line} (${c.side})`;\n lines.push(formatBody(`- ${where}`, c));\n }\n lines.push('');\n }\n\n if (replies.length > 0) {\n lines.push('## Replies');\n for (const c of replies) {\n lines.push(formatBody(`- (reply to ${c.parentId})`, c));\n }\n lines.push('');\n }\n\n lines.push(\n 'Address them as part of your next response. You can reply via the same review UI by posting back to the latest review URL at `~/.work/web.url` + `/api/sessions/<id>/comments` with `author: \"claude\"`.',\n );\n lines.push('</system-reminder>');\n const out = lines.join('\\n');\n if (out.length <= MAX_TOTAL_BYTES) return out;\n const trimmed = out.slice(0, MAX_TOTAL_BYTES - 200);\n return (\n trimmed +\n '\\n\\n(…review payload truncated for context-window safety; ' +\n `${pending.length} comment(s) total — open work web for the full list.)\\n` +\n '</system-reminder>'\n );\n}\n\nfunction formatBody(prefix: string, c: Comment): string {\n const lead = c.body.split('\\n')[0].trim();\n const capped =\n lead.length > MAX_BODY_BYTES ? `${lead.slice(0, MAX_BODY_BYTES)}…` : lead;\n return `${prefix}: ${capped}${c.body.includes('\\n') ? ' …' : ''}`;\n}\n\n/** Like `formatBody` but preserves the full multi-line body (only the byte\n * cap applies). Multi-line bodies are emitted under the bullet, indented, so\n * a broadcast prompt arrives intact rather than truncated to its first line. */\nfunction formatFullBody(prefix: string, c: Comment): string {\n const body = c.body.trim();\n const capped =\n body.length > MAX_BODY_BYTES ? `${body.slice(0, MAX_BODY_BYTES)}…` : body;\n const bodyLines = capped.split('\\n');\n if (bodyLines.length === 1) return `${prefix}: ${bodyLines[0]}`;\n const [first, ...rest] = bodyLines;\n return [`${prefix}: ${first}`, ...rest.map((l) => ` ${l}`)].join('\\n');\n}\n\n/** Re-export so consumers don't have to know which module owns it. */\nexport { sessionIdFor };\n","import { peekPty } from './pty-pool.js';\nimport {\n readSessionActivity,\n type ActivityState,\n} from './claude-activity.js';\nimport { getCommentFileStore } from './comment-file-store.js';\nimport { readPendingForSession } from './pending-delivery.js';\nimport type { WorktreeSession } from './history.js';\n\nexport type PtyStatus = 'running' | 'idle';\n\nexport interface SessionMeta {\n draftCount: number;\n commentCount: number;\n /** Comments authored by claude — used by the client to compute unread. */\n claudeCount: number;\n /** Set when *our* PTY pool has a live Claude for this session. Doesn't\n * catch external terminals — `activityState` does. */\n ptyStatus: PtyStatus;\n /** ms since epoch of Claude's last write across this worktree, or null. */\n lastActivity: number | null;\n /** Derived from `lastActivity`: 'active' (≤30 s), 'open' (≤5 min), 'stale'. */\n activityState: ActivityState;\n /** Published user comments not yet surfaced to Claude via the\n * UserPromptSubmit hook. Drops to zero after Claude takes its next turn. */\n pendingForClaudeCount: number;\n}\n\n/**\n * Cheap per-session metadata. Goes through the file-store cache so badge\n * counts reflect in-flight writes that haven't yet hit disk — reading the\n * raw JSON behind the cache's back produced stale counts under load.\n *\n * `session` is required because we need the worktree paths to look up\n * Claude's transcript directory (the sessionId hash alone isn't enough).\n */\nexport function readSessionMeta(\n sessionId: string,\n session: WorktreeSession,\n): SessionMeta {\n const comments = getCommentFileStore(sessionId).snapshot();\n let drafts = 0;\n let claude = 0;\n for (const c of comments) {\n if (c.status === 'draft') drafts++;\n if (c.author === 'claude') claude++;\n }\n const activity = readSessionActivity(session);\n const pending = readPendingForSession(sessionId).length;\n return {\n draftCount: drafts,\n commentCount: comments.length,\n claudeCount: claude,\n ptyStatus: peekPty(sessionId) ? 'running' : 'idle',\n lastActivity: activity.lastActivity,\n activityState: activity.state,\n pendingForClaudeCount: pending,\n };\n}\n","import { Hono } from 'hono';\nimport { zValidator } from '@hono/zod-validator';\nimport { getCommentFileStore } from './comment-file-store.js';\nimport { findSession } from './web-state.js';\nimport { peekPty, getOrCreatePty } from './pty-pool.js';\nimport {\n formatPendingForPrompt,\n markDelivered,\n readPendingForSession,\n} from './pending-delivery.js';\nimport { commentInputSchema, submitReviewSchema } from './comment-schemas.js';\n\nexport interface MountOptions {\n /** Server-level broadcast — used to emit comments-changed events scoped\n * by sessionId so the SPA can refetch the right session's comments. */\n broadcast: (event: string, data: unknown) => void;\n}\n\n/**\n * Per-session comment endpoints under `/api/sessions/:id/`. Each session's\n * comments are persisted to its own JSON file. The dashboard SPA's\n * ReviewProvider uses these endpoints when it's in `dashboard` context.\n */\nexport function mountSessionCommentRoutes(\n app: Hono,\n opts: MountOptions,\n): void {\n function requireSession(id: string) {\n return findSession(id);\n }\n\n app.get('/api/sessions/:id/comments', (c) => {\n const id = c.req.param('id');\n if (!requireSession(id)) return c.json({ error: 'unknown session' }, 404);\n const store = getCommentFileStore(id);\n return c.json({ comments: store.snapshot() });\n });\n\n app.post(\n '/api/sessions/:id/comments',\n zValidator('json', commentInputSchema),\n (c) => {\n const id = c.req.param('id');\n if (!requireSession(id)) return c.json({ error: 'unknown session' }, 404);\n const store = getCommentFileStore(id);\n try {\n const comment = store.post(c.req.valid('json'));\n opts.broadcast('comments-changed', { sessionId: id, id: comment.id });\n // If a Claude is sitting in OUR own PTY (the Terminal tab is open\n // for this session), nudge it immediately by writing the pending\n // comments to stdin. The Stop / UserPromptSubmit hooks already\n // cover the cases where Claude is mid-turn or the user types; this\n // closes the \"idle in our PTY, user not typing\" case.\n deliverViaOwnedPty(id, comment.author);\n return c.json({ comment, comments: store.snapshot() });\n } catch (err) {\n return c.json({ error: (err as Error).message }, 400);\n }\n },\n );\n\n app.delete('/api/sessions/:id/comments/:cid', (c) => {\n const id = c.req.param('id');\n const cid = c.req.param('cid');\n if (!requireSession(id)) return c.json({ error: 'unknown session' }, 404);\n const store = getCommentFileStore(id);\n const removed = store.remove(cid);\n if (removed) opts.broadcast('comments-changed', { sessionId: id, deleted: cid });\n return c.json({ comments: store.snapshot() });\n });\n\n app.post(\n '/api/sessions/:id/submit-review',\n zValidator('json', submitReviewSchema),\n (c) => {\n const id = c.req.param('id');\n if (!requireSession(id)) return c.json({ error: 'unknown session' }, 404);\n const store = getCommentFileStore(id);\n const result = store.submit(c.req.valid('json').summary);\n opts.broadcast('comments-changed', {\n sessionId: id,\n submittedCount: result.drafts.length,\n });\n return c.json({\n count: result.drafts.length,\n comments: store.snapshot(),\n });\n },\n );\n\n app.post('/api/sessions/:id/discard-review', (c) => {\n const id = c.req.param('id');\n if (!requireSession(id)) return c.json({ error: 'unknown session' }, 404);\n const store = getCommentFileStore(id);\n const discarded = store.discardDrafts();\n if (discarded > 0) opts.broadcast('comments-changed', { sessionId: id });\n return c.json({ discarded, comments: store.snapshot() });\n });\n}\n\n/**\n * If `work web` owns a live PTY for this session (the user opened the\n * Terminal tab and Claude is running there), push the pending comments\n * directly to stdin so Claude sees them without the user typing anything.\n *\n * We only push for user-authored comments — Claude-authored ones are\n * replies we already routed via the API. We deliberately don't spawn a\n * PTY here (use `peekPty`, not `getOrCreatePty`) — pushing to a freshly\n * spawned Claude is weird, and the user expects to control when Claude\n * starts.\n */\nfunction deliverViaOwnedPty(sessionId: string, author: string): void {\n if (author !== 'user') return;\n if (!peekPty(sessionId)) return;\n\n const pending = readPendingForSession(sessionId);\n if (pending.length === 0) return;\n\n // peekPty returned true so this won't spawn — it'll return the existing\n // entry. We use getOrCreatePty because it's the only public way to get\n // the entry handle. (Could refactor to expose a pure peek that returns\n // the PooledPty, but not yet worth it.)\n const pty = getOrCreatePty(sessionId);\n if (!pty) return;\n\n const text = formatPendingForPrompt(pending);\n if (!text) return;\n\n // Mark delivered ONLY after a successful stdin write. If pty.write\n // throws (PTY exited mid-call, encoding error, anything) we leave the\n // comment as pending so the UserPromptSubmit / Stop hook can still\n // pick it up on the next Claude turn — better duplicate delivery than\n // silent loss. Writing the system reminder + newline so Claude treats\n // it as a submitted user prompt.\n try {\n pty.write(text + '\\n');\n } catch {\n return;\n }\n markDelivered(\n sessionId,\n pending.map((c) => c.id),\n );\n}\n","import { Hono } from 'hono';\nimport { zValidator } from '@hono/zod-validator';\nimport { z } from 'zod';\nimport { loadConfig } from './config.js';\nimport { fetchAllPullRequests, type PullRequestInfo } from './pr.js';\nimport { fetchJiraPane, type JiraIssue } from './jira.js';\nimport {\n addTask,\n completeTask,\n editTask,\n getTasks,\n removeTask,\n uncompleteTask,\n} from './tasks.js';\n\nexport interface PanesMountOptions {\n /** Server-level broadcast so mutations emit *-changed events. */\n broadcast: (event: string, data: unknown) => void;\n}\n\n/**\n * Hono sub-app exposing the read endpoints and the tasks-CRUD that drive\n * the dashboard's PRs / Jira / Tasks sidebars. Mirrors the data sources\n * the TUI already uses (`core/pr.ts`, `core/jira.ts`, `core/tasks.ts`)\n * one-to-one — no new logic, just a network surface.\n */\nexport function mountPanesRoutes(\n app: Hono,\n opts: PanesMountOptions,\n): void {\n // -- Projects ----------------------------------------------------------\n //\n // Used by the new-worktree modal's project picker. Lists configured\n // single repos and groups together; the client filters.\n app.get('/api/projects', (c) => {\n const config = loadConfig();\n if (!config) {\n return c.json({ singles: [], groups: [] });\n }\n const singles = Object.keys(config.repos).map((alias) => ({\n name: alias,\n kind: 'single' as const,\n path: config.repos[alias],\n }));\n const groups = Object.entries(config.groups).map(([name, aliases]) => ({\n name,\n kind: 'group' as const,\n members: aliases,\n }));\n return c.json({ singles, groups });\n });\n\n // -- PRs ---------------------------------------------------------------\n //\n // In-flight dedup: `gh pr list` is slow and the pane fires on a 60s\n // interval. A burst of refresh clicks (or interval + sessions-changed\n // racing) would otherwise spawn N × gh subprocesses concurrently. The\n // first concurrent request triggers the fetch; everyone else awaits\n // the same promise.\n let prsInFlight: Promise<{ prs: PullRequestInfo[] }> | null = null;\n app.get('/api/prs', async (c) => {\n const config = loadConfig();\n if (!config) return c.json({ prs: [] });\n if (!prsInFlight) {\n prsInFlight = (async () => {\n try {\n const map = await fetchAllPullRequests(config.repos);\n // Flatten: one entry per PR, with the resolved repo alias attached.\n const prs = Array.from(map.values()).flat();\n return { prs };\n } finally {\n prsInFlight = null;\n }\n })();\n }\n try {\n const result = await prsInFlight;\n return c.json(result);\n } catch (err) {\n // gh missing or unauthenticated — surface empty rather than 500;\n // the client renders a \"gh not available\" hint.\n return c.json({\n prs: [],\n error: (err as Error).message,\n available: false,\n });\n }\n });\n\n // -- Jira --------------------------------------------------------------\n //\n // Single `acli jira auth status` probe (combined with the issue search\n // inside fetchJiraPane) — replaces the previous two-call pattern. Also\n // dedups concurrent refreshes; `acli` can be slow.\n let jiraInFlight: Promise<{ available: boolean; issues: JiraIssue[] }> | null =\n null;\n app.get('/api/jira', async (c) => {\n if (!jiraInFlight) {\n jiraInFlight = (async () => {\n try {\n return await fetchJiraPane();\n } finally {\n jiraInFlight = null;\n }\n })();\n }\n try {\n const result = await jiraInFlight;\n return c.json(result);\n } catch (err) {\n return c.json({\n issues: [],\n available: false,\n error: (err as Error).message,\n });\n }\n });\n\n // -- Tasks -------------------------------------------------------------\n //\n // File-watched on the server side via the existing `tasks-changed`\n // broadcast (added below). Mutations both write through the\n // `core/tasks.ts` API and broadcast so other tabs refresh.\n\n app.get('/api/tasks', (c) => c.json({ tasks: getTasks() }));\n\n const newTaskSchema = z.object({\n text: z.string().min(1),\n link: z.string().optional(),\n });\n app.post(\n '/api/tasks',\n zValidator('json', newTaskSchema),\n async (c) => {\n const { text, link } = c.req.valid('json');\n const task = await addTask(text, link);\n opts.broadcast('tasks-changed', { id: task.id });\n return c.json({ task, tasks: getTasks() });\n },\n );\n\n const editSchema = z.object({\n text: z.string().min(1).optional(),\n done: z.boolean().optional(),\n });\n app.patch(\n '/api/tasks/:id',\n zValidator('json', editSchema),\n async (c) => {\n const id = Number(c.req.param('id'));\n if (!Number.isFinite(id)) {\n return c.json({ error: 'invalid id' }, 400);\n }\n const body = c.req.valid('json');\n let updated = null;\n if (typeof body.text === 'string') {\n updated = await editTask(id, body.text);\n }\n if (typeof body.done === 'boolean') {\n updated = body.done\n ? await completeTask(id)\n : await uncompleteTask(id);\n }\n if (!updated) return c.json({ error: 'not found' }, 404);\n opts.broadcast('tasks-changed', { id });\n return c.json({ task: updated, tasks: getTasks() });\n },\n );\n\n app.delete('/api/tasks/:id', async (c) => {\n const id = Number(c.req.param('id'));\n if (!Number.isFinite(id)) return c.json({ error: 'invalid id' }, 400);\n const removed = await removeTask(id);\n if (!removed) return c.json({ error: 'not found' }, 404);\n opts.broadcast('tasks-changed', { id });\n return c.json({ tasks: getTasks() });\n });\n}\n","import path from 'node:path';\nimport { spawn } from 'node:child_process';\nimport { Hono } from 'hono';\nimport { zValidator } from '@hono/zod-validator';\nimport { z } from 'zod';\nimport { loadConfig } from './config.js';\nimport { setupWorktree, teardownWorktree } from './worktree.js';\nimport { removeSession } from './history.js';\nimport { findSession, sessionIdFor } from './web-state.js';\nimport { git } from './git.js';\nimport { detectParentBranch } from './diff-scope.js';\n\nexport interface WorktreeMutOptions {\n broadcast: (event: string, data: unknown) => void;\n}\n\n/**\n * Hono sub-app exposing the worktree mutation surface that the dashboard\n * needs to reach parity with `work dash`:\n *\n * POST /api/worktrees — create (target + branch [+ base])\n * DELETE /api/sessions/:id/worktree — remove (with force flag)\n * POST /api/sessions/:id/sync — git fetch (+ pull where safe)\n * POST /api/sessions/:id/rebase — rebase on detected/recorded parent\n * POST /api/sessions/:id/open-editor — spawn `code <path>`\n *\n * All mutations broadcast `sessions-changed` so the SPA refetches and\n * the sidebar updates without a manual refresh.\n */\nexport function mountWorktreeRoutes(\n app: Hono,\n opts: WorktreeMutOptions,\n): void {\n // -- Create ------------------------------------------------------------\n const createSchema = z.object({\n target: z.string().min(1),\n branch: z.string().min(1),\n base: z.string().optional(),\n jiraKey: z.string().optional(),\n });\n app.post(\n '/api/worktrees',\n zValidator('json', createSchema),\n async (c) => {\n const { target, branch, base, jiraKey } = c.req.valid('json');\n const config = loadConfig();\n if (!config) return c.json({ error: 'no config' }, 400);\n\n try {\n const result = await setupWorktree(\n target,\n branch,\n config,\n base,\n jiraKey,\n );\n if (!result) {\n return c.json({ error: 'setup failed (target not found?)' }, 400);\n }\n opts.broadcast('sessions-changed', { ts: Date.now() });\n // Re-derive the new session id so the client can route to it\n // immediately. sessionIdFor takes a WorktreeSession, but we\n // have the same inputs — hash sha1(target+':'+branch).\n const id = sessionIdFor({\n target,\n isGroup: result.isGroup,\n branch,\n paths: result.paths,\n createdAt: '',\n lastAccessedAt: '',\n });\n return c.json({\n sessionId: id,\n launchDir: result.launchDir,\n paths: result.paths,\n });\n } catch (err) {\n return c.json({ error: (err as Error).message }, 500);\n }\n },\n );\n\n // -- Remove ------------------------------------------------------------\n const removeSchema = z.object({ force: z.boolean().optional() });\n app.delete(\n '/api/sessions/:id/worktree',\n zValidator('json', removeSchema),\n async (c) => {\n const id = c.req.param('id');\n const session = findSession(id);\n if (!session) return c.json({ error: 'unknown session' }, 404);\n const config = loadConfig();\n if (!config) return c.json({ error: 'no config' }, 400);\n\n const { force } = c.req.valid('json');\n try {\n const ok = teardownWorktree(\n session.target,\n session.isGroup,\n session.branch,\n config,\n force ?? false,\n );\n if (!ok) {\n return c.json(\n {\n error:\n 'remove blocked (uncommitted changes — pass force:true to override)',\n },\n 409,\n );\n }\n await removeSession(session.target, session.branch);\n opts.broadcast('sessions-changed', { ts: Date.now() });\n return c.json({ ok: true });\n } catch (err) {\n return c.json({ error: (err as Error).message }, 500);\n }\n },\n );\n\n // -- Sync (fetch + try to pull) ---------------------------------------\n app.post('/api/sessions/:id/sync', (c) => {\n const id = c.req.param('id');\n const session = findSession(id);\n if (!session) return c.json({ error: 'unknown session' }, 404);\n const results = session.paths.map((p) => {\n const fetch = git(['fetch', '--all', '--prune', '--quiet'], p);\n const pull = git(['pull', '--ff-only', '--quiet'], p);\n return {\n path: p,\n fetched: fetch.exitCode === 0,\n pulled: pull.exitCode === 0,\n pullError: pull.exitCode === 0 ? undefined : pull.stderr.trim(),\n };\n });\n opts.broadcast('sessions-changed', { ts: Date.now() });\n return c.json({ results });\n });\n\n // -- Rebase on parent --------------------------------------------------\n app.post('/api/sessions/:id/rebase', (c) => {\n const id = c.req.param('id');\n const session = findSession(id);\n if (!session) return c.json({ error: 'unknown session' }, 404);\n const results = session.paths.map((p) => {\n const parent = session.baseBranch ?? detectParentBranch(p);\n if (!parent) {\n return { path: p, ok: false, error: 'no parent branch detected' };\n }\n const r = git(['rebase', parent], p);\n return {\n path: p,\n ok: r.exitCode === 0,\n parent,\n error: r.exitCode === 0 ? undefined : r.stderr.trim(),\n };\n });\n opts.broadcast('sessions-changed', { ts: Date.now() });\n return c.json({ results });\n });\n\n // -- Open in editor ----------------------------------------------------\n app.post('/api/sessions/:id/open-editor', (c) => {\n const id = c.req.param('id');\n const session = findSession(id);\n if (!session) return c.json({ error: 'unknown session' }, 404);\n // Open the worktree (or group root) in VS Code. Detached + ignored\n // stdio so the spawn returns immediately and the parent doesn't\n // hold on to a zombie.\n const target = session.isGroup\n ? path.dirname(session.paths[0])\n : session.paths[0];\n try {\n // Resolve `code` vs `code.cmd` by platform instead of using\n // shell:true. shell:true routes through cmd.exe / sh -c, which\n // means any shell metacharacters in `target` (an `&`, a backtick,\n // an unescaped quote) would execute — a TOCTOU risk if anything\n // ever writes a malformed path into history.json.\n const cmd = process.platform === 'win32' ? 'code.cmd' : 'code';\n const child = spawn(cmd, [target], {\n detached: true,\n stdio: 'ignore',\n shell: false,\n });\n child.unref();\n return c.json({ ok: true, opened: target });\n } catch (err) {\n return c.json({ error: (err as Error).message }, 500);\n }\n });\n}\n","import { Hono } from 'hono';\nimport { zValidator } from '@hono/zod-validator';\nimport { z } from 'zod';\nimport { EventEmitter } from 'node:events';\nimport path from 'node:path';\nimport spawn from 'cross-spawn';\nimport { computeDiff, computeRangeDiff } from './diff-pipeline.js';\nimport { readContextLines } from './file-context.js';\nimport { resolveRepoDiff, sessionBaseForPath } from './diff-scope.js';\nimport { git } from './git.js';\nimport {\n clearCheckpoints,\n loadManifest,\n takeCheckpoint,\n type CheckpointEntry,\n} from './checkpoint.js';\nimport {\n commentStoreIdForScope,\n getScope,\n listScopes,\n markScopeEnded,\n registerScope,\n removeScope,\n ScopePathRejectedError,\n subscribeScope,\n} from './scope-manager.js';\nimport { getCommentFileStore } from './comment-file-store.js';\nimport { commentInputSchema, submitReviewSchema } from './comment-schemas.js';\nimport { streamSSE } from 'hono/streaming';\n\nexport interface ScopeMountOptions {\n /** Server-level broadcast. Scope events go here too. */\n broadcast: (event: string, data: unknown) => void;\n}\n\n\n/**\n * Hono sub-app exposing the per-scope diff + review surface that lets\n * `wd` and `wd -c` consolidate onto the `work web` server.\n *\n * Routes:\n * POST /api/scopes — register a scope; returns\n * `{ hash, diffUrl, reviewUrl }`\n * GET /api/scopes — list active scopes\n * DELETE /api/scopes/:hash — deregister\n * GET /api/scopes/:hash/diff?base=… — diff data (same shape as\n * session diff)\n * GET /api/scopes/:hash/comments — list comments\n * POST /api/scopes/:hash/comments — add (zod-validated)\n * DELETE /api/scopes/:hash/comments/:cid — remove\n * POST /api/scopes/:hash/submit-review — promote drafts\n * POST /api/scopes/:hash/discard-review — drop drafts\n * GET /api/scopes/:hash/events — SSE: diff-changed,\n * comments-changed\n */\nexport function mountScopeRoutes(app: Hono, opts: ScopeMountOptions): void {\n // -- Lifecycle -----------------------------------------------------------\n\n // Scopes that already have an auto-snapshot subscriber wired. The\n // register endpoint is idempotent (same paths → same hash), and we\n // only want one subscriber per scope no matter how many times `wd`\n // re-registers in the same `work web` lifetime.\n const checkpointWatched = new Set<string>();\n\n // Per-scope fingerprint of the last working-tree state we observed.\n // `git status --porcelain --no-renames -z` is dramatically cheaper\n // than the full snapshot path (`git add -A` against a temp index +\n // write-tree + commit-tree + update-ref ≈ 6 spawns per repo) — when\n // a save touches an `.gitignore`'d file or doesn't actually change\n // content (editor rewriting the same bytes), the status output is\n // unchanged and we can skip the whole snapshot pipeline.\n const lastStatus = new Map<string, string>();\n\n function workingTreeFingerprint(paths: string[]): string {\n const parts: string[] = [];\n for (const p of paths) {\n const r = spawn.sync(\n 'git',\n ['status', '--porcelain', '--no-renames', '-z'],\n { cwd: p, encoding: 'utf-8', windowsHide: true },\n );\n parts.push(r.stdout ?? '');\n }\n return parts.join('\\0|\\0');\n }\n\n // In-process bus for checkpoint events — lets the per-scope SSE handler\n // forward `checkpoints-changed` to subscribers of a single scope's\n // stream. Server-level `/events` still gets the same event via\n // `opts.broadcast`; both paths are needed because the SPA chooses one\n // stream or the other depending on whether it's mounted via `work web`\n // or running standalone.\n const scopeBus = new EventEmitter();\n scopeBus.setMaxListeners(0);\n\n /** Build the repo list for `takeCheckpoint`. The `name` field is the\n * manifest key — must be unique within a scope, otherwise two repos\n * with the same basename in a group worktree would overwrite each\n * other's SHA in the entry. Using the full resolved path guarantees\n * uniqueness (paths in a scope are already de-duplicated by\n * `registerScope`'s sort+normalise). */\n function scopeRepos(scopePaths: string[]) {\n return scopePaths.map((p) => ({ name: p, root: p }));\n }\n\n app.post(\n '/api/scopes',\n zValidator(\n 'json',\n z.object({\n paths: z.array(z.string().min(1)).min(1),\n label: z.string().optional(),\n }),\n ),\n (c) => {\n const { paths, label } = c.req.valid('json');\n try {\n const scope = registerScope(paths, label);\n opts.broadcast('scopes-changed', { hash: scope.hash });\n\n const announceCheckpoint = (entry: CheckpointEntry) => {\n const payload = { scopeHash: scope.hash, id: entry.id };\n opts.broadcast('checkpoints-changed', payload);\n scopeBus.emit('checkpoints-changed', payload);\n };\n\n // Initial snapshot — establishes the \"Initial\" checkpoint that\n // every subsequent fs-change snapshot diffs against. Fire and\n // forget: `git add -A` against a large untracked tree can take\n // hundreds of ms, and the `wd` foreground caller is waiting on\n // this response to open the browser. The SPA fetches checkpoints\n // separately and will see the initial entry once it lands (or\n // via the `checkpoints-changed` SSE event below).\n if (loadManifest(scope.hash).entries.length === 0) {\n takeCheckpoint(scope.hash, scopeRepos(scope.paths))\n .then((entry) => {\n if (entry) announceCheckpoint(entry);\n })\n .catch((err) => {\n // Best-effort, but DO surface persistent failures (disk\n // full on `~/.work/diffs/`, stale lockfile, git permission\n // denied). `installConsoleLogger` mirrors console.error\n // into `~/.work/debug.log`, giving the user a diagnostic\n // trail even though we don't fail the request.\n console.error('[checkpoint] initial snapshot failed:', err);\n });\n }\n\n // Wire an auto-snapshot subscriber that fires on every debounced\n // fs change for this scope. A cheap `git status` fingerprint\n // gate short-circuits before the expensive snapshot pipeline\n // when the working tree hasn't moved — common when editor\n // autosave rewrites a file with identical content, or when\n // chokidar fires for an `.gitignore`'d path that git wouldn't\n // capture anyway.\n if (!checkpointWatched.has(scope.hash)) {\n checkpointWatched.add(scope.hash);\n subscribeScope(scope.hash, () => {\n try {\n const fp = workingTreeFingerprint(scope.paths);\n if (lastStatus.get(scope.hash) === fp) return;\n lastStatus.set(scope.hash, fp);\n } catch {\n // Fingerprint failure shouldn't block the snapshot — fall\n // through to takeCheckpoint and let its own logic decide.\n }\n takeCheckpoint(scope.hash, scopeRepos(scope.paths))\n .then((entry) => {\n if (entry) announceCheckpoint(entry);\n })\n .catch((err) => {\n console.error('[checkpoint] auto-snapshot failed:', err);\n });\n });\n }\n\n return c.json({\n hash: scope.hash,\n diffUrl: `/diff/${scope.hash}`,\n reviewUrl: `/review/${scope.hash}`,\n });\n } catch (err) {\n if (err instanceof ScopePathRejectedError) {\n return c.json({ error: err.message, rejected: err.rejected }, 403);\n }\n throw err;\n }\n },\n );\n\n app.get('/api/scopes', (c) => c.json({ scopes: listScopes() }));\n\n app.delete('/api/scopes/:hash', (c) => {\n const hash = c.req.param('hash');\n // Capture the paths BEFORE removeScope — the scope-manager entry\n // is gone after that, and clearCheckpoints needs the repo roots\n // to delete each `refs/wd/<hash>/<n>` ref. Without this cleanup,\n // refs + the manifest leak forever: git GC can't reclaim the\n // commits behind a named ref, and a re-register with the same\n // paths would skip the Initial snapshot because the stale\n // manifest still has entries.\n const scope = getScope(hash);\n const repoPaths = scope ? [...scope.paths] : [];\n const ok = removeScope(hash);\n // `removeScope` clears the scope's fs-watch subscribers; drop our\n // record of having wired one so a re-register of the same paths\n // rewires the auto-snapshot subscriber instead of silently doing\n // nothing.\n checkpointWatched.delete(hash);\n lastStatus.delete(hash);\n if (repoPaths.length > 0) clearCheckpoints(hash, repoPaths);\n if (ok) opts.broadcast('scopes-changed', { hash });\n return c.json({ ok });\n });\n\n // -- Diff ----------------------------------------------------------------\n\n /** Parse \"0\", \"1\", ... as a numeric id; everything else as undefined.\n * The literal \"working\" stays as the sentinel — only meaningful for\n * the `to` parameter. */\n function parseCheckpointParam(\n raw: string | undefined,\n ): number | 'working' | undefined {\n if (raw === undefined || raw === '') return undefined;\n if (raw === 'working') return 'working';\n const n = Number(raw);\n if (Number.isInteger(n) && n >= 0) return n;\n return undefined;\n }\n\n app.get('/api/scopes/:hash/diff', (c) => {\n const scope = getScope(c.req.param('hash'));\n if (!scope) return c.json({ error: 'unknown scope' }, 404);\n const base = c.req.query('base') === 'branch' ? 'branch' : 'uncommitted';\n const fromParam = parseCheckpointParam(c.req.query('from'));\n const toParam = parseCheckpointParam(c.req.query('to'));\n try {\n // `from='working'` is meaningless — the working tree only makes\n // sense as the right endpoint. Reject explicitly instead of\n // silently falling through to legacy mode and serving a HEAD-vs-\n // working diff that looks like the requested range.\n if (fromParam === 'working') {\n return c.json(\n { error: \"'working' is not valid as a from-checkpoint\" },\n 400,\n );\n }\n // Checkpoint-range mode: ignore `base`, look up commits from the\n // manifest, run computeRangeDiff per repo. Either endpoint can be\n // an id or (for `to`) 'working'. Missing checkpoint id for a repo\n // → fall back to HEAD (treats that repo as if there's no snapshot).\n if (fromParam !== undefined) {\n const manifest = loadManifest(scope.hash);\n const fromEntry = manifest.entries.find((e) => e.id === fromParam);\n if (!fromEntry) {\n return c.json({ error: `unknown from checkpoint ${fromParam}` }, 400);\n }\n let toEntry: CheckpointEntry | undefined;\n if (toParam !== undefined && toParam !== 'working') {\n toEntry = manifest.entries.find((e) => e.id === toParam);\n if (!toEntry) {\n return c.json({ error: `unknown to checkpoint ${toParam}` }, 400);\n }\n // Reject reversed ranges. `git diff <toSha> <fromSha>` produces\n // an inverted diff (adds look like deletes) — clearly wrong but\n // would still return 200. Surface the misuse so callers (the\n // SPA or curl users) get a clear error instead of confusing\n // output.\n if (toEntry.id < fromEntry.id) {\n return c.json(\n { error: `to (${toEntry.id}) must be >= from (${fromEntry.id})` },\n 400,\n );\n }\n }\n const repos = scope.paths.map((p) => {\n // Manifest is keyed by full path (see `scopeRepos`). The\n // response's `name` is still the basename — it's the user-\n // visible repo tab label and doesn't need to be unique-by-key.\n const fromSha = fromEntry.repos[p] ?? 'HEAD';\n const toSha: string | 'working' =\n toEntry === undefined ? 'working' : (toEntry.repos[p] ?? 'HEAD');\n return {\n name: path.basename(p),\n root: p,\n files: computeRangeDiff({ root: p, fromRef: fromSha, toRef: toSha }),\n };\n });\n return c.json({\n scopeHash: scope.hash,\n base: 'uncommitted',\n resolvedBase: `checkpoint-${fromEntry.id}`,\n from: fromEntry.id,\n to: toEntry?.id ?? 'working',\n repos,\n });\n }\n\n // Resolve each repo independently — they may have different parent\n // branches in a group worktree. Each entry carries its own\n // `resolvedBase` so the UI can label per-repo if it wants to. The\n // recorded fork point (`work tree --base`) wins over auto-detection,\n // per-repo — so a group forked `backend=dev frontend=feat/x` diffs\n // each repo against its own base.\n const resolved = scope.paths.map((p) =>\n resolveRepoDiff(p, base, sessionBaseForPath(p)),\n );\n const repos = scope.paths.map((p, i) => ({\n name: path.basename(p),\n root: p,\n resolvedBase: resolved[i].resolvedBase,\n files: computeDiff({ root: p, diffArg: resolved[i].diffArg }),\n }));\n // Top-level `resolvedBase` is the primary repo's value — used for\n // the single-line \"vs X\" badge in the sidebar header. Mirrors what\n // session-diff returns from web-server.ts.\n const resolvedBase = resolved[0]?.resolvedBase ?? 'HEAD';\n // Current branch of the primary repo, for the \"<branch> vs <base>\"\n // title. In work-web mode the SPA synthesizes its context from the\n // URL hash (no headBranch), so it reads this off the diff instead.\n const head = git(['rev-parse', '--abbrev-ref', 'HEAD'], scope.paths[0]);\n const headBranch =\n head.exitCode === 0 && head.stdout && head.stdout !== 'HEAD'\n ? head.stdout\n : undefined;\n return c.json({\n scopeHash: scope.hash,\n base,\n resolvedBase,\n headBranch,\n repos,\n });\n } catch (err) {\n return c.json({ error: (err as Error).message }, 500);\n }\n });\n\n // -- Expand context ------------------------------------------------------\n //\n // Reveals the unchanged lines around a hunk (\"expand lines\" in the SPA).\n // The expandable region is identical on both diff sides, so the client\n // reads one side and maps old line numbers via the gap offset. `ref` is\n // omitted for the common working-tree case; supplied (a checkpoint sha)\n // when the diff's new side is a committed snapshot.\n app.get('/api/scopes/:hash/file-lines', (c) => {\n const scope = getScope(c.req.param('hash'));\n if (!scope) return c.json({ error: 'unknown scope' }, 404);\n const repoName = c.req.query('repo') ?? '';\n const relPath = c.req.query('path') ?? '';\n const start = Number(c.req.query('start'));\n const end = Number(c.req.query('end'));\n const ref = c.req.query('ref') || undefined;\n if (!relPath || !Number.isInteger(start) || !Number.isInteger(end)) {\n return c.json({ error: 'bad path/start/end' }, 400);\n }\n // Resolve the repo root. Single-repo scopes have exactly one path;\n // group scopes key each repo's response `name` by basename, so match\n // the same way the diff route labels them.\n const root =\n scope.paths.length === 1\n ? scope.paths[0]\n : scope.paths.find((p) => path.basename(p) === repoName);\n if (!root) return c.json({ error: 'unknown repo' }, 404);\n const result = readContextLines({ root, relPath, start, end, ref });\n if (!result) return c.json({ error: 'cannot read file' }, 400);\n return c.json(result);\n });\n\n // -- Checkpoints ---------------------------------------------------------\n\n app.get('/api/scopes/:hash/checkpoints', (c) => {\n const scope = getScope(c.req.param('hash'));\n if (!scope) return c.json({ error: 'unknown scope' }, 404);\n const manifest = loadManifest(scope.hash);\n return c.json({\n scopeHash: scope.hash,\n entries: manifest.entries,\n });\n });\n\n // -- Comments ------------------------------------------------------------\n\n function commentStore(hash: string) {\n return getCommentFileStore(commentStoreIdForScope(hash));\n }\n\n app.get('/api/scopes/:hash/comments', (c) => {\n const scope = getScope(c.req.param('hash'));\n if (!scope) return c.json({ error: 'unknown scope' }, 404);\n return c.json({\n comments: commentStore(scope.hash).snapshot(),\n ended: scope.ended,\n });\n });\n\n app.post(\n '/api/scopes/:hash/comments',\n zValidator('json', commentInputSchema),\n (c) => {\n const scope = getScope(c.req.param('hash'));\n if (!scope) return c.json({ error: 'unknown scope' }, 404);\n const store = commentStore(scope.hash);\n try {\n const comment = store.post(c.req.valid('json'));\n opts.broadcast('comments-changed', {\n scopeHash: scope.hash,\n id: comment.id,\n });\n return c.json({ comment, comments: store.snapshot() });\n } catch (err) {\n return c.json({ error: (err as Error).message }, 400);\n }\n },\n );\n\n app.delete('/api/scopes/:hash/comments/:cid', (c) => {\n const scope = getScope(c.req.param('hash'));\n if (!scope) return c.json({ error: 'unknown scope' }, 404);\n const store = commentStore(scope.hash);\n const removed = store.remove(c.req.param('cid'));\n if (removed) {\n opts.broadcast('comments-changed', {\n scopeHash: scope.hash,\n deleted: c.req.param('cid'),\n });\n }\n return c.json({ comments: store.snapshot() });\n });\n\n app.post(\n '/api/scopes/:hash/submit-review',\n zValidator('json', submitReviewSchema),\n (c) => {\n const scope = getScope(c.req.param('hash'));\n if (!scope) return c.json({ error: 'unknown scope' }, 404);\n const store = commentStore(scope.hash);\n const result = store.submit(c.req.valid('json').summary);\n opts.broadcast('comments-changed', {\n scopeHash: scope.hash,\n submittedCount: result.drafts.length,\n });\n return c.json({\n count: result.drafts.length,\n comments: store.snapshot(),\n });\n },\n );\n\n app.post('/api/scopes/:hash/discard-review', (c) => {\n const scope = getScope(c.req.param('hash'));\n if (!scope) return c.json({ error: 'unknown scope' }, 404);\n const store = commentStore(scope.hash);\n const discarded = store.discardDrafts();\n if (discarded > 0) {\n opts.broadcast('comments-changed', { scopeHash: scope.hash });\n }\n return c.json({ discarded, comments: store.snapshot() });\n });\n\n // End Review button hits this. Sets the scope's `ended` flag so the\n // `wd -c` CLI proxy can emit `--- review done ---` and exit on the\n // next poll. Scope itself stays alive — the browser tab keeps\n // working, reloading the URL keeps working.\n app.post('/api/scopes/:hash/done', (c) => {\n const scope = getScope(c.req.param('hash'));\n if (!scope) return c.json({ error: 'unknown scope' }, 404);\n const store = commentStore(scope.hash);\n const count = store.list().length;\n markScopeEnded(scope.hash);\n opts.broadcast('review-done', { scopeHash: scope.hash, count });\n return c.json({ ok: true, count });\n });\n\n // -- Per-scope SSE -------------------------------------------------------\n //\n // The shared /events stream already broadcasts global events; this\n // endpoint adds a scope-narrowed stream so the `wd -c` CLI can tail\n // *just* its scope's comments without filtering at the client.\n\n app.get('/api/scopes/:hash/events', (c) => {\n const scope = getScope(c.req.param('hash'));\n if (!scope) return c.json({ error: 'unknown scope' }, 404);\n return streamSSE(c, async (stream) => {\n const unsubscribe = subscribeScope(scope.hash, () => {\n stream\n .writeSSE({\n event: 'diff-changed',\n data: JSON.stringify({ scopeHash: scope.hash }),\n })\n .catch(() => { /* */ });\n });\n // Relay checkpoint events for THIS scope only. The auto-snapshot\n // subscriber emits to `scopeBus` whenever a new checkpoint passes\n // the dedup, so the SPA's strip refreshes without polling.\n const onCheckpoint = (payload: { scopeHash: string; id: number }) => {\n if (payload.scopeHash !== scope.hash) return;\n stream\n .writeSSE({\n event: 'checkpoints-changed',\n data: JSON.stringify(payload),\n })\n .catch(() => { /* */ });\n };\n scopeBus.on('checkpoints-changed', onCheckpoint);\n await stream.writeSSE({ event: 'connected', data: '' });\n await new Promise<void>((resolve) => {\n stream.onAbort(() => {\n unsubscribe?.();\n scopeBus.off('checkpoints-changed', onCheckpoint);\n resolve();\n });\n });\n });\n });\n}\n","/**\n * Per-scope diff checkpoints.\n *\n * A \"checkpoint\" is a snapshot of every repo in a scope's working tree\n * (including untracked files) captured as a real git commit and held alive\n * by `refs/wd/<scope-hash>/<n>` in each repo. A small JSON manifest at\n * `~/.work/diffs/<scope-hash>.checkpoints.json` records the per-checkpoint\n * timestamp + repo→sha mapping so the SPA can list checkpoints and ask for\n * a diff between any two (or between one and the live working tree).\n *\n * Snapshots are taken automatically:\n * - once when a scope is first registered (the \"Initial\" point). This\n * baseline captures HEAD's tree, NOT the working tree — so a range of\n * \"Initial → working\" equals the full uncommitted diff (`git diff HEAD`\n * plus untracked), and any pre-existing uncommitted work the user\n * already had when `wd` launched stays visible instead of being baked\n * into an invisible baseline.\n * - again whenever the scope's fs-watch debounce fires AND the working\n * tree differs from the previous snapshot (the dedup keeps idle saves\n * from spawning empty checkpoints)\n *\n * The capture mechanism uses a temp git index file so the real index is\n * never disturbed:\n *\n * GIT_INDEX_FILE=<tmp> git read-tree HEAD\n * GIT_INDEX_FILE=<tmp> git add -A\n * GIT_INDEX_FILE=<tmp> git write-tree → <tree-sha>\n * git commit-tree <tree-sha> -p HEAD -m \"wd checkpoint\" → <commit-sha>\n * git update-ref refs/wd/<hash>/<n> <commit-sha>\n *\n * Untracked files end up in the snapshot because `git add -A` against the\n * temp index promotes them. `.gitignore` is honoured (same as the diff\n * pipeline's untracked detection).\n */\n\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport spawn from 'cross-spawn';\nimport { atomicWriteFile, ensureFile, withFileLock } from './fs-safe.js';\nimport { writeTempTree } from './git-tree-snapshot.js';\n\nexport interface CheckpointEntry {\n /** Monotonic per-scope sequence id, starting at 0 for the initial\n * snapshot. */\n id: number;\n /** ISO timestamp of capture. */\n ts: string;\n /** Human label — \"Initial\" for id 0, otherwise empty (the SPA shows\n * \"#1\", \"#2\", ...). Reserved for future user-labelled checkpoints. */\n label?: string;\n /** Per-repo commit sha captured. Keyed by repo name (the same `name`\n * used in `RepoData`). When a repo has no head yet (fresh repo, no\n * commits), the value is null — diff ranges that touch it on the\n * \"from\" side fall back to the empty tree. */\n repos: Record<string, string | null>;\n}\n\nexport interface CheckpointManifest {\n version: 1;\n scopeHash: string;\n entries: CheckpointEntry[];\n}\n\n/** Where this scope's manifest lives on disk. */\nexport function manifestPath(scopeHash: string): string {\n const dir = path.join(os.homedir(), '.work', 'diffs');\n fs.mkdirSync(dir, { recursive: true });\n return path.join(dir, `${scopeHash}.checkpoints.json`);\n}\n\nfunction emptyManifest(scopeHash: string): CheckpointManifest {\n return { version: 1, scopeHash, entries: [] };\n}\n\n/** Read manifest from disk (returns an empty one if the file is missing\n * or unparseable). Never throws. */\nexport function loadManifest(scopeHash: string): CheckpointManifest {\n const file = manifestPath(scopeHash);\n if (!fs.existsSync(file)) return emptyManifest(scopeHash);\n try {\n const raw = fs.readFileSync(file, 'utf-8');\n const parsed = JSON.parse(raw) as Partial<CheckpointManifest>;\n if (parsed.version !== 1 || !Array.isArray(parsed.entries)) {\n return emptyManifest(scopeHash);\n }\n return {\n version: 1,\n scopeHash,\n entries: parsed.entries as CheckpointEntry[],\n };\n } catch {\n return emptyManifest(scopeHash);\n }\n}\n\n// (Previously a separate `appendCheckpoint` lived here. It was folded\n// into `takeCheckpoint` so id assignment + snapshot + ref write + manifest\n// append all happen under a single lock — concurrent fs-watch fires across\n// the same scope must not race on `nextId`.)\n\n/**\n * Capture a single repo as a commit and return its sha. Updates\n * `refs/wd/<scopeHash>/<id>` to point at the commit so git GC can't\n * reclaim it. Returns null on failure (caller should skip this repo).\n *\n * `includeWorkingTree` (default true) controls what tree is captured:\n * - true — the working tree, incl. untracked files (subject to\n * .gitignore). This is the normal per-change snapshot.\n * - false — HEAD's tree verbatim (no `git add -A`). Used for the\n * \"Initial\" baseline so a diff of Initial → working reproduces\n * the full uncommitted diff rather than hiding pre-existing\n * changes inside the baseline. On a repo with no commits this\n * yields the empty tree (everything shows as added).\n *\n * Internally uses a temp `GIT_INDEX_FILE` so the user's real index is\n * untouched. The temp file is unlinked on success and on most failures.\n */\nexport function snapshotRepo(\n repoRoot: string,\n scopeHash: string,\n id: number,\n includeWorkingTree = true,\n): string | null {\n // Build the tree (HEAD baseline, or full working tree) via the shared\n // temp-index helper — same dance `diff-pipeline.ts` uses to diff against\n // a checkpoint.\n const tree = writeTempTree(repoRoot, { includeWorkingTree });\n if (!tree) return null;\n const { treeSha, headSha } = tree;\n\n // commit-tree doesn't need the temp index. Use a fixed author/committer +\n // date so two snapshots of identical content produce the SAME commit sha\n // (the tree sha is already identical; matching commit shas help dedup).\n // The dedup path in `takeCheckpoint` relies on this: when nothing changed\n // between captures, every repo's recomputed sha matches the previous entry\n // and the new manifest row is discarded. (The id isn't in the message; it\n // lives in the ref name + manifest entry.)\n const commitArgs = ['commit-tree', treeSha, '-m', 'wd checkpoint'];\n if (headSha) commitArgs.push('-p', headSha);\n const commitEnv = {\n ...process.env,\n GIT_AUTHOR_NAME: 'wd',\n GIT_AUTHOR_EMAIL: 'wd@local',\n GIT_AUTHOR_DATE: '2000-01-01T00:00:00Z',\n GIT_COMMITTER_NAME: 'wd',\n GIT_COMMITTER_EMAIL: 'wd@local',\n GIT_COMMITTER_DATE: '2000-01-01T00:00:00Z',\n };\n const commit = spawn.sync('git', commitArgs, {\n cwd: repoRoot,\n encoding: 'utf-8',\n env: commitEnv,\n windowsHide: true,\n });\n if (commit.status !== 0 || !commit.stdout) return null;\n const commitSha = commit.stdout.trim();\n\n const refName = `refs/wd/${scopeHash}/${id}`;\n const updateRef = spawn.sync('git', ['update-ref', refName, commitSha], {\n cwd: repoRoot,\n encoding: 'utf-8',\n windowsHide: true,\n });\n if (updateRef.status !== 0) return null;\n\n return commitSha;\n}\n\nexport interface ScopeRepo {\n name: string;\n root: string;\n}\n\n/**\n * Take one checkpoint across every repo in a scope. Returns the appended\n * entry, or null when the snapshot is identical to the previous entry's\n * (every repo's commit sha matches — i.e. nothing changed). The caller\n * uses the null return to skip the SSE broadcast.\n *\n * When this is the first checkpoint for the scope, `label` defaults to\n * \"Initial\" so the SPA can render it specially.\n */\nexport async function takeCheckpoint(\n scopeHash: string,\n repos: ScopeRepo[],\n opts: { force?: boolean; label?: string } = {},\n): Promise<CheckpointEntry | null> {\n // Entire flow runs under one file lock so concurrent fs-watch fires\n // (or a register-handler racing with a debounced auto-snapshot) can't\n // assign duplicate ids or interleave manifest writes. Snapshot work\n // itself is git-side-effect-safe to concurrent — git's index/ref\n // locking handles same-repo overlap, and our temp `GIT_INDEX_FILE`\n // means the user's real index is untouched either way. We only need\n // the file lock to serialise the *id assignment + ref write + append*\n // triple, which has to be atomic across processes.\n const file = manifestPath(scopeHash);\n ensureFile(file, JSON.stringify(emptyManifest(scopeHash), null, 2));\n return withFileLock(file, () => {\n const manifest = loadManifest(scopeHash);\n const isFirst = manifest.entries.length === 0;\n const nextId = isFirst\n ? 0\n : manifest.entries[manifest.entries.length - 1].id + 1;\n\n // The first checkpoint baselines HEAD (not the working tree) so\n // pre-existing uncommitted work stays visible in an \"Initial → working\"\n // range. Every subsequent checkpoint captures the working tree.\n const captured: Record<string, string | null> = {};\n for (const repo of repos) {\n captured[repo.name] = snapshotRepo(\n repo.root,\n scopeHash,\n nextId,\n !isFirst,\n );\n }\n\n // Delete `refs/wd/<hash>/<id>` in every listed repo. Used by both\n // the partial-failure bailout and the dedup-cancel path —\n // `update-ref -d` is a no-op on a missing ref, so the same helper\n // covers \"rollback what we wrote\" and \"rollback what we might\n // have written\" without needing per-repo state.\n const rollbackRefs = () => {\n const refName = `refs/wd/${scopeHash}/${nextId}`;\n for (const repo of repos) {\n spawn.sync('git', ['update-ref', '-d', refName], {\n cwd: repo.root,\n encoding: 'utf-8',\n windowsHide: true,\n });\n }\n };\n\n // If any repo snapshot failed (returned null), bail — a partial\n // entry would let range diffs against this id silently produce\n // empty diffs for the failed repos (via the `?? 'HEAD'` fallback\n // in scope-routes) instead of surfacing the problem. The next\n // fs-event will retry.\n if (repos.some((r) => captured[r.name] === null)) {\n rollbackRefs();\n return null;\n }\n\n // Dedup: when not forced, compare against the previous entry by\n // TREE sha rather than commit sha. Two captures of the same working\n // tree produce identical trees but can have different commit shas\n // (different parent because HEAD moved, different `-p` argument\n // when one capture saw an empty HEAD). Compare-by-tree means we\n // drop checkpoints that didn't actually change any content —\n // exactly what the user expects from \"no changes since last snapshot\".\n if (!opts.force && !isFirst) {\n const prev = manifest.entries[manifest.entries.length - 1];\n const treeOf = (root: string, commitSha: string | null): string | null => {\n if (!commitSha) return null;\n const r = spawn.sync(\n 'git',\n ['rev-parse', `${commitSha}^{tree}`],\n { cwd: root, encoding: 'utf-8', windowsHide: true },\n );\n if (r.status !== 0 || typeof r.stdout !== 'string') return null;\n return r.stdout.trim() || null;\n };\n const allTreesMatch = repos.every((repo) => {\n const curTree = treeOf(repo.root, captured[repo.name]);\n const prevTree = treeOf(repo.root, prev.repos[repo.name] ?? null);\n return curTree !== null && curTree === prevTree;\n });\n if (allTreesMatch) {\n rollbackRefs();\n return null;\n }\n }\n\n const entry: CheckpointEntry = {\n id: nextId,\n ts: new Date().toISOString(),\n repos: captured,\n };\n const label = opts.label ?? (isFirst ? 'Initial' : undefined);\n if (label) entry.label = label;\n manifest.entries.push(entry);\n atomicWriteFile(file, JSON.stringify(manifest, null, 2));\n return entry;\n });\n}\n\n/** Remove the manifest + every ref for this scope. Called when a scope\n * is explicitly torn down. Best-effort — partial failure leaves orphaned\n * refs but doesn't otherwise corrupt state. */\nexport function clearCheckpoints(\n scopeHash: string,\n repoRoots: string[],\n): void {\n const manifest = loadManifest(scopeHash);\n for (const root of repoRoots) {\n for (const entry of manifest.entries) {\n const refName = `refs/wd/${scopeHash}/${entry.id}`;\n spawn.sync('git', ['update-ref', '-d', refName], {\n cwd: root,\n encoding: 'utf-8',\n windowsHide: true,\n });\n }\n }\n const file = manifestPath(scopeHash);\n if (fs.existsSync(file)) {\n try {\n fs.unlinkSync(file);\n } catch {\n // Leave it — next register will overwrite.\n }\n }\n}\n","/**\n * Stage 1 of the migration to \"one server per machine.\"\n *\n * A scope is an ad-hoc registration from a `wd` (or `wd -c`) invocation:\n * a label and a list of repo roots. Identified by the same sha1-of-roots\n * hash that `stableDiffPath` produces, so the same directory always gets\n * the same scope id across CLI invocations.\n *\n * Scopes are in-memory only — registering one doesn't touch disk. The\n * file watcher and comment store are per-scope, lazily started on first\n * access and reused thereafter.\n *\n * This module is mounted into `work web` so multiple `wd` invocations\n * share a single server process. Eventually `wd` itself will become a\n * thin client that registers a scope and opens a URL.\n */\n\nimport path from 'node:path';\nimport crypto from 'node:crypto';\nimport { createFsWatcher, type FsWatcher } from './fs-watcher.js';\nimport { loadConfig } from './config.js';\n\nexport interface Scope {\n /** Stable hash — same as `stableDiffPath` would emit. */\n hash: string;\n /** Repo roots this scope covers. One for a single-repo worktree,\n * many for a group. */\n paths: string[];\n /** Optional human label (e.g. \"work-tree · feat/x\"). */\n label: string;\n /** When the scope was first registered. */\n createdAt: string;\n /** Set when the user clicks \"End Review\" in the browser. The scope\n * itself stays alive (browser tab still works) — this is just a\n * signal so the `wd -c` CLI proxy knows to emit\n * `--- review done ---` and exit. */\n ended: boolean;\n}\n\ninterface ScopeEntry {\n scope: Scope;\n watcher: FsWatcher | null;\n subscribers: Set<() => void>;\n}\n\nconst scopes = new Map<string, ScopeEntry>();\n\nfunction hashFor(paths: string[]): string {\n const key = paths.slice().sort().join('|');\n return crypto.createHash('sha1').update(key).digest('hex').slice(0, 12);\n}\n\n/** Thrown by registerScope when the requested paths aren't inside any\n * configured repo or under the configured worktrees root. */\nexport class ScopePathRejectedError extends Error {\n constructor(public rejected: string[]) {\n super(\n `paths not allowed: ${rejected.join(', ')} (must be inside a configured repo or worktreesRoot)`,\n );\n this.name = 'ScopePathRejectedError';\n }\n}\n\nfunction normaliseForCompare(p: string): string {\n return path.resolve(p).replace(/\\\\/g, '/').toLowerCase();\n}\n\n/**\n * Reject paths that aren't inside any configured repo or under the\n * configured `worktreesRoot`. The work web server binds to 127.0.0.1\n * only, but any local process can still POST `/api/scopes` with an\n * arbitrary filesystem path — without this check a malicious npm\n * package could cause `git` and chokidar to operate on\n * `C:\\Windows\\System32` or the user's home root.\n *\n * Returns the rejected subset (empty array when everything's allowed).\n */\nfunction rejectedPaths(normalised: string[]): string[] {\n const config = loadConfig();\n if (!config) {\n // No config means no allowlist to check against. Fall back to\n // permissive — `work init` hasn't been run yet, and rejecting\n // everything would break first-run flows.\n return [];\n }\n const allowed = [\n ...Object.values(config.repos),\n ...(config.worktreesRoot ? [config.worktreesRoot] : []),\n ].map(normaliseForCompare);\n if (allowed.length === 0) return [];\n return normalised.filter((p) => {\n const np = normaliseForCompare(p);\n return !allowed.some((a) => np === a || np.startsWith(a + '/'));\n });\n}\n\n/** Register a scope (idempotent). Returns the resolved entry — same call\n * twice with the same paths gets the same hash and the same entry.\n * Throws `ScopePathRejectedError` when any path is outside the configured\n * repos / worktrees root. */\nexport function registerScope(paths: string[], label?: string): Scope {\n const normalised = paths.map((p) => path.resolve(p));\n const rejected = rejectedPaths(normalised);\n if (rejected.length > 0) throw new ScopePathRejectedError(rejected);\n const hash = hashFor(normalised);\n const existing = scopes.get(hash);\n if (existing) {\n if (label && existing.scope.label !== label) {\n existing.scope.label = label;\n }\n return existing.scope;\n }\n const scope: Scope = {\n hash,\n paths: normalised,\n label: label ?? path.basename(normalised[0]),\n createdAt: new Date().toISOString(),\n ended: false,\n };\n scopes.set(hash, { scope, watcher: null, subscribers: new Set() });\n return scope;\n}\n\n/** Mark a scope as ended (the user clicked \"End Review\"). Idempotent.\n * Does NOT remove the scope — the URL stays viewable. */\nexport function markScopeEnded(hash: string): boolean {\n const entry = scopes.get(hash);\n if (!entry) return false;\n if (entry.scope.ended) return false;\n entry.scope.ended = true;\n return true;\n}\n\nexport function getScope(hash: string): Scope | null {\n return scopes.get(hash)?.scope ?? null;\n}\n\nexport function listScopes(): Scope[] {\n return Array.from(scopes.values()).map((e) => e.scope);\n}\n\nexport function removeScope(hash: string): boolean {\n const entry = scopes.get(hash);\n if (!entry) return false;\n entry.watcher?.stop();\n entry.subscribers.clear();\n scopes.delete(hash);\n return true;\n}\n\n/**\n * Subscribe to file-change events for a scope. Lazy-starts the\n * chokidar watcher on first subscribe; tears it down when the last\n * subscriber unsubscribes. Same lifecycle as `web-state.subscribeSession`.\n *\n * The callback fires once per debounced fs event burst.\n */\nexport function subscribeScope(\n hash: string,\n cb: () => void,\n): (() => void) | null {\n const entry = scopes.get(hash);\n if (!entry) return null;\n entry.subscribers.add(cb);\n if (!entry.watcher) {\n entry.watcher = createFsWatcher({\n roots: entry.scope.paths,\n debounceMs: 150,\n onChange: () => {\n for (const sub of entry.subscribers) {\n try { sub(); } catch { /* */ }\n }\n },\n });\n }\n return () => {\n entry.subscribers.delete(cb);\n if (entry.subscribers.size === 0 && entry.watcher) {\n entry.watcher.stop();\n entry.watcher = null;\n }\n };\n}\n\n/** Shut down every scope's watcher. Web server shutdown hook. */\nexport function disposeAllScopes(): void {\n for (const e of scopes.values()) {\n e.watcher?.stop();\n e.subscribers.clear();\n }\n scopes.clear();\n}\n\n/** Build a comment-store id for a scope. Distinguishes from session\n * ids so the two namespaces can't collide on disk. */\nexport function commentStoreIdForScope(hash: string): string {\n return `scope-${hash}`;\n}\n","import type { Hono } from 'hono';\n\n/**\n * PTY route registration. The actual WebSocket upgrade happens at the Node\n * HTTP server level via `attachTerminalUpgrade()` — Hono only owns the\n * \"is this session known?\" gating endpoint that the client hits first to\n * discover whether the terminal tab is available.\n */\nexport function mountTerminalRoutes(app: Hono): void {\n app.get('/api/sessions/:id/terminal/health', (c) => {\n return c.json({ ok: true, sessionId: c.req.param('id') });\n });\n}\n","import type { IncomingMessage } from 'node:http';\nimport type { Socket } from 'node:net';\nimport type EventEmitter from 'node:events';\nimport { WebSocketServer, type WebSocket } from 'ws';\nimport { getOrCreatePty } from './pty-pool.js';\n\nconst TERMINAL_PATH = /^\\/ws\\/sessions\\/([^/]+)\\/terminal$/;\n\n/** Minimal contract over the Node `http.Server` we need — broad enough to\n * accept both HTTP/1 and HTTP/2 servers from `@hono/node-server`. */\ntype UpgradableServer = EventEmitter;\n\n/**\n * Attach a WebSocket handler to the same Node http server Hono is running\n * on. Listens for upgrade requests at /ws/sessions/:id/terminal, attaches\n * to (or spawns) that session's PTY, and bridges traffic both ways.\n *\n * Browser → server frames are JSON:\n * { type: 'input', data: string } stdin bytes\n * { type: 'resize', cols, rows } PTY resize\n *\n * Server → browser frames are binary (PTY output, sent as utf-8 strings).\n *\n * `port` is the listening port — used for the Host-header DNS-rebinding\n * guard, mirroring the one applied to Hono routes in `diff-server.launch`.\n * The WS upgrade bypasses Hono entirely so it needs its own check.\n */\nexport function attachTerminalWs(\n httpServer: UpgradableServer,\n port: number,\n): { close: () => void } {\n const wss = new WebSocketServer({ noServer: true });\n const allowedHosts = new Set([\n `127.0.0.1:${port}`,\n `localhost:${port}`,\n ]);\n\n httpServer.on('upgrade', (req: IncomingMessage, socket: Socket, head) => {\n const host = req.headers.host;\n if (!host || !allowedHosts.has(host)) {\n socket.destroy();\n return;\n }\n const url = req.url ?? '';\n const match = url.match(TERMINAL_PATH);\n if (!match) {\n socket.destroy();\n return;\n }\n const sessionId = decodeURIComponent(match[1]);\n wss.handleUpgrade(req, socket, head, (ws) => {\n const pty = getOrCreatePty(sessionId);\n if (!pty) {\n try {\n ws.send(JSON.stringify({ type: 'error', message: 'unknown session' }));\n ws.close(1011);\n } catch { /* */ }\n return;\n }\n handleConnection(ws, pty);\n });\n });\n\n return {\n close: () => wss.close(),\n };\n}\n\nfunction handleConnection(\n ws: WebSocket,\n pty: ReturnType<typeof getOrCreatePty> & object,\n): void {\n // Replay first so the browser sees existing scrollback immediately.\n const replay = pty.replay();\n if (replay) {\n try { ws.send(replay); } catch { /* */ }\n }\n\n const unsubscribe = pty.subscribe((data) => {\n try { ws.send(data); } catch { /* client gone */ }\n });\n\n ws.on('message', (raw) => {\n let msg: unknown;\n try {\n msg = JSON.parse(raw.toString('utf-8'));\n } catch {\n return;\n }\n if (!msg || typeof msg !== 'object') return;\n const m = msg as {\n type?: string;\n data?: string;\n cols?: number;\n rows?: number;\n };\n if (m.type === 'input' && typeof m.data === 'string') {\n pty.write(m.data);\n } else if (\n m.type === 'resize' &&\n typeof m.cols === 'number' &&\n typeof m.rows === 'number'\n ) {\n pty.resize(m.cols, m.rows);\n }\n });\n\n ws.on('close', () => {\n unsubscribe();\n });\n ws.on('error', () => {\n unsubscribe();\n });\n}\n","/**\n * Installs a `command`-type entry in `~/.claude/settings.json` so Claude\n * Code spawns our hook subcommand and injects its stdout into the\n * conversation. Distinct from `HookServer` (which uses `http`-type hooks\n * for fire-and-forget notifications) — command hooks let us return text\n * that becomes part of Claude's context.\n *\n * Each install is tagged via the shared `settings-editor` (owner + PID)\n * so stale entries from a crashed previous run get pruned automatically\n * and writes are atomic.\n *\n * SECURITY NOTE: the `command` we register is resolved against the user's\n * PATH at hook-fire time (not install time). If an attacker can shadow\n * `work` earlier in PATH between install and fire, they intercept review\n * comments. We accept this for V1 — the tool is local-only — but a future\n * hardening pass should resolve the absolute path of the running `work`\n * binary at install time and embed that instead.\n */\n\nimport {\n editSettings,\n editSettingsSync,\n isOwnerEntry,\n isStaleEntry,\n tag,\n type HookEntry,\n} from './settings-editor.js';\n\nexport interface CommandHookOptions {\n owner: string;\n /** Claude Code hook event to register under (e.g. UserPromptSubmit). */\n event: string;\n /** Shell command to execute. Receives the hook payload on stdin. */\n command: string;\n /** Hook timeout in seconds. Default 5. */\n timeoutSec?: number;\n}\n\nconst HOOK_TYPE = 'command';\n\nexport function installCommandHook(opts: CommandHookOptions): Promise<void> {\n return editSettings((s) => {\n if (!s.hooks) s.hooks = {};\n const list = (s.hooks[opts.event] ?? []) as HookEntry[];\n const cleaned = list.filter(\n (h) => !isStaleEntry(h) && !isOwnerEntry(h, opts.owner),\n );\n cleaned.push(\n tag(\n {\n hooks: [\n {\n type: HOOK_TYPE,\n command: opts.command,\n timeout: opts.timeoutSec ?? 5,\n },\n ],\n },\n opts.owner,\n ),\n );\n s.hooks[opts.event] = cleaned;\n });\n}\n\nexport function removeCommandHook(owner: string, event: string): Promise<void> {\n return editSettings((s) => removeOwnerEntries(s, owner, event));\n}\n\n/** Synchronous variant for signal handlers. */\nexport function removeCommandHookSync(owner: string, event: string): void {\n editSettingsSync((s) => removeOwnerEntries(s, owner, event));\n}\n\nfunction removeOwnerEntries(\n s: { hooks?: Record<string, HookEntry[] | undefined> },\n owner: string,\n event: string,\n): void {\n if (!s.hooks) return;\n const list = s.hooks[event];\n if (!Array.isArray(list)) return;\n s.hooks[event] = list.filter(\n (h) => !isStaleEntry(h) && !isOwnerEntry(h, owner),\n );\n if (s.hooks[event]!.length === 0) delete s.hooks[event];\n}\n","import type { CommandModule } from 'yargs';\nimport { readSessionActivity } from '../core/claude-activity.js';\nimport {\n findSessionForCwd,\n formatPendingForPrompt,\n markDelivered,\n readPendingForWorktree,\n sessionIdFor,\n} from '../core/pending-delivery.js';\n\n/**\n * `work hook prompt-submit` / `work hook stop` — invoked by Claude Code's\n * UserPromptSubmit / Stop hooks (registered by `work web` on startup).\n * Reads pending review comments for the worktree the user is currently\n * in, prints them in the format Claude Code expects for that event, and\n * marks them delivered so they don't repeat.\n *\n * Exits silently when:\n * - cwd isn't a `work`-managed worktree\n * - there are no pending comments\n * - Claude isn't actively running in this worktree (paranoia — the hook\n * only fires from inside a running Claude, but we double-check)\n */\nexport type HookEvent = 'prompt-submit' | 'stop';\n\nexport interface HookInput {\n event: HookEvent;\n cwd: string;\n}\n\nexport interface HookOutput {\n /** Plain text appended to the user's prompt (prompt-submit) or\n * raw JSON `{decision:'block', reason}` that prevents stop (stop). */\n stdout: string;\n /** The comment ids surfaced — caller marks them delivered. */\n deliveredIds: string[];\n /** The session id that produced the output, when there was something\n * to deliver. Null otherwise (so the caller knows to skip markDelivered). */\n sessionId: string | null;\n}\n\n/**\n * Pure transformation: given an event and cwd, produce the stdout payload\n * Claude Code expects plus the ids to mark delivered. Returns null when\n * there's nothing to surface. No I/O on stdin/stdout — the caller wraps.\n */\nexport function computeHookOutput(input: HookInput): HookOutput | null {\n const session = findSessionForCwd(input.cwd);\n if (!session) return null;\n\n const activity = readSessionActivity(session);\n if (activity.state === 'stale') return null;\n\n const sessionId = sessionIdFor(session);\n const pending = readPendingForWorktree(session);\n if (pending.length === 0) return null;\n\n const text = formatPendingForPrompt(pending);\n if (!text) return null;\n\n const ids = pending.map((c) => c.id);\n\n if (input.event === 'prompt-submit') {\n return { stdout: text + '\\n', deliveredIds: ids, sessionId };\n }\n // Stop hook: `decision: 'block'` keeps Claude in the turn and feeds\n // `reason` back as additional context.\n return {\n stdout: JSON.stringify({ decision: 'block', reason: text }) + '\\n',\n deliveredIds: ids,\n sessionId,\n };\n}\n\ninterface HookPayload {\n cwd?: string;\n session_id?: string;\n hook_event_name?: string;\n}\n\nasync function readStdinJson(): Promise<HookPayload> {\n if (process.stdin.isTTY) return {};\n return new Promise((resolve) => {\n const chunks: Buffer[] = [];\n let resolved = false;\n const done = (val: HookPayload) => {\n if (resolved) return;\n resolved = true;\n resolve(val);\n };\n process.stdin.on('data', (c: Buffer) => chunks.push(c));\n process.stdin.on('end', () => {\n try {\n const raw = Buffer.concat(chunks).toString('utf-8').trim();\n done(raw ? (JSON.parse(raw) as HookPayload) : {});\n } catch {\n done({});\n }\n });\n process.stdin.on('error', () => done({}));\n setTimeout(() => done({}), 1000);\n });\n}\n\nexport const hookCommand: CommandModule = {\n command: 'hook <event>',\n describe: false, // hidden — humans don't call this directly\n builder: (y) =>\n y.positional('event', {\n type: 'string',\n choices: ['prompt-submit', 'stop'] as const,\n describe: 'Hook event name',\n }),\n handler: async (argv) => {\n const event = argv.event as HookEvent;\n const payload = await readStdinJson();\n const cwd = payload.cwd ?? process.cwd();\n const result = computeHookOutput({ event, cwd });\n if (!result || !result.sessionId) return;\n process.stdout.write(result.stdout);\n markDelivered(result.sessionId, result.deliveredIds);\n },\n};\n","import chalk from 'chalk';\nimport spawn from 'cross-spawn';\nimport type { CommandModule } from 'yargs';\nimport { loadHistory } from '../core/history.js';\nimport {\n selectSessions,\n expandRunUnits,\n anyFailed,\n type RunResult,\n type RunUnit,\n} from '../core/fleet.js';\n\n/** Build the shell invocation for the current platform. `sh -c <cmd>` on\n * POSIX, `cmd.exe /c <cmd>` on Windows. We pass cross-spawn an argv array\n * with shell:false — the user's command string is the intentional payload,\n * but we never interpolate paths/branches into a shell string ourselves. */\nfunction shellInvocation(cmd: string): { bin: string; args: string[] } {\n if (process.platform === 'win32') {\n return { bin: 'cmd.exe', args: ['/c', cmd] };\n }\n return { bin: 'sh', args: ['-c', cmd] };\n}\n\n/** Live children so a SIGINT (Ctrl-C) can tear the whole fleet down instead\n * of orphaning subprocesses. Keyed by the ChildProcess itself. */\nconst liveChildren = new Set<import('node:child_process').ChildProcess>();\n\nfunction killAllChildren(signal: NodeJS.Signals = 'SIGTERM'): void {\n for (const child of liveChildren) {\n try {\n child.kill(signal);\n } catch {\n /* already gone */\n }\n }\n}\n\n/**\n * Run `cmd` in `unit.path`. When `prefix` is set (parallel mode) the child's\n * stdout/stderr are captured and re-emitted line-by-line with a per-worktree\n * tag, so concurrent output stays attributable. Sequential mode passes\n * `stdio: 'inherit'` for a transparent, unprefixed passthrough.\n */\nfunction runInPath(\n unit: RunUnit,\n cmd: string,\n prefix?: string,\n): Promise<RunResult> {\n const { bin, args } = shellInvocation(cmd);\n return new Promise((resolve) => {\n const child = spawn(bin, args, {\n cwd: unit.path,\n stdio: prefix ? ['ignore', 'pipe', 'pipe'] : 'inherit',\n shell: false,\n });\n liveChildren.add(child);\n\n if (prefix) {\n const tag = chalk.cyan(`${prefix} `);\n const pipe = (\n stream: NodeJS.ReadableStream | null,\n sink: NodeJS.WriteStream,\n ) => {\n if (!stream) return;\n let buf = '';\n stream.setEncoding('utf-8');\n stream.on('data', (chunk: string) => {\n buf += chunk;\n let nl: number;\n while ((nl = buf.indexOf('\\n')) >= 0) {\n sink.write(tag + buf.slice(0, nl) + '\\n');\n buf = buf.slice(nl + 1);\n }\n });\n stream.on('end', () => {\n if (buf.length > 0) sink.write(tag + buf + '\\n');\n });\n };\n pipe(child.stdout, process.stdout);\n pipe(child.stderr, process.stderr);\n }\n\n child.on('close', (code) => {\n liveChildren.delete(child);\n resolve({ ...unit, code, ok: code === 0 });\n });\n child.on('error', () => {\n liveChildren.delete(child);\n resolve({ ...unit, code: null, ok: false });\n });\n });\n}\n\n/**\n * Run `units` concurrently with at most `limit` in flight at any time. Order\n * of the returned results matches `units`. A worker-pool rather than a single\n * `Promise.all` so a large fleet doesn't fork every subprocess at once.\n */\nexport async function runPool(\n units: RunUnit[],\n cmd: string,\n limit: number,\n /** Polled before each unit is dequeued. Once it returns true (Ctrl-C),\n * workers stop spawning new units rather than draining the whole queue. */\n isInterrupted: () => boolean = () => false,\n): Promise<RunResult[]> {\n const results: RunResult[] = new Array(units.length);\n let next = 0;\n async function worker(): Promise<void> {\n while (true) {\n // Bail BEFORE dequeuing/spawning the next unit once interrupted — the\n // SIGINT handler has already killed in-flight children; we must not\n // keep launching the remaining ones.\n if (isInterrupted()) return;\n const idx = next++;\n if (idx >= units.length) return;\n const u = units[idx];\n const prefix = `[${u.session.target}/${u.session.branch}]`;\n results[idx] = await runInPath(u, cmd, prefix);\n }\n }\n const workers = Array.from(\n { length: Math.min(limit, units.length) },\n () => worker(),\n );\n await Promise.all(workers);\n return results;\n}\n\nfunction label(r: RunResult): string {\n const head = chalk.cyan(`[${r.session.target}/${r.session.branch}]`);\n const where = chalk.gray(`(${r.path})`);\n if (r.ok) {\n return `${head} ${where} ${chalk.green('✓')} exit 0`;\n }\n const codeStr = r.code === null ? 'signalled' : `exit ${r.code}`;\n return `${head} ${where} ${chalk.red('✗')} ${codeStr}`;\n}\n\n/** Default cap on concurrent worktrees under `--parallel`. Bounded so a large\n * fleet doesn't fork hundreds of subprocesses at once. */\nconst DEFAULT_JOBS = 4;\n\n/** Our own fleet options. Everything else after the first bare token is the\n * user's command and is captured verbatim. Booleans take no value; the rest\n * consume the following token as their value. */\nconst BOOLEAN_FLAGS = new Set(['--parallel', '--halt-on-error', '--all']);\nconst VALUE_FLAGS = new Set(['--target', '--branch', '--jobs', '-j']);\n\n/** Drop the leading `run` command token(s). Under our parser config yargs\n * reports `argv._` as `['run', 'run', ...userArgs]` — the matched command\n * name plus a halt-at-non-option artifact — so we always strip exactly the\n * first two. A user-typed literal `run` (`work run run echo`) survives. */\nexport function stripRunToken(raw: string[]): string[] {\n let drop = 0;\n while (drop < 2 && raw[drop] === 'run') drop++;\n return raw.slice(drop);\n}\n\nexport interface ExtractedRun {\n /** Our parsed fleet options (only the ones present). */\n options: {\n target?: string;\n branch?: string;\n parallel?: boolean;\n haltOnError?: boolean;\n all?: boolean;\n jobs?: number;\n };\n /** The user's command argv, captured verbatim (flags included). */\n cmd: string[];\n}\n\n/**\n * Split `work run`'s raw arguments into our fleet options and the user's\n * command. yargs cannot do this safely: with the default parser it eats the\n * user command's own flags (`work run git log --oneline` drops `--oneline`;\n * `work run x --parallel` steals our fleet flag). So we parse the leading\n * fleet flags ourselves and treat the first bare token (or anything after a\n * literal `--`) as the start of the user command — capturing the rest,\n * including any flags, verbatim.\n *\n * `args` is everything after the `run` subcommand token.\n */\nexport function extractRun(args: string[]): ExtractedRun {\n const options: ExtractedRun['options'] = {};\n let i = 0;\n for (; i < args.length; i++) {\n const tok = args[i];\n if (tok === '--') {\n // Explicit separator: everything after is the command, verbatim.\n i++;\n break;\n }\n if (BOOLEAN_FLAGS.has(tok)) {\n if (tok === '--parallel') options.parallel = true;\n else if (tok === '--halt-on-error') options.haltOnError = true;\n else if (tok === '--all') options.all = true;\n continue;\n }\n if (VALUE_FLAGS.has(tok)) {\n const val = args[i + 1];\n if (val === undefined) break;\n if (tok === '--target') options.target = val;\n else if (tok === '--branch') options.branch = val;\n else if (tok === '--jobs' || tok === '-j') options.jobs = Number(val);\n i++;\n continue;\n }\n // First bare token (or unknown flag): start of the user command.\n break;\n }\n return { options, cmd: args.slice(i) };\n}\n\nexport const runCommand: CommandModule = {\n // `run [cmd..]` keeps yargs happy about the trailing positional while we do\n // the real argument extraction ourselves from the raw process argv.\n command: 'run [cmd..]',\n describe: 'Run a shell command in every worktree (optionally filtered)',\n builder: (yargs) =>\n yargs\n // Keep the user command's own flags out of our parser: unknown flags\n // (`--oneline`, `--fix`) become args instead of errors, and halt-at-non\n // -option stops parsing at the first bare token so a colliding flag\n // (`--parallel`) after the command name is not stolen. We do the real\n // split in `extractRun` from the raw argv.\n .parserConfiguration({\n 'halt-at-non-option': true,\n 'unknown-options-as-args': true,\n })\n .positional('cmd', {\n describe: 'Command to run (everything after `run`)',\n type: 'string',\n array: true,\n })\n .option('target', {\n describe: 'Only run in worktrees for this project/group alias',\n type: 'string',\n })\n .option('branch', {\n describe: 'With --target, only run in this branch',\n type: 'string',\n })\n .option('parallel', {\n describe: 'Run all worktrees concurrently (default: sequential)',\n type: 'boolean',\n default: false,\n })\n .option('jobs', {\n alias: 'j',\n describe: 'Max concurrent worktrees with --parallel (default: 4)',\n type: 'number',\n })\n .option('halt-on-error', {\n describe: 'Stop after the first failure (sequential only)',\n type: 'boolean',\n default: false,\n })\n .option('all', {\n describe: 'Required to run in every worktree when no --target',\n type: 'boolean',\n default: false,\n }),\n handler: async (argv) => {\n // yargs dumps the whole invocation into `_` verbatim under our parser\n // config (`['run', 'run', ...userArgs]` — the command token, doubled by\n // halt-at-non-option). Strip leading `run` tokens, then split off our\n // fleet flags ourselves so the user command (incl. its flags) survives.\n const rawAll = (argv._ as Array<string | number>).map(String);\n const { options, cmd: cmdParts } = extractRun(stripRunToken(rawAll));\n const cmd = cmdParts.join(' ').trim();\n if (!cmd) {\n console.error(chalk.red('No command given. Usage: work run <cmd...>'));\n process.exitCode = 1;\n return;\n }\n\n const target = options.target;\n const branch = options.branch;\n const parallel = options.parallel ?? false;\n const haltOnError = options.haltOnError ?? false;\n const all = options.all ?? false;\n const jobs =\n options.jobs && Number.isFinite(options.jobs) && options.jobs > 0\n ? Math.floor(options.jobs)\n : DEFAULT_JOBS;\n\n if (branch && !target) {\n console.error(chalk.red('--branch requires --target.'));\n process.exitCode = 1;\n return;\n }\n\n // Guardrail (mirrors `work broadcast`): running an arbitrary command in\n // every worktree is a wide blast radius, so a bare unfiltered `work run`\n // must opt in with --all rather than fanning out by accident.\n if (!target && !all) {\n console.error(\n chalk.red(\n 'Refusing to run in every worktree. Pass --all to confirm, or --target <alias> to narrow.',\n ),\n );\n process.exitCode = 1;\n return;\n }\n\n // --halt-on-error only applies to the sequential walk; the parallel pool\n // has no first-failure stop. Warn rather than silently ignoring it.\n if (parallel && haltOnError) {\n console.error(\n chalk.yellow(\n 'Warning: --halt-on-error has no effect with --parallel (it only applies to sequential runs).',\n ),\n );\n }\n\n const sessions = selectSessions(loadHistory(), { target, branch });\n const units = expandRunUnits(sessions);\n\n if (units.length === 0) {\n console.log(chalk.yellow('No matching worktrees.'));\n return;\n }\n\n console.log(\n chalk.cyan(\n `Running in ${units.length} worktree${units.length === 1 ? '' : 's'} (${parallel ? `parallel, up to ${jobs} at a time` : 'sequential'}): `,\n ) + chalk.white(cmd),\n );\n console.log('');\n\n const results: RunResult[] = [];\n\n // Tear the whole fleet down on Ctrl-C rather than orphaning children.\n let interrupted = false;\n const onSigint = () => {\n interrupted = true;\n console.error(chalk.yellow('\\nInterrupted — terminating child processes…'));\n killAllChildren('SIGTERM');\n };\n process.on('SIGINT', onSigint);\n\n try {\n if (parallel) {\n const settled = await runPool(units, cmd, jobs, () => interrupted);\n // On interrupt the pool returns a sparse array (un-dequeued units are\n // never filled); drop the holes before reporting.\n const done = settled.filter((r): r is RunResult => r != null);\n results.push(...done);\n for (const r of done) console.log(label(r));\n } else {\n for (const unit of units) {\n if (interrupted) break;\n const r = await runInPath(unit, cmd);\n results.push(r);\n console.log(label(r));\n if (!r.ok && haltOnError) {\n console.log(chalk.yellow('Halting on first failure (--halt-on-error).'));\n break;\n }\n }\n }\n } finally {\n process.off('SIGINT', onSigint);\n }\n\n if (interrupted) {\n console.log('');\n console.log(chalk.yellow('Aborted.'));\n process.exitCode = 130;\n return;\n }\n\n console.log('');\n const failed = results.filter((r) => !r.ok).length;\n if (anyFailed(results)) {\n console.log(chalk.red(`${failed} of ${results.length} failed.`));\n process.exitCode = 1;\n } else {\n console.log(chalk.green(`All ${results.length} succeeded.`));\n }\n },\n};\n","/**\n * Shared selection logic for the fleet commands (`work run`, `work broadcast`).\n * Pure helpers so command files stay thin and the filtering is unit-testable.\n *\n * Direction: core does NOT import from commands. Commands import these.\n */\n\nimport type { WorktreeSession } from './history.js';\n\nexport interface FleetFilter {\n /** Restrict to sessions whose target matches this alias/group name. */\n target?: string;\n /** With `target`, further restrict to a single branch. */\n branch?: string;\n}\n\n/**\n * Select the sessions a fleet command should act on.\n *\n * - No filter → every session.\n * - `target` only → all sessions for that target.\n * - `target` + `branch` → the single matching session (if any).\n *\n * `branch` without `target` is ignored for selection but the caller is\n * expected to reject that combination up front (it's ambiguous).\n */\nexport function selectSessions(\n sessions: WorktreeSession[],\n filter: FleetFilter,\n): WorktreeSession[] {\n return sessions.filter((s) => {\n if (filter.target && s.target !== filter.target) return false;\n if (filter.branch && s.branch !== filter.branch) return false;\n return true;\n });\n}\n\nexport interface RunUnit {\n session: WorktreeSession;\n /** A single worktree directory to run the command in. */\n path: string;\n}\n\n/**\n * Flatten selected sessions into one unit of work per worktree path. Group\n * sessions contribute one unit per path. Order is stable (session order,\n * then path order) so sequential runs are deterministic.\n */\nexport function expandRunUnits(sessions: WorktreeSession[]): RunUnit[] {\n const units: RunUnit[] = [];\n for (const session of sessions) {\n for (const p of session.paths) {\n units.push({ session, path: p });\n }\n }\n return units;\n}\n\nexport interface RunResult extends RunUnit {\n /** Exit code; null when the process was killed by a signal. */\n code: number | null;\n ok: boolean;\n}\n\n/** True when any unit failed (non-zero / signalled). */\nexport function anyFailed(results: RunResult[]): boolean {\n return results.some((r) => !r.ok);\n}\n","import fs from 'node:fs';\nimport chalk from 'chalk';\nimport type { CommandModule } from 'yargs';\nimport { loadHistory } from '../core/history.js';\nimport { broadcastPrompt } from '../core/broadcast.js';\n\n/** Read the whole of stdin synchronously (for `broadcast -`). */\nfunction readStdin(): string {\n try {\n return fs.readFileSync(0, 'utf-8');\n } catch {\n return '';\n }\n}\n\nexport const broadcastCommand: CommandModule = {\n command: 'broadcast <prompt>',\n describe:\n 'Queue a prompt to every live session; delivered on each session\\'s next turn',\n builder: (yargs) =>\n yargs\n .positional('prompt', {\n describe: 'Prompt to send, or \"-\" to read from stdin',\n type: 'string',\n })\n .option('target', {\n describe: 'Only sessions for this project/group alias',\n type: 'string',\n })\n .option('branch', {\n describe: 'With --target, only this branch',\n type: 'string',\n })\n .option('all', {\n describe: 'Required to broadcast to every session when no --target',\n type: 'boolean',\n default: false,\n }),\n handler: async (argv) => {\n let prompt = (argv.prompt as string | undefined) ?? '';\n if (prompt === '-') {\n prompt = readStdin();\n }\n prompt = prompt.trim();\n if (!prompt) {\n console.error(chalk.red('Empty prompt. Usage: work broadcast <prompt>'));\n process.exitCode = 1;\n return;\n }\n\n const target = argv.target as string | undefined;\n const branch = argv.branch as string | undefined;\n const all = argv.all as boolean;\n if (branch && !target) {\n console.error(chalk.red('--branch requires --target.'));\n process.exitCode = 1;\n return;\n }\n // Guardrail: broadcasting to every session is a wide blast radius, so\n // require an explicit --all (or a narrowing --target) rather than letting\n // a bare `work broadcast \"...\"` fan out to the whole fleet by accident.\n if (!target && !all) {\n console.error(\n chalk.red(\n 'Refusing to broadcast to every session. Pass --all to confirm, or --target <alias> to narrow.',\n ),\n );\n process.exitCode = 1;\n return;\n }\n\n const queued = await broadcastPrompt(loadHistory(), { target, branch }, prompt);\n\n if (queued.length === 0) {\n console.log(chalk.yellow('No matching sessions to broadcast to.'));\n return;\n }\n\n console.log(\n chalk.cyan(\n `Queued prompt to ${queued.length} session${queued.length === 1 ? '' : 's'}:`,\n ),\n );\n for (const t of queued) {\n console.log(\n ` ${chalk.cyan(`[${t.session.target}/${t.session.branch}]`)} ${chalk.gray(t.sessionId)}`,\n );\n }\n console.log('');\n console.log(\n chalk.gray(\n 'Delivery is lazy: each session picks this up on its next prompt (UserPromptSubmit hook).',\n ),\n );\n },\n};\n","/**\n * Core for `work broadcast`: queue a prompt to every (filtered) live session\n * via the existing pending-delivery mechanism. We do NOT touch PTYs — those\n * are per-process and unreachable from a separate CLI. Instead we post a\n * `published`, `author:'user'` comment to each session's comment file store;\n * `work hook prompt-submit` surfaces it on that session's next turn (see\n * pending-delivery.ts `readPendingForSession`, which selects exactly those).\n *\n * Because broadcast writes `~/.work` state from a SEPARATE process while\n * `work web` may be writing the same comment file, the write goes through\n * `withFileLock` + `atomicWriteFile` (§5.2): we lock, re-read the file from\n * disk, append, and atomically write back — so a concurrent server write\n * can't be lost or corrupted.\n */\n\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport {\n clearCommentStoreCache,\n commentsDir,\n commentsFileFor,\n} from './comment-file-store.js';\nimport { ensureFile, withFileLock, atomicWriteFile } from './fs-safe.js';\nimport { sessionIdFor } from './web-state.js';\nimport { selectSessions, type FleetFilter } from './fleet.js';\nimport type { WorktreeSession } from './history.js';\nimport type { Comment } from './comment-types.js';\n\nexport interface BroadcastTarget {\n session: WorktreeSession;\n sessionId: string;\n commentId: string;\n}\n\nfunction readComments(file: string): Comment[] {\n try {\n const parsed = JSON.parse(fs.readFileSync(file, 'utf-8'));\n return Array.isArray(parsed) ? (parsed as Comment[]) : [];\n } catch {\n return [];\n }\n}\n\n/** Append one published user comment to a session's comment file under an\n * advisory lock, so concurrent `work web` writes to the same file can't\n * clobber each other. Returns the new comment's id. */\nasync function appendLocked(sessionId: string, body: string): Promise<string> {\n const file = commentsFileFor(sessionId);\n fs.mkdirSync(commentsDir(), { recursive: true });\n ensureFile(file, '[]');\n const comment: Comment = {\n id: crypto.randomBytes(8).toString('hex'),\n repo: '',\n file: '',\n line: 0,\n side: 'general',\n body,\n createdAt: new Date().toISOString(),\n author: 'user',\n status: 'published',\n };\n await withFileLock(file, () => {\n const existing = readComments(file);\n existing.push(comment);\n atomicWriteFile(file, JSON.stringify(existing, null, 2));\n });\n return comment.id;\n}\n\n/**\n * Post `prompt` as a published user comment (side 'general') to every session\n * matching `filter`. Returns one entry per session queued.\n */\nexport async function broadcastPrompt(\n sessions: WorktreeSession[],\n filter: FleetFilter,\n prompt: string,\n): Promise<BroadcastTarget[]> {\n const body = prompt.trim();\n if (!body) throw new Error('broadcast prompt is empty');\n\n const selected = selectSessions(sessions, filter);\n const out: BroadcastTarget[] = [];\n for (const session of selected) {\n const sessionId = sessionIdFor(session);\n const commentId = await appendLocked(sessionId, body);\n out.push({ session, sessionId, commentId });\n }\n // Any in-process cache for these files is now stale (we wrote underneath it\n // via the lock path) — drop it so subsequent reads see the on-disk truth.\n if (out.length > 0) clearCommentStoreCache();\n return out;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { loadConfig } from '../core/config.js';\nimport { parseWorktreeList, getCurrentBranch } from '../core/git.js';\nimport { resolveProjectTarget } from '../core/resolve.js';\n\ntype Done = (completions: string[]) => void;\n\n/**\n * Dynamic completion handler for yargs.\n * Called when the user presses TAB (via --get-yargs-completions).\n *\n * argv._ = ['<scriptName>', '<command>', '<arg1>', ...., '<current>']\n * We skip the script name and the trailing current word to get the \"completed\" args.\n */\nexport function completionHandler(\n current: string,\n argv: Record<string, unknown>,\n done: Done,\n): void {\n const rawArgs = argv._ as string[];\n\n // Skip the script name (first element) and the trailing current word (last element)\n const args = rawArgs.slice(1, -1);\n const command = args[0] as string | undefined;\n\n const config = loadConfig();\n if (!config) {\n done([]);\n return;\n }\n\n if (!command) {\n done(\n ['tree', 't', 'remove', 'list', 'status', 'recent', 'resume', 'prune', 'dash', 'todo', 'hydrate', 'diff', 'web', 'init', 'config', 'completion'].filter(\n (c) => c.startsWith(current),\n ),\n );\n return;\n }\n\n switch (command) {\n case 'config':\n completeConfig(args, current, config, done);\n return;\n\n case 'tree':\n case 't':\n case 'remove':\n case 'list':\n case 'status':\n completeTreeRemoveList(command, args, current, config, done);\n return;\n\n default:\n done([]);\n }\n}\n\nfunction completeConfig(\n args: string[],\n current: string,\n config: NonNullable<ReturnType<typeof loadConfig>>,\n done: Done,\n): void {\n const subAction = args[1] as string | undefined;\n\n if (!subAction) {\n const actions = [\n 'add',\n 'remove',\n 'list',\n 'group',\n 'show',\n 'edit',\n ];\n done(actions.filter((a) => a.startsWith(current)));\n return;\n }\n\n if (subAction === 'remove' && args.length === 2) {\n const aliases = Object.keys(config.repos);\n done(aliases.filter((a) => a.startsWith(current)));\n return;\n }\n\n if (subAction === 'group') {\n const groupSub = args[2] as string | undefined;\n\n if (!groupSub) {\n const subs = ['add', 'remove', 'regen'];\n done(subs.filter((s) => s.startsWith(current)));\n return;\n }\n\n if (\n (groupSub === 'remove' || groupSub === 'regen') &&\n args.length === 3\n ) {\n const groups = Object.keys(config.groups);\n done(groups.filter((g) => g.startsWith(current)));\n return;\n }\n\n if (groupSub === 'add' && args.length >= 4) {\n const alreadyUsed = args.slice(3);\n const aliases = Object.keys(config.repos).filter(\n (a) => !alreadyUsed.includes(a),\n );\n done(aliases.filter((a) => a.startsWith(current)));\n return;\n }\n\n done([]);\n return;\n }\n\n done([]);\n}\n\nfunction completeTreeRemoveList(\n command: string,\n args: string[],\n current: string,\n config: NonNullable<ReturnType<typeof loadConfig>>,\n done: Done,\n): void {\n const targetName = args[1] as string | undefined;\n\n if (!targetName) {\n const names = [\n ...Object.keys(config.repos),\n ...Object.keys(config.groups),\n ];\n done(names.filter((n) => n.startsWith(current)));\n return;\n }\n\n if (args.length === 2) {\n const target = resolveProjectTarget(targetName, config);\n if (!target) {\n done([]);\n return;\n }\n\n if (target.isGroup) {\n completeGroupBranches(target.name, target.repoAliases, current, config, done);\n } else {\n completeRepoBranches(targetName, current, config, done);\n }\n return;\n }\n\n done([]);\n}\n\nfunction completeRepoBranches(\n alias: string,\n current: string,\n config: NonNullable<ReturnType<typeof loadConfig>>,\n done: Done,\n): void {\n const repoPath = config.repos[alias];\n if (!repoPath || !fs.existsSync(repoPath)) {\n done([]);\n return;\n }\n\n const worktrees = parseWorktreeList(repoPath);\n const branches = worktrees\n .map((wt) => wt.branch)\n .filter((b): b is string => !!b && b.startsWith(current));\n done(branches);\n}\n\nfunction completeGroupBranches(\n groupName: string,\n repoAliases: string[],\n current: string,\n config: NonNullable<ReturnType<typeof loadConfig>>,\n done: Done,\n): void {\n const groupDir = path.join(config.worktreesRoot, groupName);\n if (!fs.existsSync(groupDir)) {\n done([]);\n return;\n }\n\n try {\n const branchDirs = fs\n .readdirSync(groupDir, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => {\n const bdPath = path.join(groupDir, d.name);\n for (const alias of repoAliases) {\n const repoPath = config.repos[alias];\n if (!repoPath) continue;\n const subPath = path.join(bdPath, path.basename(repoPath));\n if (fs.existsSync(subPath)) {\n const branch = getCurrentBranch(subPath);\n if (branch) return branch;\n }\n }\n return d.name;\n });\n\n done(branchDirs.filter((b) => b.startsWith(current)));\n } catch {\n done([]);\n }\n}\n","declare const __WORK2_VERSION__: string;\n\n/**\n * Inlined at build time from package.json#version. When running via `tsx`\n * (dev mode) the define isn't applied, so we fall back to a placeholder.\n */\nexport const VERSION: string =\n typeof __WORK2_VERSION__ !== 'undefined' ? __WORK2_VERSION__ : 'dev';\n"],"mappings":"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","names":["fs","path","chalk","chalk","fs","path","chalk","path","fs","path","fs","path","path","fs","path","spawn","path","fs","content","spawn","spawn","fs","spawn","yargs","fs","chalk","path","path","os","chalk","fs","path","os","chalk","spawn","label","chalk","path","os","fs","chalk","fs","path","path","fs","fs","path","chalk","fs","path","fs","fs","os","path","fs","input","path","fs","fs","path","fs","chalk","path","yargs","fs","chalk","chalk","yargs","chalk","fs","path","chalk","yargs","chalk","fs","path","fs","chalk","yargs","chalk","fs","fs","path","chalk","select","yargs","chalk","fs","select","path","fs","path","chalk","select","yargs","fs","chalk","select","path","chalk","fs","path","chalk","fetch","fs","chalk","path","label","yargs","chalk","fs","chalk","yargs","fs","chalk","fs","path","Box","fs","os","path","execFile","spawn","execFile","execFile","execAsync","fs","path","path","fs","path","fs","path","os","fs","path","path","spawn","sessionKey","spawn","sessionKey","label","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","jsx","jsxs","path","os","fs","pty","execFile","sessionKey","sessions","spawn","jsxs","Box","jsx","jsx","label","fs","path","yargs","chalk","yargs","chalk","chalk","yargs","chalk","chalk","fs","path","path","fs","yargs","chalk","fs","os","path","chalk","fs","path","spawn","input","fs","path","path","fs","fs","os","path","spawn","run","path","spawn","realRoot","fs","files","result","fs","path","os","crypto","fs","path","chalk","path","fs","chalk","Hono","crypto","findParent","input","chalk","fs","path","spawn","path","realRoot","fs","spawn","fs","path","chalk","fs","path","path","fs","fs","path","info","chalk","Hono","input","fs","path","path","fs","chalk","path","os","fs","info","yargs","fs","os","path","chalk","fs","os","path","chalk","Hono","streamSSE","path","crypto","chokidar","crypto","findSession","chokidar","path","findSession","pty","fs","path","os","path","os","fs","input","fs","path","pathFor","path","fs","writeAtomic","zValidator","findSession","zValidator","pty","zValidator","z","z","zValidator","path","spawn","zValidator","z","z","zValidator","findSession","fetch","path","spawn","zValidator","z","path","spawn","fs","os","path","spawn","path","os","fs","spawn","label","path","crypto","crypto","path","label","streamSSE","spawn","zValidator","z","label","repos","path","pty","path","os","fs","Hono","findSession","streamSSE","chalk","info","path","os","fs","chalk","yargs","input","chalk","spawn","spawn","tag","chalk","yargs","fs","chalk","crypto","fs","fs","crypto","fs","yargs","chalk","fs","path","fs","path","chalk","yargs","chalk","fs","path"]}