sisyphi 1.1.17 → 1.1.18

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shared/utils.ts","../src/shared/format.ts","../src/tui/lib/reports.ts","../src/tui/lib/context.ts","../src/shared/companion-badges.ts","../src/shared/client.ts"],"sourcesContent":["import type { Session } from './types.js';\n\n/**\n * Return the tracked active time for a session.\n * Active time is accumulated by the daemon's pane monitor, excluding sleep/idle gaps.\n */\nexport function computeActiveTimeMs(session: Session): number {\n return session.activeMs;\n}\n","/** Format milliseconds or ISO date range to human-readable duration */\nexport function formatDuration(startOrMs: string | number, endIso?: string | null): string {\n let totalMs: number;\n if (typeof startOrMs === 'number') {\n totalMs = startOrMs;\n } else {\n const start = new Date(startOrMs).getTime();\n const end = endIso ? new Date(endIso).getTime() : Date.now();\n totalMs = end - start;\n }\n const totalSeconds = Math.floor(totalMs / 1000);\n if (totalSeconds < 0) return '0s';\n const hours = Math.floor(totalSeconds / 3600);\n const minutes = Math.floor((totalSeconds % 3600) / 60);\n const seconds = totalSeconds % 60;\n if (hours > 0) return `${hours}h${minutes}m`;\n if (minutes > 0) return `${minutes}m${seconds}s`;\n return `${seconds}s`;\n}\n\n/** Map session/agent status to a color name */\nexport function statusColor(status: string): string {\n switch (status) {\n case 'active':\n case 'running':\n return 'green';\n case 'completed':\n return 'cyan';\n case 'paused':\n return 'yellow';\n case 'killed':\n case 'crashed':\n return 'red';\n case 'lost':\n return 'gray';\n default:\n return 'white';\n }\n}\n","import { readFileSync } from 'node:fs';\nimport type { AgentReport } from '../../shared/types.js';\n\nexport interface ReportBlock {\n type: 'update' | 'final';\n timestamp: string;\n content: string;\n summary: string;\n}\n\nfunction loadReportContent(report: AgentReport): string {\n try {\n return readFileSync(report.filePath, 'utf-8');\n } catch {\n return report.summary;\n }\n}\n\nexport function resolveReports(reports: AgentReport[]): ReportBlock[] {\n return [...reports].reverse().map((r) => ({\n type: r.type as 'update' | 'final',\n timestamp: r.timestamp,\n content: loadReportContent(r),\n summary: r.summary,\n }));\n}\n","import { readFileSync, readdirSync } from 'node:fs';\nimport { goalPath, roadmapPath, sessionsDir, statePath } from '../../shared/paths.js';\nimport { resolveReports } from './reports.js';\nimport type { Session, AgentStatus } from '../../shared/types.js';\n\nfunction readFileSafe(filePath: string): string | null {\n try {\n return readFileSync(filePath, 'utf-8');\n } catch {\n return null;\n }\n}\n\nfunction escapeXml(s: string): string {\n return s\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n\nexport function buildCompanionContext(cwd: string): string {\n let sessionDirs: string[];\n try {\n sessionDirs = readdirSync(sessionsDir(cwd));\n } catch {\n return '<sessions>No sessions found.</sessions>';\n }\n\n const now = Date.now();\n const sevenDaysMs = 7 * 24 * 60 * 60 * 1000;\n const sessionBlocks: string[] = [];\n\n for (const sessionId of sessionDirs) {\n const stateRaw = readFileSafe(statePath(cwd, sessionId));\n if (!stateRaw) continue;\n\n let session: Session;\n try {\n session = JSON.parse(stateRaw) as Session;\n } catch {\n continue;\n }\n\n // Skip completed sessions older than 7 days\n if (session.status === 'completed' && session.completedAt) {\n if (now - new Date(session.completedAt).getTime() > sevenDaysMs) continue;\n }\n\n const lines: string[] = [];\n const nameAttr = session.name ? ` name=\"${escapeXml(session.name)}\"` : '';\n lines.push(` <session id=\"${escapeXml(session.id)}\"${nameAttr} status=\"${escapeXml(session.status)}\">`);\n lines.push(` <task>${escapeXml(session.task)}</task>`);\n lines.push(` <created>${escapeXml(session.createdAt)}</created>`);\n lines.push(` <cycles>${session.orchestratorCycles.length}</cycles>`);\n\n if (session.status === 'completed') {\n if (session.completionReport) {\n const snippet = session.completionReport.slice(0, 300).replace(/\\n+/g, ' ').trim();\n lines.push(` <completion-report>${escapeXml(snippet)}${session.completionReport.length > 300 ? '…' : ''}</completion-report>`);\n }\n } else {\n // Agent summary by status\n if (session.agents.length > 0) {\n const counts = new Map<AgentStatus, number>();\n for (const agent of session.agents) {\n counts.set(agent.status, (counts.get(agent.status) ?? 0) + 1);\n }\n const summary = [...counts.entries()].map(([status, n]) => `${n} ${status}`).join(', ');\n lines.push(` <agents>${escapeXml(summary)}</agents>`);\n }\n\n // Goal: first meaningful line\n const goalContent = readFileSafe(goalPath(cwd, session.id));\n if (goalContent) {\n const firstLine = goalContent.split('\\n').map(l => l.trim()).find(l => l.length > 0 && !l.startsWith('#'));\n if (firstLine) lines.push(` <goal>${escapeXml(firstLine)}</goal>`);\n }\n\n // Roadmap unchecked todos (up to 5)\n const roadmapContent = readFileSafe(roadmapPath(cwd, session.id));\n if (roadmapContent) {\n const todos = roadmapContent\n .split('\\n')\n .filter(l => l.includes('- [ ]'))\n .slice(0, 5)\n .map(l => l.trim());\n if (todos.length > 0) {\n lines.push(' <todos>');\n for (const todo of todos) lines.push(` ${escapeXml(todo)}`);\n lines.push(' </todos>');\n }\n }\n }\n\n lines.push(' </session>');\n sessionBlocks.push(lines.join('\\n'));\n }\n\n if (sessionBlocks.length === 0) {\n return '<sessions>No sessions found.</sessions>';\n }\n\n return ['<sessions>', ...sessionBlocks, '</sessions>'].join('\\n');\n}\n\nexport function buildSessionContext(session: Session, cwd: string): string {\n const goal = readFileSafe(goalPath(cwd, session.id));\n const roadmap = readFileSafe(roadmapPath(cwd, session.id));\n\n const agentsXml = session.agents.map((agent) => {\n const reportBlocks = resolveReports(agent.reports);\n // resolveReports returns newest-first; reverse to chronological for context\n const reportsXml = [...reportBlocks].reverse().map((block) => {\n return ` <report type=\"${block.type}\" time=\"${escapeXml(block.timestamp)}\">${escapeXml(block.content)}</report>`;\n }).join('\\n');\n\n return [\n ` <agent id=\"${escapeXml(agent.id)}\" name=\"${escapeXml(agent.name)}\" type=\"${escapeXml(agent.agentType)}\" status=\"${escapeXml(agent.status)}\">`,\n ` <instruction>${escapeXml(agent.instruction)}</instruction>`,\n ...(reportsXml ? [reportsXml] : []),\n ` </agent>`,\n ].join('\\n');\n }).join('\\n');\n\n const cyclesXml = session.orchestratorCycles.map((cycle) => {\n const agents = cycle.agentsSpawned.join(', ');\n const mode = cycle.mode ? ` mode=\"${escapeXml(cycle.mode)}\"` : '';\n return ` <cycle number=\"${cycle.cycle}\"${mode} agents=\"${escapeXml(agents)}\" />`;\n }).join('\\n');\n\n const lines: string[] = [\n '<context>',\n `<session id=\"${escapeXml(session.id)}\" status=\"${escapeXml(session.status)}\">`,\n ` <task>${escapeXml(session.task)}</task>`,\n ` <cwd>${escapeXml(session.cwd)}</cwd>`,\n ];\n\n if (goal) lines.push(` <goal>${escapeXml(goal)}</goal>`);\n if (roadmap) lines.push(` <roadmap>${escapeXml(roadmap)}</roadmap>`);\n\n if (session.agents.length > 0) {\n lines.push(' <agents>');\n lines.push(agentsXml);\n lines.push(' </agents>');\n }\n\n if (session.orchestratorCycles.length > 0) {\n lines.push(' <cycles>');\n lines.push(cyclesXml);\n lines.push(' </cycles>');\n }\n\n if (session.completionReport) {\n lines.push(` <completion-report>${escapeXml(session.completionReport)}</completion-report>`);\n }\n\n lines.push('</session>');\n lines.push('</context>');\n\n return lines.join('\\n');\n}\n","import type { AchievementId, AchievementDef, UnlockedAchievement } from './companion-types.js';\nimport { ACHIEVEMENTS } from './companion-types.js';\n\n// ---------------------------------------------------------------------------\n// Badge art — large-format ASCII art for achievement cards\n// Each art block is an array of strings, rendered centered in the card.\n// ---------------------------------------------------------------------------\n\nconst BADGE_ART: Record<AchievementId, string[]> = {\n // ── Milestone ─────────────────────────────────────────────────────────────\n 'first-blood': [\n ' ╱╲ ',\n ' ╱ ╲ ',\n ' ╱ ◆◆ ╲ ',\n ' ╱ ◆◆ ╲ ',\n ' ╱ ◆◆ ╲ ',\n ' ╱ ◆◆ ╲ ',\n ' ╱─────◆◆─────╲ ',\n ' ╲ ◆◆ ╱ ',\n ' ╲ ◆◆ ╱ ',\n ' ╲───◆◆───╱ ',\n ' ││ ',\n ' ══╪╪══ ',\n ],\n 'centurion': [\n ' ┌──═════──┐ ',\n ' │ ╔═══╗ │ ',\n ' ╱│ ║100║ │╲ ',\n ' ╱ │ ╚═══╝ │ ╲ ',\n ' ╱ └─────────┘ ╲ ',\n ' ╲ ┌───────┐ ╱ ',\n ' ╲ │ ★ ★ ★ │ ╱ ',\n ' ╲ └───────┘ ╱ ',\n ' ╲─────────╱ ',\n ],\n 'thousand-boulder': [\n ' ╭━━━╮ ',\n ' ╭━┫1K ┣━╮ ',\n ' ╭━┫ ╰━━━╯ ┣━╮ ',\n ' ┃ ◉◉◉◉◉◉ ┃ ',\n ' ┃ ◉◉◉◉◉◉◉◉ ┃ ',\n ' ┃ ◉◉◉◉◉◉ ┃ ',\n ' ╰━┫ ┣━╯ ',\n ' ╰━┫ ┣━╯ ',\n ' ╰━━━╯ ',\n ],\n 'cartographer': [\n ' N ',\n ' △ ',\n ' ╭────┼────╮ ',\n ' │ · │ · │ ',\n ' W┼····+····┼E ',\n ' │ · │ · │ ',\n ' ╰────┼────╯ ',\n ' ▽ ',\n ' S ',\n ],\n 'world-traveler': [\n ' ╭──────╮ ',\n ' ╭──┤ ├──╮ ',\n ' ╱ ·│ ◠◡◠ │· ╲ ',\n ' │ · │◠ ◡│ · │ ',\n ' │ · │ ◡◠◡ │ · │ ',\n ' ╲ ·│ │· ╱ ',\n ' ╰──┤ ├──╯ ',\n ' ╰──────╯ ',\n ],\n 'hive-mind': [\n ' ╱╲ ╱╲ ╱╲ ',\n ' ╱◆◆╲╱◆◆╲╱◆◆╲ ',\n ' ╲◆◆╱╲◆◆╱╲◆◆╱ ',\n ' ╱╲╱╱◆╲╱╱◆╲╲╱╲ ',\n ' ╱◆◆╲╲◆◆╲╲◆◆╱◆◆╲ ',\n ' ╲◆◆╱╱◆◆╱╱◆◆╲◆◆╱ ',\n ' ╲╱╲╲◆╱╲╲◆╱╱╲╱ ',\n ' ╲◆◆╱╲◆◆╱╲◆◆╱ ',\n ' ╲╱ ╲╱ ╲╱ ',\n ],\n 'old-growth': [\n ' ╱╲ ',\n ' ╱╱╲╲ ',\n ' ╱╱ ╲╲ ',\n ' ╱╱╱╲╱╲╲╲ ',\n ' ╱╱╱ ╲╲╲ ',\n ' ╱╱╱╱╲╱╲╱╲╲╲╲ ',\n ' ║║║ ',\n ' ║║║ ',\n ' ═══╩╩╩═══ ',\n ],\n 'ancient': [\n ' ┌─┬─────┬─┐ ',\n ' │ │ ◉ ◉ │ │ ',\n ' │ │ ▽ │ │ ',\n ' ╔═╧═╧═════╧═╧═╗ ',\n ' ║ A N C I E N ║ ',\n ' ║ T ║ ',\n ' ╚═════════════╝ ',\n ' ╱╱╱╱╱╱╱╱╱╱╱╱╱ ',\n ' ▔▔▔▔▔▔▔▔▔▔▔▔▔ ',\n ],\n 'regular': [\n ' ╭─────╮ ',\n ' ╭─┤ 10 ├─╮ ',\n ' ╱ ╰─────╯ ╲ ',\n ' │ ╭─────╮ │ ',\n ' │ │ ◉ │ │ ',\n ' │ ╰─────╯ │ ',\n ' ╲ ╱ ',\n ' ╰───────────╯ ',\n ],\n 'veteran': [\n ' ╔═══════════╗ ',\n ' ║ ╔═════╗ ║ ',\n ' ║ ║ V ║ ║ ',\n ' ║ ║ 500 ║ ║ ',\n ' ║ ╚═════╝ ║ ',\n ' ║ ★ ★ ★ ★ ★ ║ ',\n ' ╚═══════════╝ ',\n ' ╱╱╱╱╱╱╱╱╱╱╱ ',\n ' ▔▔▔▔▔▔▔▔▔▔▔ ',\n ],\n 'swarm-starter': [\n ' ',\n ' ◆ ◆ ◆ ◆ ◆ ',\n ' ◆ ◆ ◆ ◆ ◆ ',\n ' ◆ ◆ ◆ ◆ ◆ ',\n ' ◆ ◆ ◆ ◆ ◆ ',\n ' ◆ ◆ ◆ ◆ ◆ ',\n ' ',\n ' · 50 out · ',\n ],\n 'legion': [\n ' ◆◆◆◆◆◆◆◆◆◆◆◆◆◆ ',\n ' ◆◆◆◆◆◆◆◆◆◆◆◆◆◆ ',\n ' ◆◆◆ 2K ◆◆◆◆◆ ',\n ' ◆◆◆◆◆◆◆◆◆◆◆◆◆◆ ',\n ' ◆◆◆◆◆◆◆◆◆◆◆◆◆◆ ',\n ' ◆◆◆◆◆◆◆◆◆◆◆◆◆◆ ',\n ' ◆◆◆◆◆◆◆◆◆◆◆◆◆◆ ',\n ],\n 'army-of-thousands': [\n ' ················· ',\n ' ·◆·◆·◆·◆·◆·◆·◆·◆ ',\n ' ················· ',\n ' ·◆·◆·5K·◆·◆·◆·◆· ',\n ' ················· ',\n ' ·◆·◆·◆·◆·◆·◆·◆·◆ ',\n ' ················· ',\n ' ·◆·◆·◆·◆·◆·◆·◆·◆ ',\n ' ················· ',\n ],\n 'singularity': [\n ' ╲ │ ╱ ─ · ',\n ' ╲ │ ╱ ',\n ' ╲│╱ ',\n ' ────◆──── ',\n ' ╱│╲ ',\n ' ╱ │ ╲ ',\n ' ╱ │ ╲ ─ · ',\n ' 10000 agents ',\n ],\n 'first-shift': [\n ' ┌─────┐ ',\n ' │ ╲ │ ',\n ' ╱│ ╲ │╲ ',\n ' ╱ │ ╲ │ ╲ ',\n ' ╲ │ ╲│ ╱ ',\n ' ╲│ │╱ ',\n ' │ ╱ │ ',\n ' └─────┘ ',\n ' 10 hrs ',\n ],\n 'workaholic': [\n ' ╭─────────╮ ',\n ' ╱ 12 ╲ ',\n ' │ · │ │ ',\n ' │9 · │ 3 │ ',\n ' │ ╲ │ ',\n ' ╲ 6 · ╱ ',\n ' ╰─────────╯ ',\n ' 100 hrs ',\n ],\n 'time-lord': [\n ' ╔═══════════╗ ',\n ' ╱ ╲ ',\n ' │ · 11 12 1 · │ ',\n ' │ 10 ╲ 2 │ ',\n ' │ 9 · 3 │ ',\n ' │ 8 4 │ ',\n ' ╲ 7 5 ╱ ',\n ' ╚═══════════╝ ',\n ' 500 hrs ',\n ],\n 'eternal-grind': [\n ' ╭───╮ ╭───╮ ',\n ' ╱ ╲ ╱ ╲ ',\n ' │ ∞ ╳ ∞ │ ',\n ' ╲ ╱ ╲ ╱ ',\n ' ╰───╯ ╰───╯ ',\n ' ┌─────┐ ',\n ' │ ╲ ╱ │ ',\n ' │ × │ ',\n ' └─────┘ ',\n ],\n 'epoch': [\n ' · ★ · ',\n ' · ╱│╲ · ',\n ' ╲ ╱ │ ╲ ╱ ',\n ' ╲╱ │ ╲╱ ',\n ' ────◆──┼──◆──── ',\n ' ╱╲ │ ╱╲ ',\n ' ╱ ╲ │ ╱ ╲ ',\n ' · ╲│╱ · ',\n ' 5000 hrs ',\n ],\n 'seasoned': [\n ' ╱╲ ',\n ' ╱ ╲ ',\n ' ╱╱╲╱╲╲ ',\n ' ╱╱ ╲╲ ',\n ' ╱╱╱╱╲╱╲╲╲╲ ',\n ' ║║ ',\n ' ─────╨╨───── ',\n ' ╲╲╲╲╲╲╲╲╲╲╲╲╲ ',\n ' 90 day roots ',\n ],\n 'omnipresent': [\n ' N ',\n ' · △ · ',\n ' NW ╭─┼─╮ NE ',\n ' · ╭─┤·+·├─╮ · ',\n ' W──┤·│ · │·├──E ',\n ' · ╰─┤·+·├─╯ · ',\n ' SW ╰─┼─╯ SE ',\n ' · ▽ · ',\n ' S ',\n ],\n 'apprentice': [\n ' ',\n ' ╱╲ ',\n ' ╱ ╲ ',\n ' ╱ ╲ ',\n ' ╱ ╲ ',\n ' ╱────────╲ ',\n ' ╱ level 5 ╲ ',\n ' ▔▔▔▔▔▔▔▔▔▔▔▔ ',\n ],\n 'journeyman': [\n ' ╱╲ ',\n ' ╱ ╲ ╱╲ ',\n ' ╱ ╲ ╱ ╲ ',\n ' ╱ ╳ ╲ ',\n ' ╱ ╱ ╲ ╲ ',\n ' ╱──────╱───╲────╲',\n ' ╱ level 15 ',\n ' ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ',\n ],\n 'master': [\n ' ★╲╱★ ',\n ' ╱╲ ',\n ' ╱ ╲ ',\n ' ╱╱╲╱╲╲ ',\n ' ╱╱ ╲╲ ',\n ' ╱╱ ╲╲ ',\n ' ╱─────────╲╲ ',\n ' ╱ level 30 ╲ ',\n ' ▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ',\n ],\n 'grandmaster': [\n ' ★ ★ ★ ',\n ' ╲│╱ ',\n ' ╱╲ ',\n ' ╱ ╲ ',\n ' ╱╱╲╱╲╲ ',\n ' ╱╱ ╲╲ ',\n ' ╱╱ ★ ╲╲ ',\n ' ╱─────────╲╲ ',\n ' ╱ level 50 ╲ ',\n ],\n\n // ── Session ───────────────────────────────────────────────────────────────\n 'marathon': [\n ' ╭──────────────╮ ',\n ' │ ╱╲ ╱╲ ╱╲ ╱╲ │ ',\n ' │╱ ╳ ╳ ╳ ╲ │ ',\n ' │ ╱╲ ╱╲ ╱╲ │ ',\n ' │ ╱ ╳ ╳ ╲ │ ',\n ' │ ╱ ╱ ╲╱ ╲ ╲ │ ',\n ' │╱ ╱ ╲ ╲│ ',\n ' ├──────────────┤ ',\n ' │ ~ ^ ~ │ ',\n ' ╰──────────────╯ ',\n ],\n 'blitz': [\n ' ╱╱ ',\n ' ╱╱ ',\n ' ╱╱ ',\n ' ╱╱╱╱╱╱ ',\n ' ╱╱ ',\n ' ╱╱ ',\n ' ╱╱ ',\n ' ╱╱ ',\n ],\n 'speed-run': [\n ' ╭──────────╮ ',\n ' ╱ ╲ ',\n ' │ 10 ╱╱ │ ',\n ' │ · ╱╱ │ ',\n ' │ ╱ ╱╱ │ ',\n ' │ ╱ ╱╱ │ ',\n ' ╲ ╱╱ ╱ ',\n ' ╰──────────╯ ',\n ],\n 'flawless': [\n ' ✦ ✦ ',\n ' ✦ ╱╲ ✦ ',\n ' ╔══╧═══╗ ',\n ' ✦ ║ ║ ✦ ',\n ' ║ ◆ ║ ',\n ' ║ ║ ',\n ' ✦ ╚══════╝ ✦ ',\n ' ✦ ✦ ',\n ],\n 'iron-will': [\n ' ╔═══════════╗ ',\n ' ║ ┌───────┐ ║ ',\n ' ║ │╔═════╗│ ║ ',\n ' ║ │║ ◆◆◆ ║│ ║ ',\n ' ║ │║ ◆◆◆ ║│ ║ ',\n ' ║ │╚═════╝│ ║ ',\n ' ║ └───────┘ ║ ',\n ' ╚═══════════╝ ',\n ],\n 'glass-cannon': [\n ' ╱╲ ',\n ' ╱╱╲╲ ',\n ' ╱╱ ╲╲ ',\n ' ╱╱ ╳╳ ╲╲ ',\n ' ╱╱ ╳╳ ╲╲ ',\n ' ╲╲ ╳╳ ╱╱ ',\n ' ╲╲ ╱╱ ',\n ' ╲╲ ╱╱ ',\n ' ╲╲╱╱ ',\n ' ═══╧╧═══ ',\n ],\n 'solo': [\n ' ',\n ' ╭─────────╮ ',\n ' │ │ ',\n ' │ ◆ │ ',\n ' │ │ ',\n ' ╰─────────╯ ',\n ' ',\n ],\n 'one-more-cycle': [\n ' ╭──→──╮ ',\n ' │ │ ',\n ' ↑ ∞ ↓ ',\n ' │ │ ',\n ' ╰──←──╯ ',\n ' ',\n ' ╭──→──╮ ',\n ' │ │ ',\n ' ↑ ∞ ↓ ',\n ' │ │ ',\n ' ╰──←──╯ ',\n ],\n 'quick-draw': [\n ' ╱│ ',\n ' ╱ │ ',\n ' ──╱──│── ',\n ' ╱ │ ',\n ' ╱ ╭─╯ ',\n ' ╱ │ ',\n ' ╱ ╭─╯ ',\n ' │ ',\n ' ──╨── ',\n ],\n 'squad': [\n ' ',\n ' ◆ ◆ ◆ ◆ ',\n ' ',\n ' ◆ ◆ ◆ ◆ ',\n ' ',\n ' ◆ ◆ ',\n ' ',\n ' · 10 strong · ',\n ],\n 'battalion': [\n ' ◆◆◆◆◆◆◆◆◆◆◆◆◆ ',\n ' ',\n ' ◆◆◆◆◆◆◆◆◆◆◆◆◆ ',\n ' ',\n ' ◆◆◆◆◆◆◆◆◆◆◆◆◆ ',\n ' ',\n ' · 25 strong · ',\n ],\n 'swarm': [\n ' ◆·◆·◆·◆·◆·◆·◆·◆· ',\n ' ·◆·◆·◆·◆·◆·◆·◆·◆ ',\n ' ◆·◆·◆·◆·◆·◆·◆·◆· ',\n ' ·◆·◆·◆·◆·◆·◆·◆·◆ ',\n ' ◆·◆·◆·◆·◆·◆·◆·◆· ',\n ' ·◆·◆·◆·◆·◆·◆·◆·◆ ',\n ' · 50 strong · ',\n ],\n 'deep-dive': [\n ' ≈≈≈≈≈≈≈≈≈≈≈≈≈ ',\n ' ↓ ',\n ' ╱│╲ ',\n ' │ ',\n ' │ ',\n ' ▼ ',\n ' · · · · · · · ',\n ' 15 deep ',\n ],\n 'abyss': [\n ' ≈≈≈≈≈≈≈≈≈≈≈≈≈≈ ',\n ' │ ',\n ' │ ',\n ' │ ',\n ' ▼ ',\n ' │ ',\n ' │ ',\n ' ▼ ',\n ' 25 deep ',\n ],\n 'eternal-recurrence': [\n ' ╭──→──╮ ',\n ' ╱ ∞ ╲ ',\n ' │ ╭───╮ │ ',\n ' │ │ ∞ │ │ ',\n ' │ ╰───╯ │ ',\n ' ╲ ∞ ╱ ',\n ' ╰──←──╯ ',\n ' 40 cycles ',\n ],\n 'endurance': [\n ' ',\n ' ╔══════════════╗ ',\n ' ║▓▓▓▓▓▓▓▓▓▓▓▓▓║ ',\n ' ╚══════════════╝ ',\n ' ',\n ' ╔══════════════╗ ',\n ' ║▓▓▓▓▓▓▓▓▓ ║ ',\n ' ╚══════════════╝ ',\n ' 4 hours ',\n ],\n 'ultramarathon': [\n ' ╔══════════════╗ ',\n ' ║▓▓▓▓▓▓▓▓▓▓▓▓▓║ ',\n ' ╚══════════════╝ ',\n ' ─────────────→ ',\n ' · ',\n ' · ',\n ' · ',\n ' · ',\n ' 6 hours ',\n ],\n 'one-shot': [\n ' ╭───────╮ ',\n ' │ ╭───╮ │ ',\n ' │ │ ◉ │ │ ',\n ' │ ╰───╯ │ ',\n ' ╰───────╯ ',\n ' ↑ ',\n ' ╱│ ',\n ' ╱ │ ',\n ' → ◉ │ ',\n ],\n 'flash': [\n ' ╱╱ ',\n ' ╱╱ ',\n ' ╱╱ ',\n ' ╱╱╱╱╱╱ ',\n ' ╱╱ ',\n ' ╱╱ ',\n ' ╱╱ ',\n ' ╱╱ ',\n ' < 2 min ',\n ],\n\n // ── Time ──────────────────────────────────────────────────────────────────\n 'night-owl': [\n ' ☽ ',\n ' ╭─────╮ · ',\n ' ╱ ◉ ◉ ╲ · ',\n ' │ ▽ │ · ',\n ' │ ╱───╲ │ ',\n ' ╲╱ ╲╱ ',\n ' ╱╲ ╱╲ · ',\n ' ╱ ╲───╱ ╲ · ',\n ' ▔▔▔▔▔▔▔▔▔▔▔ ',\n ],\n 'dawn-patrol': [\n ' ',\n ' ─ ─ ─ ─ ─ ─ ─ ─ ',\n ' ╲ │ ╱ ',\n ' ╲ │ ╱ ',\n ' ────◑──── ',\n ' ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ',\n ' ░░░░░░░░░░░░░░░░ ',\n ' ',\n ],\n 'early-bird': [\n ' ╱╱ ',\n ' ◠ ╱╱ ',\n ' ╱ ╲ ╱╱ ',\n ' │ ◉ ╲>╱ ',\n ' │ ═╱ ',\n ' ╲ ╱ ',\n ' ╲╱╲ ',\n ' ╲ ╲ ',\n ' ▔▔ ',\n ],\n 'weekend-warrior': [\n ' ╔═══════════╗ ',\n ' ║ S M T W T ║ ',\n ' ║ ║ ',\n ' ║ ◆ ║ ',\n ' ║ ◆ ║ ',\n ' ╚═══════════╝ ',\n ],\n 'all-nighter': [\n ' ╭─────────╮ ',\n ' ╱ 12 ╲ ',\n ' │ · │ │ ',\n ' │9 │ 3 │ ',\n ' │ · │ ',\n ' ╲ 6 ╱ ',\n ' ╰─────────╯ ',\n ' ∞ ∞ ∞ ',\n ],\n 'witching-hour': [\n ' · · ✦ · · ',\n ' · ╱╲ · ',\n ' ╱ ╲ ',\n ' │ 3:00 │ ',\n ' │ AM │ ',\n ' ╲ ╱ ',\n ' · ╲╱ · ',\n ' · · ✦ · · ',\n ],\n\n // ── Behavioral ────────────────────────────────────────────────────────────\n 'sisyphean': [\n ' ╱ ',\n ' ╱ ◉ ',\n ' ╱ ◉◉◉ ',\n ' ╱ ◉◉◉ ',\n ' ╱ ◉ ',\n ' ╱ ; ',\n ' ╱ (>.<) ',\n ' ╱ ╱╱ ╲╲ ',\n ' ╱ ╱ ╲ ',\n ],\n 'stubborn': [\n ' ╱╱╱╱╱╱╱╱╱ ',\n ' ╱╱╱╱╱╱╱╱╱╱ ',\n ' ◉ ',\n ' ◉◉◉ ',\n ' (>.<) ',\n ' ╱╱╱╱╱╱╱╱╱ ',\n ' ╱╱╱╱╱╱╱╱╱╱ ',\n ' ✓ ',\n ],\n 'creature-of-habit': [\n ' ╭───╮ ╭───╮ ',\n ' │ → │→│ → │→ ',\n ' ╰───╯ ╰───╯ ',\n ' ╭───╮ ╭───╮ ',\n ' │ → │→│ → │→ ',\n ' ╰───╯ ╰───╯ ',\n ' ╭───╮ ╭───╮ ',\n ' │ → │→│ → │→ ',\n ' ╰───╯ ╰───╯ ',\n ],\n 'loyal': [\n ' ╱╲ ',\n ' ╱ ╲ ',\n ' ╱ ♥♥ ╲ ',\n ' ╱ ♥♥ ╲ ',\n ' ╲ 50 ╱ ',\n ' ╲ ╱ ',\n ' ╲ ╱ ',\n ' ╲╱ ',\n ],\n 'wanderer': [\n ' · · · ',\n ' ╲ │ ╱ ',\n ' ╲ │ ╱ ',\n ' · + · ',\n ' ╱ │ ╲ ',\n ' ╱ │ ╲ ',\n ' · · · ',\n ],\n 'streak': [\n ' ╔══╗╔══╗╔══╗╔══╗ ',\n ' ║M ║║T ║║W ║║T ║ ',\n ' ║◆ ║║◆ ║║◆ ║║◆ ║ ',\n ' ╚══╝╚══╝╚══╝╚══╝ ',\n ' ╔══╗╔══╗╔══╗ ',\n ' ║F ║║S ║║S ║ ',\n ' ║◆ ║║◆ ║║◆ ║ ',\n ' ╚══╝╚══╝╚══╝ ',\n ],\n 'hot-streak': [\n ' ╱╲ ',\n ' ╱╱╲╲ ',\n ' ╱╱◆◆╲╲ ',\n ' ╱╱ ◆◆ ╲╲ ',\n ' ╲╲ ◆◆ ╱╱ ',\n ' ╲╲◆◆╱╱ ',\n ' ╲╲╱╱ ',\n ' ×7 clean ',\n ],\n 'momentum': [\n ' ◉ ◉ ',\n ' │╲ ╱│ ',\n ' │ ╲ ╱ │ ',\n ' │ ╲ ╱ │ ',\n ' │ ◉◉ │ ',\n ' │ ╱ ╲ │ ',\n ' │ ╱ ╲ │ ',\n ' │╱ ╲│ ',\n ' ◉ ◉ ',\n ],\n 'patient-one': [\n ' ╭───────╮ ',\n ' │ ‾.‾ │ ',\n ' │ │ ',\n ' │ zzz │ ',\n ' │ zz │ ',\n ' │ z │ ',\n ' │ │ ',\n ' ╰───────╯ ',\n ' 30 min+ ',\n ],\n 'message-in-a-bottle': [\n ' ╭─╮ ',\n ' │×│ ',\n ' ╭─┴─┴─╮ ',\n ' │ ░░░ │ ',\n ' │ ░░░ │ ',\n ' │ ░░░ │ ',\n ' ╰─────╯ ',\n ' ≈≈≈≈≈≈≈≈≈ ',\n ' ≈≈≈≈≈≈≈ ',\n ],\n 'comeback-kid': [\n ' ╱╲ ╱╲ ',\n ' ╱ ╲ ╱ ╲ ',\n ' ╱ ╲ ╱ ╲ ',\n ' ╱ ╳ ╲ ',\n ' ╲ ╱╲ ╱ ',\n ' ╲ ╱ ╲ ╱ ',\n ' ╲ ╱ ╲ ╱ ',\n ' ╲╱ ╲╱ ✓ ',\n ],\n 'pair-programming': [\n ' ╭─────╮╭─────╮ ',\n ' │ ◉ ◉ ││ ◉ ◉ │ ',\n ' │ ▽ ││ ▽ │ ',\n ' ╰──┬──╯╰──┬──╯ ',\n ' │ ╱──╲ │ ',\n ' │╱ ╲│ ',\n ' ╱ ══ ╲ ',\n ' ╱ ════ ╲ ',\n ' ▔▔▔▔▔▔▔▔▔▔ ',\n ],\n 'overdrive': [\n ' ╭──────────╮ ',\n ' ╱ ╭──────╮ ╲ ',\n ' │ ╱ ×6 ╲ │ ',\n ' │ │ ◉ │ │ ',\n ' │ ╲ ╱ │ ',\n ' ╲ ╰──────╯ ╱ ',\n ' ╰──────────╯ ',\n ' same day ×6 ',\n ],\n 'iron-streak': [\n ' ╔══╗─╔══╗─╔══╗ ',\n ' ║◆ ║ ║◆ ║ ║◆ ║ ',\n ' ╚══╝─╚══╝─╚══╝ ',\n ' │ │ ',\n ' ╔══╗─╔══╗─╔══╗ ',\n ' ║◆ ║ ║◆ ║ ║◆ ║ ',\n ' ╚══╝─╚══╝─╚══╝ ',\n ' 14 day chain ',\n ],\n 'deep-conversation': [\n ' ╭─────────╮ ',\n ' ╱ ╭──╮ 20 ╲ ',\n ' │ │ │ │ ',\n ' │ ╰──╯ │ ',\n ' │ · · · · · │ ',\n ' ╲ ╱ ',\n ' ╰──────────╯ ',\n ' ╲ ',\n ' ● ',\n ],\n 'one-must-imagine': [\n ' ◉◉◉◉◉ ',\n ' ╱ ◉◉◉◉ ',\n ' ╱ ◉ ╱ ←─╮ ',\n '╱ (>.<) │ ',\n ' ╱╱ ╲╲ │ ',\n ' ╱ ╲ │ ',\n ' ╱ ╲─╯ ',\n ' ▔▔▔▔▔▔▔▔▔▔▔ ',\n ' ×10 restarts ',\n ],\n};\n\n// ---------------------------------------------------------------------------\n// Card rendering\n// ---------------------------------------------------------------------------\n\nconst CARD_WIDTH = 34;\nconst CARD_HEIGHT = 18;\nconst CARD_INNER = CARD_WIDTH - 2;\n\nfunction centerLine(text: string, width: number): string {\n const stripped = stripAnsiForWidth(text);\n if (stripped.length >= width) return text.slice(0, width);\n const pad = Math.floor((width - stripped.length) / 2);\n return ' '.repeat(pad) + text + ' '.repeat(width - stripped.length - pad);\n}\n\nfunction stripAnsiForWidth(s: string): string {\n // eslint-disable-next-line no-control-regex\n return s.replace(/\\x1b\\[[0-9;]*m/g, '');\n}\n\nexport interface BadgeCard {\n lines: string[];\n width: number;\n height: number;\n}\n\nexport function renderBadgeCard(\n def: AchievementDef,\n unlock: UnlockedAchievement | null,\n opts?: { dim?: boolean },\n): BadgeCard {\n const dim = opts?.dim === true || unlock === null;\n const art = BADGE_ART[def.id] ?? [];\n\n const lines: string[] = [];\n\n // Top border\n const category = def.category.toUpperCase();\n const borderLabel = ` ${category} `;\n const topPad = CARD_INNER - borderLabel.length - 2;\n const topLeft = Math.floor(topPad / 2);\n const topRight = topPad - topLeft;\n lines.push(`┌${'─'.repeat(topLeft)}${borderLabel}${'─'.repeat(topRight)}┐`);\n\n // Blank\n lines.push(`│${' '.repeat(CARD_INNER)}│`);\n\n // Art (centered, dimmed if locked)\n const artMaxLines = 9;\n const artSlice = art.slice(0, artMaxLines);\n for (const artLine of artSlice) {\n const centered = centerLine(artLine, CARD_INNER);\n lines.push(`│${dim ? dimText(centered) : centered}│`);\n }\n // Pad remaining art lines\n for (let i = artSlice.length; i < artMaxLines; i++) {\n lines.push(`│${' '.repeat(CARD_INNER)}│`);\n }\n\n // Blank\n lines.push(`│${' '.repeat(CARD_INNER)}│`);\n\n // Name (centered, bold if unlocked)\n const nameText = unlock !== null ? def.name : `? ${def.name} ?`;\n lines.push(`│${centerLine(nameText, CARD_INNER)}│`);\n\n // Description (centered, wrapped if needed)\n const descLines = wrapText(def.description, CARD_INNER - 4);\n for (const dl of descLines.slice(0, 2)) {\n const centered = centerLine(dl, CARD_INNER);\n lines.push(`│${dim ? dimText(centered) : centered}│`);\n }\n // Pad to fixed height\n const usedContent = 1 + 1 + artMaxLines + 1 + 1 + Math.min(descLines.length, 2);\n const remaining = CARD_HEIGHT - 2 - usedContent; // -2 for borders\n for (let i = 0; i < remaining; i++) {\n if (i === remaining - 1 && unlock !== null) {\n // Unlock date on last content line\n const dateStr = unlock.unlockedAt.slice(0, 10);\n lines.push(`│${centerLine(dimText(dateStr), CARD_INNER)}│`);\n } else {\n lines.push(`│${' '.repeat(CARD_INNER)}│`);\n }\n }\n\n // Bottom border\n lines.push(`└${'─'.repeat(CARD_INNER)}┘`);\n\n return { lines, width: CARD_WIDTH, height: lines.length };\n}\n\nfunction dimText(text: string): string {\n return `\\x1b[2m${text}\\x1b[22m`;\n}\n\nfunction wrapText(text: string, maxWidth: number): string[] {\n const words = text.split(' ');\n const lines: string[] = [];\n let current = '';\n for (const word of words) {\n if (current.length + word.length + 1 > maxWidth && current.length > 0) {\n lines.push(current);\n current = word;\n } else {\n current = current.length > 0 ? `${current} ${word}` : word;\n }\n }\n if (current.length > 0) lines.push(current);\n return lines;\n}\n\n// ---------------------------------------------------------------------------\n// Gallery data\n// ---------------------------------------------------------------------------\n\nexport interface BadgeGallery {\n achievements: AchievementDef[];\n unlocked: Map<AchievementId, UnlockedAchievement>;\n currentIndex: number;\n total: number;\n}\n\nexport function createBadgeGallery(\n unlockedAchievements: UnlockedAchievement[],\n startIndex?: number,\n): BadgeGallery {\n const unlocked = new Map<AchievementId, UnlockedAchievement>();\n for (const a of unlockedAchievements) {\n unlocked.set(a.id, a);\n }\n\n // Sort: unlocked first (by unlock date), then locked\n const sorted = [...ACHIEVEMENTS].sort((a, b) => {\n const aUnlocked = unlocked.has(a.id);\n const bUnlocked = unlocked.has(b.id);\n if (aUnlocked && !bUnlocked) return -1;\n if (!aUnlocked && bUnlocked) return 1;\n if (aUnlocked && bUnlocked) {\n const aDate = unlocked.get(a.id)!.unlockedAt;\n const bDate = unlocked.get(b.id)!.unlockedAt;\n return aDate.localeCompare(bDate);\n }\n return 0; // preserve category order for locked\n });\n\n return {\n achievements: sorted,\n unlocked,\n currentIndex: startIndex ?? 0,\n total: sorted.length,\n };\n}\n\nexport function galleryNext(gallery: BadgeGallery): number {\n return (gallery.currentIndex + 1) % gallery.total;\n}\n\nexport function galleryPrev(gallery: BadgeGallery): number {\n return (gallery.currentIndex - 1 + gallery.total) % gallery.total;\n}\n\nexport { CARD_WIDTH, CARD_HEIGHT };\n","import { connect } from 'node:net';\nimport { socketPath } from './paths.js';\nimport type { Request, Response } from './protocol.js';\n\nexport function rawSend(request: Request, timeoutMs = 10_000): Promise<Response> {\n const sock = socketPath();\n\n return new Promise<Response>((resolve, reject) => {\n const socket = connect(sock);\n let data = '';\n\n const timeout = setTimeout(() => {\n socket.destroy();\n reject(new Error(`Request timed out after ${(timeoutMs / 1000).toFixed(0)}s. The daemon may be overloaded.\\n Check: sisyphus doctor\\n Logs: tail -20 ~/.sisyphus/daemon.log`));\n }, timeoutMs);\n\n socket.on('connect', () => {\n socket.write(JSON.stringify(request) + '\\n');\n });\n\n socket.on('data', (chunk) => {\n data += chunk.toString();\n const newlineIdx = data.indexOf('\\n');\n if (newlineIdx !== -1) {\n clearTimeout(timeout);\n const line = data.slice(0, newlineIdx);\n socket.destroy();\n try {\n resolve(JSON.parse(line) as Response);\n } catch {\n reject(new Error(`Invalid JSON response from daemon: ${line}`));\n }\n }\n });\n\n socket.on('error', (err) => {\n clearTimeout(timeout);\n reject(err);\n });\n });\n}\n"],"mappings":"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AA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,iBAAiB;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,UAAU;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,qBAAqB;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,eAAe;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,eAAe;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,cAAc;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,aAAa;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,iBAAiB;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,YAAY;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,eAAe;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,cAAc;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,cAAc;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,UAAU;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,eAAe;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA;AAAA,EAGA,YAAY;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,aAAa;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,YAAY;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,aAAa;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,gBAAgB;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,kBAAkB;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,cAAc;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,aAAa;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,aAAa;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,sBAAsB;AAAA,IACpB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,aAAa;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,iBAAiB;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,YAAY;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA;AAAA,EAGA,aAAa;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,eAAe;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,cAAc;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,mBAAmB;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,eAAe;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,iBAAiB;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA;AAAA,EAGA,aAAa;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,YAAY;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,qBAAqB;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,YAAY;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,UAAU;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,cAAc;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,YAAY;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,eAAe;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,uBAAuB;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,gBAAgB;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,oBAAoB;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,aAAa;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,eAAe;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,qBAAqB;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,oBAAoB;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAMA,IAAM,aAAa;AACnB,IAAM,cAAc;AACpB,IAAM,aAAa,aAAa;AAEhC,SAAS,WAAW,MAAc,OAAuB;AACvD,QAAM,WAAW,kBAAkB,IAAI;AACvC,MAAI,SAAS,UAAU,MAAO,QAAO,KAAK,MAAM,GAAG,KAAK;AACxD,QAAM,MAAM,KAAK,OAAO,QAAQ,SAAS,UAAU,CAAC;AACpD,SAAO,IAAI,OAAO,GAAG,IAAI,OAAO,IAAI,OAAO,QAAQ,SAAS,SAAS,GAAG;AAC1E;AAEA,SAAS,kBAAkB,GAAmB;AAE5C,SAAO,EAAE,QAAQ,mBAAmB,EAAE;AACxC;AAQO,SAAS,gBACd,KACA,QACA,MACW;AACX,QAAM,MAAM,MAAM,QAAQ,QAAQ,WAAW;AAC7C,QAAM,MAAM,UAAU,IAAI,EAAE,KAAK,CAAC;AAElC,QAAM,QAAkB,CAAC;AAGzB,QAAM,WAAW,IAAI,SAAS,YAAY;AAC1C,QAAM,cAAc,IAAI,QAAQ;AAChC,QAAM,SAAS,aAAa,YAAY,SAAS;AACjD,QAAM,UAAU,KAAK,MAAM,SAAS,CAAC;AACrC,QAAM,WAAW,SAAS;AAC1B,QAAM,KAAK,SAAI,SAAI,OAAO,OAAO,CAAC,GAAG,WAAW,GAAG,SAAI,OAAO,QAAQ,CAAC,QAAG;AAG1E,QAAM,KAAK,SAAI,IAAI,OAAO,UAAU,CAAC,QAAG;AAGxC,QAAM,cAAc;AACpB,QAAM,WAAW,IAAI,MAAM,GAAG,WAAW;AACzC,aAAW,WAAW,UAAU;AAC9B,UAAM,WAAW,WAAW,SAAS,UAAU;AAC/C,UAAM,KAAK,SAAI,MAAM,QAAQ,QAAQ,IAAI,QAAQ,QAAG;AAAA,EACtD;AAEA,WAAS,IAAI,SAAS,QAAQ,IAAI,aAAa,KAAK;AAClD,UAAM,KAAK,SAAI,IAAI,OAAO,UAAU,CAAC,QAAG;AAAA,EAC1C;AAGA,QAAM,KAAK,SAAI,IAAI,OAAO,UAAU,CAAC,QAAG;AAGxC,QAAM,WAAW,WAAW,OAAO,IAAI,OAAO,KAAK,IAAI,IAAI;AAC3D,QAAM,KAAK,SAAI,WAAW,UAAU,UAAU,CAAC,QAAG;AAGlD,QAAM,YAAY,SAAS,IAAI,aAAa,aAAa,CAAC;AAC1D,aAAW,MAAM,UAAU,MAAM,GAAG,CAAC,GAAG;AACtC,UAAM,WAAW,WAAW,IAAI,UAAU;AAC1C,UAAM,KAAK,SAAI,MAAM,QAAQ,QAAQ,IAAI,QAAQ,QAAG;AAAA,EACtD;AAEA,QAAM,cAAc,IAAI,IAAI,cAAc,IAAI,IAAI,KAAK,IAAI,UAAU,QAAQ,CAAC;AAC9E,QAAM,YAAY,cAAc,IAAI;AACpC,WAAS,IAAI,GAAG,IAAI,WAAW,KAAK;AAClC,QAAI,MAAM,YAAY,KAAK,WAAW,MAAM;AAE1C,YAAM,UAAU,OAAO,WAAW,MAAM,GAAG,EAAE;AAC7C,YAAM,KAAK,SAAI,WAAW,QAAQ,OAAO,GAAG,UAAU,CAAC,QAAG;AAAA,IAC5D,OAAO;AACL,YAAM,KAAK,SAAI,IAAI,OAAO,UAAU,CAAC,QAAG;AAAA,IAC1C;AAAA,EACF;AAGA,QAAM,KAAK,SAAI,SAAI,OAAO,UAAU,CAAC,QAAG;AAExC,SAAO,EAAE,OAAO,OAAO,YAAY,QAAQ,MAAM,OAAO;AAC1D;AAEA,SAAS,QAAQ,MAAsB;AACrC,SAAO,UAAU,IAAI;AACvB;AAEA,SAAS,SAAS,MAAc,UAA4B;AAC1D,QAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,QAAM,QAAkB,CAAC;AACzB,MAAI,UAAU;AACd,aAAW,QAAQ,OAAO;AACxB,QAAI,QAAQ,SAAS,KAAK,SAAS,IAAI,YAAY,QAAQ,SAAS,GAAG;AACrE,YAAM,KAAK,OAAO;AAClB,gBAAU;AAAA,IACZ,OAAO;AACL,gBAAU,QAAQ,SAAS,IAAI,GAAG,OAAO,IAAI,IAAI,KAAK;AAAA,IACxD;AAAA,EACF;AACA,MAAI,QAAQ,SAAS,EAAG,OAAM,KAAK,OAAO;AAC1C,SAAO;AACT;AAaO,SAAS,mBACd,sBACA,YACc;AACd,QAAM,WAAW,oBAAI,IAAwC;AAC7D,aAAW,KAAK,sBAAsB;AACpC,aAAS,IAAI,EAAE,IAAI,CAAC;AAAA,EACtB;AAGA,QAAM,SAAS,CAAC,GAAG,YAAY,EAAE,KAAK,CAAC,GAAG,MAAM;AAC9C,UAAM,YAAY,SAAS,IAAI,EAAE,EAAE;AACnC,UAAM,YAAY,SAAS,IAAI,EAAE,EAAE;AACnC,QAAI,aAAa,CAAC,UAAW,QAAO;AACpC,QAAI,CAAC,aAAa,UAAW,QAAO;AACpC,QAAI,aAAa,WAAW;AAC1B,YAAM,QAAQ,SAAS,IAAI,EAAE,EAAE,EAAG;AAClC,YAAM,QAAQ,SAAS,IAAI,EAAE,EAAE,EAAG;AAClC,aAAO,MAAM,cAAc,KAAK;AAAA,IAClC;AACA,WAAO;AAAA,EACT,CAAC;AAED,SAAO;AAAA,IACL,cAAc;AAAA,IACd;AAAA,IACA,cAAc,cAAc;AAAA,IAC5B,OAAO,OAAO;AAAA,EAChB;AACF;AAEO,SAAS,YAAY,SAA+B;AACzD,UAAQ,QAAQ,eAAe,KAAK,QAAQ;AAC9C;AAEO,SAAS,YAAY,SAA+B;AACzD,UAAQ,QAAQ,eAAe,IAAI,QAAQ,SAAS,QAAQ;AAC9D;;;ACz2BA,SAAS,eAAe;AAIjB,SAAS,QAAQ,SAAkB,YAAY,KAA2B;AAC/E,QAAM,OAAO,WAAW;AAExB,SAAO,IAAI,QAAkB,CAAC,SAAS,WAAW;AAChD,UAAM,SAAS,QAAQ,IAAI;AAC3B,QAAI,OAAO;AAEX,UAAM,UAAU,WAAW,MAAM;AAC/B,aAAO,QAAQ;AACf,aAAO,IAAI,MAAM,4BAA4B,YAAY,KAAM,QAAQ,CAAC,CAAC;AAAA;AAAA,wCAAqG,CAAC;AAAA,IACjL,GAAG,SAAS;AAEZ,WAAO,GAAG,WAAW,MAAM;AACzB,aAAO,MAAM,KAAK,UAAU,OAAO,IAAI,IAAI;AAAA,IAC7C,CAAC;AAED,WAAO,GAAG,QAAQ,CAAC,UAAU;AAC3B,cAAQ,MAAM,SAAS;AACvB,YAAM,aAAa,KAAK,QAAQ,IAAI;AACpC,UAAI,eAAe,IAAI;AACrB,qBAAa,OAAO;AACpB,cAAM,OAAO,KAAK,MAAM,GAAG,UAAU;AACrC,eAAO,QAAQ;AACf,YAAI;AACF,kBAAQ,KAAK,MAAM,IAAI,CAAa;AAAA,QACtC,QAAQ;AACN,iBAAO,IAAI,MAAM,sCAAsC,IAAI,EAAE,CAAC;AAAA,QAChE;AAAA,MACF;AAAA,IACF,CAAC;AAED,WAAO,GAAG,SAAS,CAAC,QAAQ;AAC1B,mBAAa,OAAO;AACpB,aAAO,GAAG;AAAA,IACZ,CAAC;AAAA,EACH,CAAC;AACH;","names":["readFileSync","readFileSync"]}
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/shared/paths.ts
4
4
  import { homedir } from "os";
5
- import { join } from "path";
5
+ import { basename, join } from "path";
6
6
  function globalDir() {
7
7
  return join(homedir(), ".sisyphus");
8
8
  }
@@ -81,6 +81,18 @@ function snapshotDir(cwd, sessionId, cycle) {
81
81
  function tuiScratchDir(cwd, sessionId) {
82
82
  return join(sessionDir(cwd, sessionId), ".tui");
83
83
  }
84
+ function tmuxSessionName(cwd, sessionLabel) {
85
+ return `ssyph_${basename(cwd)}_${sessionLabel}`;
86
+ }
87
+ function companionPath() {
88
+ return join(globalDir(), "companion.json");
89
+ }
90
+ function isSisyphusSession(name) {
91
+ return name.startsWith("ssyph_");
92
+ }
93
+ function tmuxSessionDisplayName(name) {
94
+ return name.replace(/^ssyph_[^_]+_/, "");
95
+ }
84
96
 
85
97
  export {
86
98
  globalDir,
@@ -108,6 +120,10 @@ export {
108
120
  legacyLogsPath,
109
121
  snapshotsDir,
110
122
  snapshotDir,
111
- tuiScratchDir
123
+ tuiScratchDir,
124
+ tmuxSessionName,
125
+ companionPath,
126
+ isSisyphusSession,
127
+ tmuxSessionDisplayName
112
128
  };
113
- //# sourceMappingURL=chunk-GSXF3TCZ.js.map
129
+ //# sourceMappingURL=chunk-TMBAVPHH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shared/paths.ts"],"sourcesContent":["import { homedir } from 'node:os';\nimport { basename, join } from 'node:path';\n\nexport function globalDir(): string {\n return join(homedir(), '.sisyphus');\n}\n\nexport function socketPath(): string {\n return join(globalDir(), 'daemon.sock');\n}\n\nexport function globalConfigPath(): string {\n return join(globalDir(), 'config.json');\n}\n\nexport function daemonLogPath(): string {\n return join(globalDir(), 'daemon.log');\n}\n\nexport function daemonPidPath(): string {\n return join(globalDir(), 'daemon.pid');\n}\n\nexport function daemonUpdatingPath(): string {\n return join(globalDir(), 'updating');\n}\n\nexport function projectDir(cwd: string): string {\n return join(cwd, '.sisyphus');\n}\n\nexport function projectConfigPath(cwd: string): string {\n return join(projectDir(cwd), 'config.json');\n}\n\nexport function projectOrchestratorPromptPath(cwd: string): string {\n return join(projectDir(cwd), 'orchestrator.md');\n}\n\nexport function sessionsDir(cwd: string): string {\n return join(projectDir(cwd), 'sessions');\n}\n\nexport function sessionDir(cwd: string, sessionId: string): string {\n return join(sessionsDir(cwd), sessionId);\n}\n\nexport function statePath(cwd: string, sessionId: string): string {\n return join(sessionDir(cwd, sessionId), 'state.json');\n}\n\nexport function reportsDir(cwd: string, sessionId: string): string {\n return join(sessionDir(cwd, sessionId), 'reports');\n}\n\nexport function reportFilePath(cwd: string, sessionId: string, agentId: string, suffix: string): string {\n return join(reportsDir(cwd, sessionId), `${agentId}-${suffix}.md`);\n}\n\nexport function messagesDir(cwd: string, sessionId: string): string {\n return join(sessionDir(cwd, sessionId), 'messages');\n}\n\nexport function promptsDir(cwd: string, sessionId: string): string {\n return join(sessionDir(cwd, sessionId), 'prompts');\n}\n\nexport function contextDir(cwd: string, sessionId: string): string {\n return join(sessionDir(cwd, sessionId), 'context');\n}\n\nexport function roadmapPath(cwd: string, sessionId: string): string {\n return join(sessionDir(cwd, sessionId), 'roadmap.md');\n}\n\nexport function goalPath(cwd: string, sessionId: string): string {\n return join(sessionDir(cwd, sessionId), 'goal.md');\n}\n\nexport function strategyPath(cwd: string, sessionId: string): string {\n return join(sessionDir(cwd, sessionId), 'strategy.md');\n}\n\nexport function logsDir(cwd: string, sessionId: string): string {\n return join(sessionDir(cwd, sessionId), 'logs');\n}\n\nexport function cycleLogPath(cwd: string, sessionId: string, cycle: number): string {\n return join(logsDir(cwd, sessionId), `cycle-${String(cycle).padStart(3, '0')}.md`);\n}\n\n// Backwards compat for old sessions\nexport function legacyLogsPath(cwd: string, sessionId: string): string {\n return join(sessionDir(cwd, sessionId), 'logs.md');\n}\n\nexport function snapshotsDir(cwd: string, sessionId: string): string {\n return join(sessionDir(cwd, sessionId), 'snapshots');\n}\n\nexport function snapshotDir(cwd: string, sessionId: string, cycle: number): string {\n return join(snapshotsDir(cwd, sessionId), `cycle-${cycle}`);\n}\n\nexport function tuiScratchDir(cwd: string, sessionId: string): string {\n return join(sessionDir(cwd, sessionId), '.tui');\n}\n\nexport function tmuxSessionName(cwd: string, sessionLabel: string): string {\n // Use underscores as separators — slashes break tmux -t target resolution,\n // dots get silently converted to underscores by tmux (reserved for window.pane targeting)\n return `ssyph_${basename(cwd)}_${sessionLabel}`;\n}\n\nexport function companionPath(): string {\n return join(globalDir(), 'companion.json');\n}\n\nexport function isSisyphusSession(name: string): boolean {\n return name.startsWith('ssyph_');\n}\n\nexport function tmuxSessionDisplayName(name: string): string {\n return name.replace(/^ssyph_[^_]+_/, '');\n}\n\n"],"mappings":"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","names":[]}
@@ -0,0 +1,299 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ globalConfigPath,
4
+ projectConfigPath
5
+ } from "./chunk-TMBAVPHH.js";
6
+
7
+ // src/shared/config.ts
8
+ import { readFileSync } from "fs";
9
+ var DEFAULT_CONFIG = {
10
+ pollIntervalMs: 5e3,
11
+ orchestratorEffort: "high",
12
+ agentEffort: "medium",
13
+ notifications: {
14
+ enabled: true,
15
+ sound: "/System/Library/Sounds/Hero.aiff"
16
+ },
17
+ requiredPlugins: [
18
+ { name: "devcore", marketplace: "crouton-kit" }
19
+ ]
20
+ };
21
+ function readJsonFile(filePath) {
22
+ try {
23
+ const content = readFileSync(filePath, "utf-8");
24
+ return JSON.parse(content);
25
+ } catch {
26
+ return {};
27
+ }
28
+ }
29
+ function loadConfig(cwd) {
30
+ const global = readJsonFile(globalConfigPath());
31
+ const project = readJsonFile(projectConfigPath(cwd));
32
+ return { ...DEFAULT_CONFIG, ...global, ...project };
33
+ }
34
+
35
+ // src/shared/shell.ts
36
+ function shellQuote(s) {
37
+ return `'${s.replace(/'/g, "'\\''")}'`;
38
+ }
39
+
40
+ // src/shared/companion-types.ts
41
+ var ACHIEVEMENTS = [
42
+ // Milestone (25)
43
+ { id: "first-blood", name: "First Blood", category: "milestone", description: "Complete your first session.", badge: null },
44
+ { id: "regular", name: "Regular", category: "milestone", description: "Complete 10 sessions.", badge: null },
45
+ { id: "centurion", name: "Centurion", category: "milestone", description: "Complete 100 sessions.", badge: null },
46
+ { id: "veteran", name: "Veteran", category: "milestone", description: "Complete 500 sessions.", badge: null },
47
+ { id: "thousand-boulder", name: "Thousand Boulder", category: "milestone", description: "Complete 1,000 sessions.", badge: null },
48
+ { id: "cartographer", name: "Cartographer", category: "milestone", description: "Work in 5 different repos.", badge: "+" },
49
+ { id: "world-traveler", name: "World Traveler", category: "milestone", description: "Work in 15 different repos.", badge: null },
50
+ { id: "omnipresent", name: "Omnipresent", category: "milestone", description: "Work in 30 different repos.", badge: null },
51
+ { id: "swarm-starter", name: "Swarm Starter", category: "milestone", description: "Spawn 50 agents over a lifetime.", badge: null },
52
+ { id: "hive-mind", name: "Hive Mind", category: "milestone", description: "Spawn 500 agents over a lifetime.", badge: null },
53
+ { id: "legion", name: "Legion", category: "milestone", description: "Spawn 2,000 agents over a lifetime.", badge: null },
54
+ { id: "army-of-thousands", name: "Army of Thousands", category: "milestone", description: "Spawn 5,000 agents over a lifetime.", badge: null },
55
+ { id: "singularity", name: "Singularity", category: "milestone", description: "Spawn 10,000 agents over a lifetime.", badge: null },
56
+ { id: "first-shift", name: "First Shift", category: "milestone", description: "10 hours of total agent active time.", badge: null },
57
+ { id: "workaholic", name: "Workaholic", category: "milestone", description: "100 hours of total agent active time.", badge: null },
58
+ { id: "time-lord", name: "Time Lord", category: "milestone", description: "500 hours of total agent active time.", badge: null },
59
+ { id: "eternal-grind", name: "Eternal Grind", category: "milestone", description: "2,000 hours of total agent active time.", badge: null },
60
+ { id: "epoch", name: "Epoch", category: "milestone", description: "5,000 hours of total agent active time.", badge: null },
61
+ { id: "old-growth", name: "Old Growth", category: "milestone", description: "Companion is 14 days old.", badge: null },
62
+ { id: "seasoned", name: "Seasoned", category: "milestone", description: "Companion is 90 days old.", badge: null },
63
+ { id: "ancient", name: "Ancient", category: "milestone", description: "Companion is 365 days old.", badge: null },
64
+ { id: "apprentice", name: "Apprentice", category: "milestone", description: "Reach level 5.", badge: null },
65
+ { id: "journeyman", name: "Journeyman", category: "milestone", description: "Reach level 15.", badge: null },
66
+ { id: "master", name: "Master", category: "milestone", description: "Reach level 30.", badge: null },
67
+ { id: "grandmaster", name: "Grandmaster", category: "milestone", description: "Reach level 50.", badge: null },
68
+ // Session (19)
69
+ { id: "marathon", name: "Marathon", category: "session", description: "Complete a session with 15+ agents.", badge: "~^~" },
70
+ { id: "squad", name: "Squad Up", category: "session", description: "Complete a session with 10+ agents.", badge: null },
71
+ { id: "battalion", name: "Battalion", category: "session", description: "Complete a session with 25+ agents.", badge: null },
72
+ { id: "swarm", name: "The Swarm", category: "session", description: "Complete a session with 50+ agents.", badge: null },
73
+ { id: "blitz", name: "Blitz", category: "session", description: "Complete a session in under 5 minutes.", badge: null },
74
+ { id: "speed-run", name: "Speed Run", category: "session", description: "Complete a session in under 15 minutes.", badge: null },
75
+ { id: "flash", name: "Flash", category: "session", description: "Complete a session in under 2 minutes.", badge: null },
76
+ { id: "flawless", name: "Flawless", category: "session", description: "Complete a session with 10+ agents and zero crashes.", badge: "*" },
77
+ { id: "iron-will", name: "Iron Will", category: "session", description: "10 consecutive sessions completing in 3 or fewer cycles.", badge: "[]" },
78
+ { id: "glass-cannon", name: "Glass Cannon", category: "session", description: "5+ agents, all crashed, but session completed anyway.", badge: null },
79
+ { id: "solo", name: "Solo", category: "session", description: "Complete a session with exactly one agent.", badge: null },
80
+ { id: "one-more-cycle", name: "One More Cycle", category: "session", description: "A session with 10+ orchestrator cycles.", badge: null },
81
+ { id: "deep-dive", name: "Deep Dive", category: "session", description: "A session with 15+ orchestrator cycles.", badge: null },
82
+ { id: "abyss", name: "Into the Abyss", category: "session", description: "A session with 25+ orchestrator cycles.", badge: null },
83
+ { id: "eternal-recurrence", name: "Eternal Recurrence", category: "session", description: "A session with 40+ orchestrator cycles.", badge: null },
84
+ { id: "endurance", name: "Endurance", category: "session", description: "A single session running 4+ hours.", badge: null },
85
+ { id: "ultramarathon", name: "Ultramarathon", category: "session", description: "A single session running 6+ hours.", badge: null },
86
+ { id: "one-shot", name: "One Shot", category: "session", description: "Complete with 5+ agents in exactly 1 orchestrator cycle.", badge: null },
87
+ { id: "quick-draw", name: "Quick Draw", category: "session", description: "First agent spawned within 20s of session start.", badge: null },
88
+ // Time (6)
89
+ { id: "night-owl", name: "Night Owl", category: "time", description: "Complete a session started between 1am and 5am.", badge: ")" },
90
+ { id: "dawn-patrol", name: "Dawn Patrol", category: "time", description: "Session running 3+ hours that spans midnight to 6am.", badge: null },
91
+ { id: "early-bird", name: "Early Bird", category: "time", description: "Start a session before 6am.", badge: null },
92
+ { id: "weekend-warrior", name: "Weekend Warrior", category: "time", description: "Complete a session on a Saturday or Sunday.", badge: null },
93
+ { id: "all-nighter", name: "All-Nighter", category: "time", description: "Single session running 5+ hours.", badge: null },
94
+ { id: "witching-hour", name: "Witching Hour", category: "time", description: "Start a session between 3am and 4am.", badge: null },
95
+ // Behavioral (16)
96
+ { id: "sisyphean", name: "Sisyphean", category: "behavioral", description: "Restart the same task 3+ times.", badge: ";" },
97
+ { id: "stubborn", name: "Stubborn", category: "behavioral", description: "Restart the same task 5+ times and eventually complete it.", badge: null },
98
+ { id: "one-must-imagine", name: "One Must Imagine", category: "behavioral", description: "Restart the same task 10+ times.", badge: null },
99
+ { id: "creature-of-habit", name: "Creature of Habit", category: "behavioral", description: "Visit the same repo 10 times.", badge: null },
100
+ { id: "loyal", name: "Loyal", category: "behavioral", description: "Visit the same repo 30 times.", badge: null },
101
+ { id: "wanderer", name: "Wanderer", category: "behavioral", description: "3+ different repos in a single calendar day.", badge: null },
102
+ { id: "streak", name: "Streak", category: "behavioral", description: "7 consecutive days with at least one session.", badge: null },
103
+ { id: "iron-streak", name: "Iron Streak", category: "behavioral", description: "14 consecutive days with at least one session.", badge: null },
104
+ { id: "hot-streak", name: "Hot Streak", category: "behavioral", description: "15 consecutive clean sessions.", badge: null },
105
+ { id: "momentum", name: "Momentum", category: "behavioral", description: "5 sessions completed within 4 hours.", badge: null },
106
+ { id: "overdrive", name: "Overdrive", category: "behavioral", description: "Complete 6+ sessions in a single calendar day.", badge: null },
107
+ { id: "patient-one", name: "Patient One", category: "behavioral", description: "Idle 30+ minutes between cycles in a session.", badge: null },
108
+ { id: "message-in-a-bottle", name: "Message in a Bottle", category: "behavioral", description: "10+ messages sent to a single session.", badge: null },
109
+ { id: "deep-conversation", name: "Deep Conversation", category: "behavioral", description: "Send 20+ messages to a single session.", badge: null },
110
+ { id: "comeback-kid", name: "Comeback Kid", category: "behavioral", description: "Resume a paused/killed session and complete it.", badge: null },
111
+ { id: "pair-programming", name: "Pair Programming", category: "behavioral", description: "8+ user messages during a single active session.", badge: null }
112
+ ];
113
+
114
+ // src/shared/companion-render.ts
115
+ function getBaseForm(level) {
116
+ if (level <= 2) return "(FACE) {BOULDER}";
117
+ if (level <= 4) return "(FACE)/ {BOULDER}";
118
+ if (level <= 7) return "/(FACE)/ {BOULDER}";
119
+ if (level <= 11) return "\\(FACE)/ {BOULDER}";
120
+ if (level <= 19) return "\u1566(FACE)\u1564 {BOULDER}";
121
+ return "\u265B\u1566(FACE)\u1564 {BOULDER}";
122
+ }
123
+ function getMoodFace(mood) {
124
+ switch (mood) {
125
+ case "happy":
126
+ return "^.^";
127
+ case "grinding":
128
+ return ">.<";
129
+ case "frustrated":
130
+ return ">.<#";
131
+ case "zen":
132
+ return "\u203E.\u203E";
133
+ case "sleepy":
134
+ return "-.-)zzZ";
135
+ case "excited":
136
+ return "*o*";
137
+ case "existential":
138
+ return "\u25C9_\u25C9";
139
+ default:
140
+ throw new Error(`Unknown mood: ${mood}`);
141
+ }
142
+ }
143
+ function getStatCosmetics(stats) {
144
+ const cosmetics = [];
145
+ if (stats.wisdom > 5) cosmetics.push("wisps");
146
+ if (stats.endurance > 36e6) cosmetics.push("trail");
147
+ if (stats.patience > 50) cosmetics.push("zen-prefix");
148
+ return cosmetics;
149
+ }
150
+ function getBoulderForm(agentCount, repoNickname) {
151
+ let boulder;
152
+ if (agentCount === void 0 || agentCount <= 0) {
153
+ boulder = ".";
154
+ } else if (agentCount <= 1) {
155
+ boulder = "o";
156
+ } else if (agentCount <= 4) {
157
+ boulder = "O";
158
+ } else if (agentCount <= 9) {
159
+ boulder = "\u25C9";
160
+ } else if (agentCount <= 20) {
161
+ boulder = "@";
162
+ } else {
163
+ boulder = "@@";
164
+ }
165
+ if (repoNickname !== void 0) {
166
+ boulder = `${boulder} "${repoNickname}"`;
167
+ }
168
+ return boulder;
169
+ }
170
+ function composeLine(body, cosmetics, boulder) {
171
+ let b = boulder;
172
+ let hasSparkle = false;
173
+ let hasZenPrefix = false;
174
+ for (const c of cosmetics) {
175
+ switch (c) {
176
+ case "wisps":
177
+ b = `~${b}~`;
178
+ break;
179
+ case "trail":
180
+ b = `${b} ...`;
181
+ break;
182
+ case "sparkle":
183
+ hasSparkle = true;
184
+ break;
185
+ case "zen-prefix":
186
+ hasZenPrefix = true;
187
+ break;
188
+ }
189
+ }
190
+ let line = body.replace("{BOULDER}", b);
191
+ if (hasZenPrefix) line = `o ${line}`;
192
+ if (hasSparkle) line = `* ${line} *`;
193
+ return line;
194
+ }
195
+ var MOOD_COLORS = {
196
+ happy: { ansi: 32, tmux: "green" },
197
+ grinding: { ansi: 33, tmux: "yellow" },
198
+ frustrated: { ansi: 31, tmux: "red" },
199
+ zen: { ansi: 36, tmux: "cyan" },
200
+ sleepy: { ansi: 90, tmux: "colour245" },
201
+ excited: { ansi: 97, tmux: "white" },
202
+ existential: { ansi: 35, tmux: "magenta" }
203
+ };
204
+ function colorize(text, mood, tmux) {
205
+ const { ansi, tmux: tmuxColor } = MOOD_COLORS[mood];
206
+ if (tmux) {
207
+ return `#[fg=${tmuxColor}]${text}#[fg=default]`;
208
+ }
209
+ return `\x1B[${ansi}m${text}\x1B[0m`;
210
+ }
211
+ function statSummary(stats) {
212
+ const endH = Math.floor(stats.endurance / 36e5);
213
+ return `STR:${stats.strength} END:${endH}h WIS:${stats.wisdom} PAT:${stats.patience}`;
214
+ }
215
+ function renderCompanion(companion, fields, opts) {
216
+ const hasFace = fields.includes("face");
217
+ const hasBoulder = fields.includes("boulder");
218
+ const repoNickname = opts?.repoPath !== void 0 ? companion.repos[opts.repoPath]?.nickname ?? void 0 : void 0;
219
+ const boulder = getBoulderForm(opts?.agentCount, repoNickname);
220
+ const cosmetics = getStatCosmetics(companion.stats);
221
+ let facePart = null;
222
+ let boulderOnlyPart = null;
223
+ if (hasFace) {
224
+ const baseForm = getBaseForm(companion.level);
225
+ const face = getMoodFace(companion.mood);
226
+ const bodyWithFace = baseForm.replace("FACE", face);
227
+ facePart = composeLine(bodyWithFace, cosmetics, boulder);
228
+ } else if (hasBoulder) {
229
+ boulderOnlyPart = boulder;
230
+ }
231
+ let commentary = fields.includes("commentary") ? companion.lastCommentary?.text ?? "" : null;
232
+ const parts = [];
233
+ for (const field of fields) {
234
+ switch (field) {
235
+ case "face":
236
+ if (facePart !== null) parts.push(facePart);
237
+ break;
238
+ case "boulder":
239
+ if (!hasFace && boulderOnlyPart !== null) parts.push(boulderOnlyPart);
240
+ break;
241
+ case "title":
242
+ parts.push(companion.title);
243
+ break;
244
+ case "commentary":
245
+ if (commentary !== null) parts.push(commentary);
246
+ break;
247
+ case "mood":
248
+ parts.push(`[${companion.mood}]`);
249
+ break;
250
+ case "level":
251
+ parts.push(`Lv ${companion.level}`);
252
+ break;
253
+ case "stats":
254
+ parts.push(statSummary(companion.stats));
255
+ break;
256
+ case "achievements":
257
+ parts.push(`${companion.achievements.length} achievements`);
258
+ break;
259
+ }
260
+ }
261
+ if (opts?.maxWidth !== void 0) {
262
+ const maxWidth = opts.maxWidth;
263
+ const joined = parts.join(" ");
264
+ if (joined.length > maxWidth && commentary !== null && commentary.length > 0) {
265
+ const commentaryIdx = parts.indexOf(commentary);
266
+ if (commentaryIdx !== -1) {
267
+ const overhead = joined.length - commentary.length;
268
+ const available = maxWidth - overhead - 2;
269
+ if (available < 0) {
270
+ parts[commentaryIdx] = "";
271
+ } else {
272
+ parts[commentaryIdx] = commentary.slice(0, available);
273
+ }
274
+ commentary = parts[commentaryIdx];
275
+ }
276
+ }
277
+ const result2 = parts.filter((p) => p.length > 0).join(" ");
278
+ const final = result2.length > maxWidth ? result2.slice(0, maxWidth - 1) + "\u2026" : result2;
279
+ return applyColor(final, fields, facePart, companion.mood, opts);
280
+ }
281
+ const result = parts.filter((p) => p.length > 0).join(" ");
282
+ return applyColor(result, fields, facePart, companion.mood, opts);
283
+ }
284
+ function applyColor(result, fields, facePart, mood, opts) {
285
+ const useColor = opts?.color === true || opts?.tmuxFormat === true;
286
+ if (!useColor || facePart === null || !fields.includes("face")) return result;
287
+ const tmux = opts?.tmuxFormat === true;
288
+ const coloredFace = colorize(facePart, mood, tmux);
289
+ return result.replace(facePart, coloredFace);
290
+ }
291
+
292
+ export {
293
+ loadConfig,
294
+ shellQuote,
295
+ ACHIEVEMENTS,
296
+ getMoodFace,
297
+ renderCompanion
298
+ };
299
+ //# sourceMappingURL=chunk-V36NXMHP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shared/config.ts","../src/shared/shell.ts","../src/shared/companion-types.ts","../src/shared/companion-render.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { globalConfigPath, projectConfigPath } from './paths.js';\n\nexport type EffortLevel = 'low' | 'medium' | 'high' | 'max';\n\nexport interface NotificationConfig {\n enabled?: boolean;\n sound?: string;\n}\n\nexport interface RequiredPlugin {\n name: string;\n marketplace: string;\n}\n\nexport interface Config {\n model?: string;\n tmuxSession?: string;\n orchestratorPrompt?: string;\n pollIntervalMs?: number;\n autoUpdate?: boolean;\n orchestratorEffort?: EffortLevel;\n agentEffort?: EffortLevel;\n editor?: string;\n repos?: string[];\n notifications?: NotificationConfig;\n requiredPlugins?: RequiredPlugin[];\n}\n\nconst DEFAULT_CONFIG: Config = {\n pollIntervalMs: 5000,\n orchestratorEffort: 'high',\n agentEffort: 'medium',\n notifications: {\n enabled: true,\n sound: '/System/Library/Sounds/Hero.aiff',\n },\n requiredPlugins: [\n { name: 'devcore', marketplace: 'crouton-kit' },\n ],\n};\n\nfunction readJsonFile(filePath: string): Partial<Config> {\n try {\n const content = readFileSync(filePath, 'utf-8');\n return JSON.parse(content) as Partial<Config>;\n } catch {\n return {};\n }\n}\n\nexport function loadConfig(cwd: string): Config {\n const global = readJsonFile(globalConfigPath());\n const project = readJsonFile(projectConfigPath(cwd));\n return { ...DEFAULT_CONFIG, ...global, ...project };\n}\n","export function shellQuote(s: string): string {\n return `'${s.replace(/'/g, \"'\\\\''\")}'`;\n}\n","export type Mood = 'happy' | 'grinding' | 'frustrated' | 'zen' | 'sleepy' | 'excited' | 'existential';\n\nexport type CompanionField = 'face' | 'boulder' | 'title' | 'commentary' | 'mood' | 'level' | 'stats' | 'achievements';\n\nexport type CommentaryEvent =\n | 'session-start'\n | 'cycle-boundary'\n | 'session-complete'\n | 'level-up'\n | 'achievement'\n | 'agent-crash'\n | 'idle-wake'\n | 'late-night';\n\nexport type TimePersonality = 'chipper' | 'professional' | 'reflective' | 'dry-humor' | 'delirious';\n\nexport type IdleAnimation = 'sleeping' | 'pacing' | 'pondering' | 'flexing' | 'deep-sleep';\n\nexport type AchievementCategory = 'milestone' | 'session' | 'time' | 'behavioral';\n\nexport type AchievementId =\n // Milestone (25)\n | 'first-blood'\n | 'regular'\n | 'centurion'\n | 'veteran'\n | 'thousand-boulder'\n | 'cartographer'\n | 'world-traveler'\n | 'omnipresent'\n | 'swarm-starter'\n | 'hive-mind'\n | 'legion'\n | 'army-of-thousands'\n | 'singularity'\n | 'first-shift'\n | 'workaholic'\n | 'time-lord'\n | 'eternal-grind'\n | 'epoch'\n | 'old-growth'\n | 'seasoned'\n | 'ancient'\n | 'apprentice'\n | 'journeyman'\n | 'master'\n | 'grandmaster'\n // Session (19)\n | 'marathon'\n | 'squad'\n | 'battalion'\n | 'swarm'\n | 'blitz'\n | 'speed-run'\n | 'flash'\n | 'flawless'\n | 'iron-will'\n | 'glass-cannon'\n | 'solo'\n | 'one-more-cycle'\n | 'deep-dive'\n | 'abyss'\n | 'eternal-recurrence'\n | 'endurance'\n | 'ultramarathon'\n | 'one-shot'\n | 'quick-draw'\n // Time (6)\n | 'night-owl'\n | 'dawn-patrol'\n | 'early-bird'\n | 'weekend-warrior'\n | 'all-nighter'\n | 'witching-hour'\n // Behavioral (16)\n | 'sisyphean'\n | 'stubborn'\n | 'one-must-imagine'\n | 'creature-of-habit'\n | 'loyal'\n | 'wanderer'\n | 'streak'\n | 'iron-streak'\n | 'hot-streak'\n | 'momentum'\n | 'overdrive'\n | 'patient-one'\n | 'message-in-a-bottle'\n | 'deep-conversation'\n | 'comeback-kid'\n | 'pair-programming';\n\nexport interface AchievementDef {\n id: AchievementId;\n name: string;\n category: AchievementCategory;\n description: string;\n badge: string | null;\n}\n\nexport interface CompanionStats {\n strength: number; // lifetime completed sessions\n endurance: number; // lifetime active ms\n wisdom: number; // efficient orchestration count\n patience: number; // persistence score (cycles + lifecycle bonuses)\n}\n\nexport interface UnlockedAchievement {\n id: AchievementId;\n unlockedAt: string; // ISO timestamp\n}\n\nexport interface RepoMemory {\n visits: number;\n completions: number;\n crashes: number;\n totalActiveMs: number;\n moodAvg: number; // running average (0-1 scale)\n nickname: string | null;\n firstSeen: string; // ISO timestamp\n lastSeen: string; // ISO timestamp\n}\n\nexport interface LastCommentary {\n text: string;\n event: CommentaryEvent;\n timestamp: string; // ISO timestamp\n}\n\nexport interface CompanionState {\n version: 1;\n name: string | null;\n createdAt: string; // ISO timestamp\n stats: CompanionStats;\n xp: number;\n level: number;\n title: string;\n mood: Mood;\n moodUpdatedAt: string; // ISO timestamp\n achievements: UnlockedAchievement[];\n repos: Record<string, RepoMemory>; // keyed by absolute cwd path\n lastCommentary: LastCommentary | null;\n // Lifetime counters (redundant with derivable stats but kept for fast achievement checks)\n sessionsCompleted: number;\n sessionsCrashed: number;\n totalActiveMs: number;\n lifetimeAgentsSpawned: number;\n // Achievement tracking counters\n consecutiveCleanSessions: number;\n consecutiveEfficientSessions: number;\n consecutiveDaysActive: number;\n lastActiveDate: string | null; // ISO date string YYYY-MM-DD\n taskHistory: Record<string, number>; // normalized task hash → attempt count\n dailyRepos: Record<string, string[]>; // ISO date → array of repo paths\n recentCompletions: string[]; // last 3 ISO timestamps for momentum check\n // Debug: last mood signals and scores (written by pane-monitor, read by TUI debug overlay)\n debugMood?: {\n signals: MoodSignals;\n scores: Record<Mood, number>;\n winner: Mood;\n };\n}\n\nexport interface IdleState {\n animation: IdleAnimation;\n frame: number; // current frame index in the animation cycle\n idleSince: string; // ISO timestamp of last session event\n}\n\nexport interface CompanionRenderOpts {\n maxWidth?: number;\n color?: boolean;\n tmuxFormat?: boolean;\n repoPath?: string;\n agentCount?: number;\n}\n\nexport interface MoodSignals {\n recentCrashes: number; // crashes in last 30 minutes\n idleDurationMs: number; // ms since last session activity\n sessionLengthMs: number; // current session running time\n cleanStreak: number; // consecutive clean completions\n justCompleted: boolean; // session just completed successfully\n justCrashed: boolean; // agent just crashed\n justLeveledUp: boolean; // level up just happened\n hourOfDay: number; // 0-23\n activeAgentCount?: number; // agents currently with status === 'running'\n // NEW fields for richer mood scoring\n cycleCount?: number; // current session orchestrator cycle count\n sessionsCompletedToday?: number; // sessions completed today\n}\n\nexport const ACHIEVEMENTS: AchievementDef[] = [\n // Milestone (25)\n { id: 'first-blood', name: 'First Blood', category: 'milestone', description: 'Complete your first session.', badge: null },\n { id: 'regular', name: 'Regular', category: 'milestone', description: 'Complete 10 sessions.', badge: null },\n { id: 'centurion', name: 'Centurion', category: 'milestone', description: 'Complete 100 sessions.', badge: null },\n { id: 'veteran', name: 'Veteran', category: 'milestone', description: 'Complete 500 sessions.', badge: null },\n { id: 'thousand-boulder', name: 'Thousand Boulder', category: 'milestone', description: 'Complete 1,000 sessions.', badge: null },\n { id: 'cartographer', name: 'Cartographer', category: 'milestone', description: 'Work in 5 different repos.', badge: '+' },\n { id: 'world-traveler', name: 'World Traveler', category: 'milestone', description: 'Work in 15 different repos.', badge: null },\n { id: 'omnipresent', name: 'Omnipresent', category: 'milestone', description: 'Work in 30 different repos.', badge: null },\n { id: 'swarm-starter', name: 'Swarm Starter', category: 'milestone', description: 'Spawn 50 agents over a lifetime.', badge: null },\n { id: 'hive-mind', name: 'Hive Mind', category: 'milestone', description: 'Spawn 500 agents over a lifetime.', badge: null },\n { id: 'legion', name: 'Legion', category: 'milestone', description: 'Spawn 2,000 agents over a lifetime.', badge: null },\n { id: 'army-of-thousands', name: 'Army of Thousands', category: 'milestone', description: 'Spawn 5,000 agents over a lifetime.', badge: null },\n { id: 'singularity', name: 'Singularity', category: 'milestone', description: 'Spawn 10,000 agents over a lifetime.', badge: null },\n { id: 'first-shift', name: 'First Shift', category: 'milestone', description: '10 hours of total agent active time.', badge: null },\n { id: 'workaholic', name: 'Workaholic', category: 'milestone', description: '100 hours of total agent active time.', badge: null },\n { id: 'time-lord', name: 'Time Lord', category: 'milestone', description: '500 hours of total agent active time.', badge: null },\n { id: 'eternal-grind', name: 'Eternal Grind', category: 'milestone', description: '2,000 hours of total agent active time.', badge: null },\n { id: 'epoch', name: 'Epoch', category: 'milestone', description: '5,000 hours of total agent active time.', badge: null },\n { id: 'old-growth', name: 'Old Growth', category: 'milestone', description: 'Companion is 14 days old.', badge: null },\n { id: 'seasoned', name: 'Seasoned', category: 'milestone', description: 'Companion is 90 days old.', badge: null },\n { id: 'ancient', name: 'Ancient', category: 'milestone', description: 'Companion is 365 days old.', badge: null },\n { id: 'apprentice', name: 'Apprentice', category: 'milestone', description: 'Reach level 5.', badge: null },\n { id: 'journeyman', name: 'Journeyman', category: 'milestone', description: 'Reach level 15.', badge: null },\n { id: 'master', name: 'Master', category: 'milestone', description: 'Reach level 30.', badge: null },\n { id: 'grandmaster', name: 'Grandmaster', category: 'milestone', description: 'Reach level 50.', badge: null },\n // Session (19)\n { id: 'marathon', name: 'Marathon', category: 'session', description: 'Complete a session with 15+ agents.', badge: '~^~' },\n { id: 'squad', name: 'Squad Up', category: 'session', description: 'Complete a session with 10+ agents.', badge: null },\n { id: 'battalion', name: 'Battalion', category: 'session', description: 'Complete a session with 25+ agents.', badge: null },\n { id: 'swarm', name: 'The Swarm', category: 'session', description: 'Complete a session with 50+ agents.', badge: null },\n { id: 'blitz', name: 'Blitz', category: 'session', description: 'Complete a session in under 5 minutes.', badge: null },\n { id: 'speed-run', name: 'Speed Run', category: 'session', description: 'Complete a session in under 15 minutes.', badge: null },\n { id: 'flash', name: 'Flash', category: 'session', description: 'Complete a session in under 2 minutes.', badge: null },\n { id: 'flawless', name: 'Flawless', category: 'session', description: 'Complete a session with 10+ agents and zero crashes.', badge: '*' },\n { id: 'iron-will', name: 'Iron Will', category: 'session', description: '10 consecutive sessions completing in 3 or fewer cycles.', badge: '[]' },\n { id: 'glass-cannon', name: 'Glass Cannon', category: 'session', description: '5+ agents, all crashed, but session completed anyway.', badge: null },\n { id: 'solo', name: 'Solo', category: 'session', description: 'Complete a session with exactly one agent.', badge: null },\n { id: 'one-more-cycle', name: 'One More Cycle', category: 'session', description: 'A session with 10+ orchestrator cycles.', badge: null },\n { id: 'deep-dive', name: 'Deep Dive', category: 'session', description: 'A session with 15+ orchestrator cycles.', badge: null },\n { id: 'abyss', name: 'Into the Abyss', category: 'session', description: 'A session with 25+ orchestrator cycles.', badge: null },\n { id: 'eternal-recurrence', name: 'Eternal Recurrence', category: 'session', description: 'A session with 40+ orchestrator cycles.', badge: null },\n { id: 'endurance', name: 'Endurance', category: 'session', description: 'A single session running 4+ hours.', badge: null },\n { id: 'ultramarathon', name: 'Ultramarathon', category: 'session', description: 'A single session running 6+ hours.', badge: null },\n { id: 'one-shot', name: 'One Shot', category: 'session', description: 'Complete with 5+ agents in exactly 1 orchestrator cycle.', badge: null },\n { id: 'quick-draw', name: 'Quick Draw', category: 'session', description: 'First agent spawned within 20s of session start.', badge: null },\n // Time (6)\n { id: 'night-owl', name: 'Night Owl', category: 'time', description: 'Complete a session started between 1am and 5am.', badge: ')' },\n { id: 'dawn-patrol', name: 'Dawn Patrol', category: 'time', description: 'Session running 3+ hours that spans midnight to 6am.', badge: null },\n { id: 'early-bird', name: 'Early Bird', category: 'time', description: 'Start a session before 6am.', badge: null },\n { id: 'weekend-warrior', name: 'Weekend Warrior', category: 'time', description: 'Complete a session on a Saturday or Sunday.', badge: null },\n { id: 'all-nighter', name: 'All-Nighter', category: 'time', description: 'Single session running 5+ hours.', badge: null },\n { id: 'witching-hour', name: 'Witching Hour', category: 'time', description: 'Start a session between 3am and 4am.', badge: null },\n // Behavioral (16)\n { id: 'sisyphean', name: 'Sisyphean', category: 'behavioral', description: 'Restart the same task 3+ times.', badge: ';' },\n { id: 'stubborn', name: 'Stubborn', category: 'behavioral', description: 'Restart the same task 5+ times and eventually complete it.', badge: null },\n { id: 'one-must-imagine', name: 'One Must Imagine', category: 'behavioral', description: 'Restart the same task 10+ times.', badge: null },\n { id: 'creature-of-habit', name: 'Creature of Habit', category: 'behavioral', description: 'Visit the same repo 10 times.', badge: null },\n { id: 'loyal', name: 'Loyal', category: 'behavioral', description: 'Visit the same repo 30 times.', badge: null },\n { id: 'wanderer', name: 'Wanderer', category: 'behavioral', description: '3+ different repos in a single calendar day.', badge: null },\n { id: 'streak', name: 'Streak', category: 'behavioral', description: '7 consecutive days with at least one session.', badge: null },\n { id: 'iron-streak', name: 'Iron Streak', category: 'behavioral', description: '14 consecutive days with at least one session.', badge: null },\n { id: 'hot-streak', name: 'Hot Streak', category: 'behavioral', description: '15 consecutive clean sessions.', badge: null },\n { id: 'momentum', name: 'Momentum', category: 'behavioral', description: '5 sessions completed within 4 hours.', badge: null },\n { id: 'overdrive', name: 'Overdrive', category: 'behavioral', description: 'Complete 6+ sessions in a single calendar day.', badge: null },\n { id: 'patient-one', name: 'Patient One', category: 'behavioral', description: 'Idle 30+ minutes between cycles in a session.', badge: null },\n { id: 'message-in-a-bottle', name: 'Message in a Bottle', category: 'behavioral', description: '10+ messages sent to a single session.', badge: null },\n { id: 'deep-conversation', name: 'Deep Conversation', category: 'behavioral', description: 'Send 20+ messages to a single session.', badge: null },\n { id: 'comeback-kid', name: 'Comeback Kid', category: 'behavioral', description: 'Resume a paused/killed session and complete it.', badge: null },\n { id: 'pair-programming', name: 'Pair Programming', category: 'behavioral', description: '8+ user messages during a single active session.', badge: null },\n];\n","import type {\n CompanionState,\n CompanionField,\n CompanionRenderOpts,\n CompanionStats,\n Mood,\n} from './companion-types.js';\n\n// --- Base form ---\n//\n// Returns a template with two placeholders:\n// FACE — replaced by getMoodFace() in renderCompanion\n// {BOULDER} — replaced by composeLine() with the agent-count-driven boulder\n//\n// No literal boulder characters are embedded here. The previous design embedded\n// them (`.`, `o`, `O`, `OO`, `@`) which caused splitBodyAndBoulder to either\n// discard multi-char boulders (OO) or corrupt output when the dynamic boulder\n// didn't match the embedded one.\n\nexport function getBaseForm(level: number): string {\n if (level <= 2) return '(FACE) {BOULDER}';\n if (level <= 4) return '(FACE)/ {BOULDER}';\n if (level <= 7) return '/(FACE)/ {BOULDER}';\n if (level <= 11) return '\\\\(FACE)/ {BOULDER}';\n if (level <= 19) return 'ᕦ(FACE)ᕤ {BOULDER}';\n return '♛ᕦ(FACE)ᕤ {BOULDER}';\n}\n\n// --- Mood face ---\n\nexport function getMoodFace(mood: Mood): string {\n switch (mood) {\n case 'happy': return '^.^';\n case 'grinding': return '>.<';\n case 'frustrated': return '>.<#';\n case 'zen': return '‾.‾';\n case 'sleepy': return '-.-)zzZ';\n case 'excited': return '*o*';\n case 'existential': return '◉_◉';\n default: throw new Error(`Unknown mood: ${mood as string}`);\n }\n}\n\n// --- Stat cosmetics ---\n\nexport function getStatCosmetics(stats: CompanionStats): string[] {\n const cosmetics: string[] = [];\n if (stats.wisdom > 5) cosmetics.push('wisps');\n if (stats.endurance > 36_000_000) cosmetics.push('trail');\n if (stats.patience > 50) cosmetics.push('zen-prefix');\n return cosmetics;\n}\n\n// --- Boulder form ---\n\nexport function getBoulderForm(agentCount?: number, repoNickname?: string): string {\n let boulder: string;\n if (agentCount === undefined || agentCount <= 0) {\n boulder = '.';\n } else if (agentCount <= 1) {\n boulder = 'o';\n } else if (agentCount <= 4) {\n boulder = 'O';\n } else if (agentCount <= 9) {\n boulder = '◉';\n } else if (agentCount <= 20) {\n boulder = '@';\n } else {\n boulder = '@@';\n }\n if (repoNickname !== undefined) {\n boulder = `${boulder} \"${repoNickname}\"`;\n }\n return boulder;\n}\n\n// --- composeLine ---\n//\n// body has already had FACE replaced with the mood face, and still contains\n// the {BOULDER} placeholder from getBaseForm.\n// composeLine applies cosmetics to `boulder`, then substitutes {BOULDER}.\n\nexport function composeLine(\n body: string,\n cosmetics: string[],\n boulder: string,\n): string {\n let b = boulder;\n\n let hasSparkle = false;\n let hasZenPrefix = false;\n\n for (const c of cosmetics) {\n switch (c) {\n case 'wisps':\n b = `~${b}~`;\n break;\n case 'trail':\n b = `${b} ...`;\n break;\n case 'sparkle':\n hasSparkle = true;\n break;\n case 'zen-prefix':\n hasZenPrefix = true;\n break;\n }\n }\n\n let line = body.replace('{BOULDER}', b);\n\n if (hasZenPrefix) line = `o ${line}`;\n if (hasSparkle) line = `* ${line} *`;\n\n return line;\n}\n\n// --- Color helpers ---\n\ntype AnsiCode = number;\ntype TmuxColor = string;\n\ninterface MoodColor {\n ansi: AnsiCode;\n tmux: TmuxColor;\n}\n\nconst MOOD_COLORS: Record<Mood, MoodColor> = {\n happy: { ansi: 32, tmux: 'green' },\n grinding: { ansi: 33, tmux: 'yellow' },\n frustrated: { ansi: 31, tmux: 'red' },\n zen: { ansi: 36, tmux: 'cyan' },\n sleepy: { ansi: 90, tmux: 'colour245' },\n excited: { ansi: 97, tmux: 'white' },\n existential: { ansi: 35, tmux: 'magenta' },\n};\n\nfunction colorize(text: string, mood: Mood, tmux: boolean): string {\n const { ansi, tmux: tmuxColor } = MOOD_COLORS[mood];\n if (tmux) {\n return `#[fg=${tmuxColor}]${text}#[fg=default]`;\n }\n return `\\x1b[${ansi}m${text}\\x1b[0m`;\n}\n\n// --- Stat summary string ---\n\nfunction statSummary(stats: CompanionStats): string {\n const endH = Math.floor(stats.endurance / 3_600_000);\n return `STR:${stats.strength} END:${endH}h WIS:${stats.wisdom} PAT:${stats.patience}`;\n}\n\n// --- Main renderer ---\n\nexport function renderCompanion(\n companion: CompanionState,\n fields: CompanionField[],\n opts?: CompanionRenderOpts,\n): string {\n const hasFace = fields.includes('face');\n const hasBoulder = fields.includes('boulder');\n\n const repoNickname = opts?.repoPath !== undefined\n ? companion.repos[opts.repoPath]?.nickname ?? undefined\n : undefined;\n\n const boulder = getBoulderForm(opts?.agentCount, repoNickname);\n const cosmetics = getStatCosmetics(companion.stats);\n\n let facePart: string | null = null;\n let boulderOnlyPart: string | null = null;\n\n if (hasFace) {\n const baseForm = getBaseForm(companion.level);\n const face = getMoodFace(companion.mood);\n const bodyWithFace = baseForm.replace('FACE', face);\n facePart = composeLine(bodyWithFace, cosmetics, boulder);\n } else if (hasBoulder) {\n // Boulder standalone (unusual)\n boulderOnlyPart = boulder;\n }\n\n let commentary = fields.includes('commentary')\n ? (companion.lastCommentary?.text ?? '')\n : null;\n\n const parts: string[] = [];\n\n for (const field of fields) {\n switch (field) {\n case 'face':\n if (facePart !== null) parts.push(facePart);\n break;\n case 'boulder':\n if (!hasFace && boulderOnlyPart !== null) parts.push(boulderOnlyPart);\n // If face included, boulder is already embedded — skip\n break;\n case 'title':\n parts.push(companion.title);\n break;\n case 'commentary':\n if (commentary !== null) parts.push(commentary);\n break;\n case 'mood':\n parts.push(`[${companion.mood}]`);\n break;\n case 'level':\n parts.push(`Lv ${companion.level}`);\n break;\n case 'stats':\n parts.push(statSummary(companion.stats));\n break;\n case 'achievements':\n parts.push(`${companion.achievements.length} achievements`);\n break;\n }\n }\n\n // Apply maxWidth: truncate commentary first, then right-truncate\n if (opts?.maxWidth !== undefined) {\n const maxWidth = opts.maxWidth;\n const joined = parts.join(' ');\n if (joined.length > maxWidth && commentary !== null && commentary.length > 0) {\n // Shorten commentary progressively\n const commentaryIdx = parts.indexOf(commentary);\n if (commentaryIdx !== -1) {\n const overhead = joined.length - commentary.length;\n const available = maxWidth - overhead - 2; // account for double-space\n if (available < 0) {\n parts[commentaryIdx] = '';\n } else {\n parts[commentaryIdx] = commentary.slice(0, available);\n }\n commentary = parts[commentaryIdx];\n }\n }\n const result = parts.filter(p => p.length > 0).join(' ');\n const final = result.length > maxWidth\n ? result.slice(0, maxWidth - 1) + '…'\n : result;\n\n return applyColor(final, fields, facePart, companion.mood, opts);\n }\n\n const result = parts.filter(p => p.length > 0).join(' ');\n return applyColor(result, fields, facePart, companion.mood, opts);\n}\n\nfunction applyColor(\n result: string,\n fields: CompanionField[],\n facePart: string | null,\n mood: Mood,\n opts?: CompanionRenderOpts,\n): string {\n const useColor = opts?.color === true || opts?.tmuxFormat === true;\n if (!useColor || facePart === null || !fields.includes('face')) return result;\n\n const tmux = opts?.tmuxFormat === true;\n const coloredFace = colorize(facePart, mood, tmux);\n return result.replace(facePart, coloredFace);\n}\n"],"mappings":"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","names":["result"]}