@rucher/carnet 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resume-Daotqfdq.mjs","names":[],"sources":["../src/context.ts","../src/errors.ts","../src/events.ts","../src/status/model.ts","../src/status/note.ts","../src/agents.ts","../src/db/schema.ts","../src/db/open.ts","../src/hosts/claude-code.ts","../src/hosts/host.ts","../src/group/registry.ts","../src/group/locate.ts","../src/sessions.ts","../src/merge.ts","../src/purge.ts","../src/status/render.ts","../src/table.ts","../src/status/view.ts","../src/subagents.ts","../src/carnet.ts","../src/resume.ts"],"sourcesContent":["import type { DatabaseSync } from 'node:sqlite';\n\nexport interface Ctx {\n db: DatabaseSync;\n now: () => Date;\n}\n\nexport function nowIso(ctx: Ctx): string {\n return ctx.now().toISOString();\n}\n","export type CarnetErrorCode =\n | 'NO_GROUP'\n | 'GROUP_EXISTS'\n | 'REPO_NOT_FOUND'\n | 'UNKNOWN_HOST'\n | 'NO_SESSION'\n | 'SESSION_NOT_FOUND'\n | 'SESSION_AMBIGUOUS'\n | 'SESSION_NAME_TAKEN'\n | 'SESSION_MERGE_INVALID'\n | 'AGENT_EXISTS'\n | 'AGENT_NOT_FOUND'\n | 'INVALID_STATUS'\n | 'INVALID_EVENT_TYPE'\n | 'TABLE_STATUS_WITH_MAIN_WORKING';\n\nexport class CarnetError extends Error {\n constructor(\n readonly code: CarnetErrorCode,\n message: string,\n ) {\n super(message);\n this.name = 'CarnetError';\n }\n}\n","import { type Ctx, nowIso } from './context.ts';\nimport { CarnetError } from './errors.ts';\n\nexport const EVENT_TYPES = [\n 'started',\n 'finished',\n 'answered',\n 'applied',\n 'tested',\n 'committed',\n 'dropped',\n] as const;\n\nexport type EventType = (typeof EVENT_TYPES)[number];\n\nexport interface CarnetEvent {\n id: number;\n session: string;\n at: string;\n type: EventType;\n agent: string | null;\n commit: string | null;\n link: string | null;\n note: string | null;\n}\n\nexport interface LogEventInput {\n type: EventType;\n agent?: string | undefined;\n commit?: string | undefined;\n link?: string | undefined;\n note?: string | undefined;\n}\n\nexport interface ReadEventsOptions {\n session: string;\n limit?: number | undefined;\n}\n\ninterface EventRow {\n id: number;\n session: string;\n at: string;\n type: EventType;\n agent: string | null;\n commit_sha: string | null;\n link: string | null;\n note: string | null;\n}\n\nexport function isEventType(value: unknown): value is EventType {\n return typeof value === 'string' && (EVENT_TYPES as readonly string[]).includes(value);\n}\n\nfunction toEvent(row: EventRow): CarnetEvent {\n return {\n id: row.id,\n session: row.session,\n at: row.at,\n type: row.type,\n agent: row.agent,\n commit: row.commit_sha,\n link: row.link,\n note: row.note,\n };\n}\n\nexport function logEvent(ctx: Ctx, session: string, input: LogEventInput): CarnetEvent {\n if (!isEventType(input.type)) {\n throw new CarnetError('INVALID_EVENT_TYPE', `Unknown event type \"${String(input.type)}\"`);\n }\n const result = ctx.db\n .prepare(\n 'INSERT INTO events (session, at, type, agent, commit_sha, link, note) VALUES (?, ?, ?, ?, ?, ?, ?)',\n )\n .run(\n session,\n nowIso(ctx),\n input.type,\n input.agent ?? null,\n input.commit ?? null,\n input.link ?? null,\n input.note ?? null,\n );\n const row = ctx.db\n .prepare('SELECT * FROM events WHERE id = ?')\n .get(result.lastInsertRowid) as unknown as EventRow;\n return toEvent(row);\n}\n\nexport function readEvents(ctx: Ctx, { session, limit = 20 }: ReadEventsOptions): CarnetEvent[] {\n const rows = ctx.db\n .prepare(\n `SELECT * FROM (SELECT * FROM events WHERE session = ? ORDER BY id DESC LIMIT ?) ORDER BY id ASC`,\n )\n .all(session, limit) as unknown as unknown as EventRow[];\n return rows.map(toEvent);\n}\n","export const AGENT_STATUSES = [\n 'running',\n 'discussing',\n 'answer',\n 'review',\n 'apply',\n 'test',\n 'commit',\n 'branch',\n 'closed',\n] as const;\n\nexport type AgentStatus = (typeof AGENT_STATUSES)[number];\n\nexport const USER_ACTION_STATUSES = [\n 'discussing',\n 'answer',\n 'review',\n 'apply',\n 'test',\n 'commit',\n 'branch',\n] as const;\n\nexport type UserActionStatus = (typeof USER_ACTION_STATUSES)[number];\n\nexport const EMOJI: Record<Exclude<AgentStatus, 'closed'>, string> = {\n running: '⏳',\n discussing: '💬',\n answer: '❓',\n review: '👀',\n apply: '📥',\n test: '🧪',\n commit: '✅',\n branch: '🌿',\n};\n\nexport const MAIN_WORKING_EMOJI = '⚙️';\n\nexport const LEGEND =\n '⚙️ main working · 💬 discussing · ❓ answer · 👀 review · 📥 apply · 🧪 test · ✅ commit · 🌿 branch · ⏳ running';\n\nexport function isAgentStatus(value: unknown): value is AgentStatus {\n return typeof value === 'string' && (AGENT_STATUSES as readonly string[]).includes(value);\n}\n\nexport function isUserActionStatus(value: unknown): value is UserActionStatus {\n return typeof value === 'string' && (USER_ACTION_STATUSES as readonly string[]).includes(value);\n}\n\nexport const OPEN_AGENT_STATUSES = ['running', ...USER_ACTION_STATUSES] as const;\n","export const NOTE_MAX = 60;\n\nconst PATH_PREFIX = /^(\\/|\\.\\.?\\/|~\\/)/;\nconst FILE_AFTER_SLASH = /\\/[^/\\s]+\\.[A-Za-z0-9]+$/;\n\nfunction looksLikePath(token: string): boolean {\n if (PATH_PREFIX.test(token)) return true;\n const slashes = token.split('/').length - 1;\n return slashes >= 2 || (slashes === 1 && FILE_AFTER_SLASH.test(token));\n}\n\nexport function sanitizeNote(text: string): string {\n const cleaned = text\n .replaceAll('`', '')\n .split(/\\s+/)\n .filter((token) => token && !looksLikePath(token))\n .join(' ')\n .trim();\n if (cleaned.length <= NOTE_MAX) return cleaned;\n return `${cleaned.slice(0, NOTE_MAX - 1).trimEnd()}…`;\n}\n","import { type Ctx, nowIso } from './context.ts';\nimport { CarnetError } from './errors.ts';\nimport { logEvent } from './events.ts';\nimport { type AgentStatus, isAgentStatus } from './status/model.ts';\nimport { sanitizeNote } from './status/note.ts';\n\nexport interface Agent {\n session: string;\n name: string;\n task: string | null;\n status: AgentStatus;\n note: string | null;\n reportPath: string | null;\n baseCommit: string | null;\n worktree: string | null;\n queueOrder: number | null;\n hostAgentId: string | null;\n pendingStatus: AgentStatus | null;\n doneAt: string | null;\n startedAt: string;\n updatedAt: string;\n}\n\nexport interface StartAgentInput {\n name: string;\n task: string;\n baseCommit?: string | undefined;\n worktree?: string | undefined;\n}\n\nexport interface UpdateAgentInput {\n status: AgentStatus;\n note?: string | undefined;\n reportPath?: string | undefined;\n}\n\nexport interface FinishAgentInput {\n reportPath: string;\n hasQuestions: boolean;\n}\n\nexport interface ListAgentsOptions {\n includeClosed?: boolean;\n}\n\nexport interface AgentRow {\n session: string;\n name: string;\n task: string | null;\n status: AgentStatus;\n note: string | null;\n report_path: string | null;\n base_commit: string | null;\n worktree: string | null;\n queue_order: number | null;\n host_agent_id: string | null;\n pending_status: AgentStatus | null;\n done_at: string | null;\n started_at: string;\n updated_at: string;\n}\n\nexport function toAgent(row: AgentRow): Agent {\n return {\n session: row.session,\n name: row.name,\n task: row.task,\n status: row.status,\n note: row.note,\n reportPath: row.report_path,\n baseCommit: row.base_commit,\n worktree: row.worktree,\n queueOrder: row.queue_order,\n hostAgentId: row.host_agent_id,\n pendingStatus: row.pending_status,\n doneAt: row.done_at,\n startedAt: row.started_at,\n updatedAt: row.updated_at,\n };\n}\n\nexport const REPORTING_NOTE = 'reporting';\nexport const PUBLISH_FALLBACK_MILLISECONDS = 120_000;\n\nexport function publishPending(ctx: Ctx, session: string, name: string): void {\n const row = ctx.db\n .prepare('SELECT pending_status, report_path FROM agents WHERE session = ? AND name = ?')\n .get(session, name) as Pick<AgentRow, 'pending_status' | 'report_path'> | undefined;\n if (!row?.pending_status) return;\n ctx.db\n .prepare(\n 'UPDATE agents SET status = pending_status, pending_status = NULL, note = NULL, updated_at = ? WHERE session = ? AND name = ?',\n )\n .run(nowIso(ctx), session, name);\n logEvent(ctx, session, { type: 'finished', agent: name, link: row.report_path ?? undefined });\n}\n\n// Covers a paired fork whose SubagentStop never arrives (crash, hook timeout).\nfunction publishOverdue(ctx: Ctx, session: string): void {\n const before = new Date(ctx.now().getTime() - PUBLISH_FALLBACK_MILLISECONDS).toISOString();\n const rows = ctx.db\n .prepare(\n 'SELECT name FROM agents WHERE session = ? AND pending_status IS NOT NULL AND done_at <= ?',\n )\n .all(session, before) as unknown as Pick<AgentRow, 'name'>[];\n for (const row of rows) publishPending(ctx, session, row.name);\n}\n\nexport function getAgent(ctx: Ctx, session: string, name: string): Agent | undefined {\n publishOverdue(ctx, session);\n const row = ctx.db\n .prepare('SELECT * FROM agents WHERE session = ? AND name = ?')\n .get(session, name) as AgentRow | undefined;\n return row && toAgent(row);\n}\n\nfunction requireAgent(ctx: Ctx, session: string, name: string): Agent {\n const agent = getAgent(ctx, session, name);\n if (!agent) throw new CarnetError('AGENT_NOT_FOUND', `No agent \"${name}\" in this session`);\n return agent;\n}\n\nexport function startAgent(ctx: Ctx, session: string, input: StartAgentInput): Agent {\n const name = sanitizeNote(input.name);\n const existing = getAgent(ctx, session, name);\n if (existing && existing.status !== 'closed') {\n throw new CarnetError('AGENT_EXISTS', `Agent \"${name}\" is already open in this session`);\n }\n if (existing) {\n ctx.db.prepare('DELETE FROM agents WHERE session = ? AND name = ?').run(session, name);\n }\n const at = nowIso(ctx);\n ctx.db\n .prepare(\n `INSERT INTO agents (session, name, task, status, base_commit, worktree, started_at, updated_at)\n VALUES (?, ?, ?, 'running', ?, ?, ?, ?)`,\n )\n .run(session, name, input.task, input.baseCommit ?? null, input.worktree ?? null, at, at);\n logEvent(ctx, session, { type: 'started', agent: name, note: input.task });\n return requireAgent(ctx, session, name);\n}\n\nexport function updateAgent(\n ctx: Ctx,\n session: string,\n name: string,\n input: UpdateAgentInput,\n): Agent {\n const current = requireAgent(ctx, session, name);\n if (!isAgentStatus(input.status) || input.status === 'closed') {\n throw new CarnetError(\n 'INVALID_STATUS',\n `Invalid agent status \"${input.status}\"; use agent_drop to close`,\n );\n }\n const note = input.note === undefined ? null : sanitizeNote(input.note);\n if (current.pendingStatus) publishPending(ctx, session, name);\n ctx.db\n .prepare(\n \"UPDATE agents SET note = COALESCE(?, CASE WHEN status = 'running' AND ? <> 'running' THEN NULL ELSE note END), status = ?, report_path = COALESCE(?, report_path), updated_at = ? WHERE session = ? AND name = ?\",\n )\n .run(note, input.status, input.status, input.reportPath ?? null, nowIso(ctx), session, name);\n return requireAgent(ctx, session, name);\n}\n\nexport function finishAgent(\n ctx: Ctx,\n session: string,\n name: string,\n input: FinishAgentInput,\n): Agent {\n const agent = requireAgent(ctx, session, name);\n const status: AgentStatus = input.hasQuestions ? 'answer' : 'review';\n const at = nowIso(ctx);\n // A paired fork keeps writing its final message after agent_done; SubagentStop marks the real end.\n ctx.db\n .prepare(\n 'UPDATE agents SET pending_status = ?, note = ?, report_path = ?, done_at = ?, updated_at = ? WHERE session = ? AND name = ?',\n )\n .run(status, REPORTING_NOTE, input.reportPath, at, at, session, name);\n if (!agent.hostAgentId) publishPending(ctx, session, name);\n return requireAgent(ctx, session, name);\n}\n\nexport function dropAgent(ctx: Ctx, session: string, name: string, reason?: string): Agent {\n requireAgent(ctx, session, name);\n ctx.db\n .prepare(\n \"UPDATE agents SET status = 'closed', queue_order = NULL, updated_at = ? WHERE session = ? AND name = ?\",\n )\n .run(nowIso(ctx), session, name);\n logEvent(ctx, session, { type: 'dropped', agent: name, note: reason });\n return requireAgent(ctx, session, name);\n}\n\nexport function listAgents(ctx: Ctx, session: string, options: ListAgentsOptions = {}): Agent[] {\n publishOverdue(ctx, session);\n const where = options.includeClosed ? '' : \"AND status != 'closed'\";\n const rows = ctx.db\n .prepare(`SELECT * FROM agents WHERE session = ? ${where} ORDER BY started_at ASC, name ASC`)\n .all(session) as unknown as unknown as AgentRow[];\n return rows.map(toAgent);\n}\n\nexport function reorderQueue(ctx: Ctx, session: string, names: string[]): Agent[] {\n const at = nowIso(ctx);\n ctx.db.prepare('UPDATE agents SET queue_order = NULL WHERE session = ?').run(session);\n const update = ctx.db.prepare(\n 'UPDATE agents SET queue_order = ?, updated_at = ? WHERE session = ? AND name = ?',\n );\n names.forEach((name, index) => {\n update.run(index + 1, at, session, sanitizeNote(name));\n });\n return listAgents(ctx, session);\n}\n","import type { DatabaseSync } from 'node:sqlite';\n\nexport const migrations: readonly string[] = [\n `\nCREATE TABLE sessions (\n id TEXT PRIMARY KEY,\n name TEXT UNIQUE,\n purpose TEXT,\n status TEXT NOT NULL DEFAULT 'temporary'\n CHECK (status IN ('temporary', 'active', 'dropped')),\n cwd TEXT,\n repo TEXT,\n claude_pid INTEGER,\n created_at TEXT NOT NULL,\n last_active_at TEXT NOT NULL\n);\n\nCREATE TABLE agents (\n session TEXT NOT NULL REFERENCES sessions(id),\n name TEXT NOT NULL,\n task TEXT,\n status TEXT NOT NULL\n CHECK (status IN ('running', 'discussing', 'answer', 'review', 'apply', 'test', 'commit', 'branch', 'closed')),\n note TEXT,\n report_path TEXT,\n base_commit TEXT,\n worktree TEXT,\n queue_order INTEGER,\n started_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n PRIMARY KEY (session, name)\n);\n\nCREATE TABLE table_items (\n session TEXT PRIMARY KEY REFERENCES sessions(id),\n text TEXT NOT NULL,\n status TEXT,\n main_working INTEGER NOT NULL DEFAULT 0,\n updated_at TEXT NOT NULL\n);\n\nCREATE TABLE events (\n id INTEGER PRIMARY KEY,\n session TEXT NOT NULL REFERENCES sessions(id),\n at TEXT NOT NULL,\n type TEXT NOT NULL\n CHECK (type IN ('started', 'finished', 'answered', 'applied', 'tested', 'committed', 'dropped')),\n agent TEXT,\n commit_sha TEXT,\n link TEXT,\n note TEXT\n);\n\nCREATE INDEX events_session_at ON events(session, at);\nCREATE INDEX agents_session_status ON agents(session, status);\n`,\n `\nALTER TABLE agents ADD COLUMN claude_agent_id TEXT;\n`,\n `\nALTER TABLE sessions ADD COLUMN hooked_at TEXT;\nCREATE INDEX sessions_claude_pid ON sessions(claude_pid, hooked_at);\n`,\n `\nALTER TABLE agents ADD COLUMN pending_status TEXT;\nALTER TABLE agents ADD COLUMN done_at TEXT;\n`,\n `\nUPDATE sessions SET status = 'active' WHERE status = 'paused';\n`,\n `\nALTER TABLE sessions RENAME COLUMN claude_pid TO host_pid;\nALTER TABLE sessions ADD COLUMN host TEXT NOT NULL DEFAULT 'claude-code';\nALTER TABLE agents RENAME COLUMN claude_agent_id TO host_agent_id;\nDROP INDEX sessions_claude_pid;\nCREATE INDEX sessions_host_pid ON sessions(host, host_pid, hooked_at);\n`,\n];\n\nexport function schemaVersion(db: DatabaseSync): number {\n const row = db.prepare('PRAGMA user_version').get() as { user_version: number };\n return row.user_version;\n}\n\nexport function migrate(db: DatabaseSync): void {\n const current = schemaVersion(db);\n for (const [version, migration] of migrations.entries()) {\n if (version < current) continue;\n db.exec('BEGIN');\n try {\n db.exec(migration);\n db.exec(`PRAGMA user_version = ${version + 1}`);\n db.exec('COMMIT');\n } catch (error) {\n db.exec('ROLLBACK');\n throw error;\n }\n }\n}\n","import { DatabaseSync } from 'node:sqlite';\nimport { migrate } from './schema.ts';\n\nexport const MEMORY_DB = ':memory:';\n\nexport function openDatabase(path: string): DatabaseSync {\n const db = new DatabaseSync(path);\n if (path !== MEMORY_DB) {\n db.exec('PRAGMA journal_mode = WAL');\n }\n db.exec('PRAGMA foreign_keys = ON');\n db.exec('PRAGMA busy_timeout = 5000');\n migrate(db);\n return db;\n}\n","import {\n closeSync,\n existsSync,\n fstatSync,\n mkdirSync,\n openSync,\n readFileSync,\n readSync,\n writeFileSync,\n} from 'node:fs';\nimport { basename, dirname, join } from 'node:path';\nimport type { Group } from '../group/locate.ts';\nimport type {\n ContextFile,\n ContextFileResult,\n Host,\n HostPromptPayload,\n HostSessionPayload,\n HostSubagentPayload,\n} from './host.ts';\n\nconst TRANSCRIPT_TAIL_BYTES = 256 * 1024;\nconst TITLE_PATTERN = /\"type\":\"custom-title\",\"customTitle\":(\"(?:[^\"\\\\]|\\\\.)*\")/g;\n\nexport const REPOS_START = '<!-- rucher:repos start -->';\nexport const REPOS_END = '<!-- rucher:repos end -->';\n\nfunction payloadObject(json: string): Record<string, unknown> {\n const data = JSON.parse(json) as Record<string, unknown>;\n if (typeof data.session_id !== 'string' || !data.session_id) {\n throw new TypeError('Hook payload has no session_id');\n }\n return data;\n}\n\nfunction text(data: Record<string, unknown>, key: string): string | undefined {\n const value = data[key];\n return typeof value === 'string' ? value : undefined;\n}\n\nexport function transcriptTitle(path: string): string | undefined {\n let descriptor: number | undefined;\n try {\n descriptor = openSync(path, 'r');\n const size = fstatSync(descriptor).size;\n const length = Math.min(size, TRANSCRIPT_TAIL_BYTES);\n const buffer = Buffer.alloc(length);\n readSync(descriptor, buffer, 0, length, size - length);\n const matches = [...buffer.toString('utf8').matchAll(TITLE_PATTERN)];\n const last = matches.at(-1)?.[1];\n return last === undefined ? undefined : (JSON.parse(last) as string);\n } catch {\n return undefined;\n } finally {\n if (descriptor !== undefined) closeSync(descriptor);\n }\n}\n\nexport function reposBlock(group: Group): string {\n const repos = group.repos.length\n ? group.repos.map((repo) => `- \\`${basename(repo)}\\`: ${repo}`).join('\\n')\n : '- (none yet: `rucher group add <path>`)';\n return `${REPOS_START}\\n## Repos\\n\\n${repos}\\n${REPOS_END}`;\n}\n\nexport function replaceReposBlock(content: string, group: Group): string | null {\n const start = content.indexOf(REPOS_START);\n const end = content.indexOf(REPOS_END);\n if (start === -1 || end === -1 || end < start) return null;\n return content.slice(0, start) + reposBlock(group) + content.slice(end + REPOS_END.length);\n}\n\nexport function groupInstructions(group: Group): string {\n return `# ${group.name}\n\nMeta directory of the ${group.name} group: context only, the repos live at their own paths below and are reachable through \\`.claude/settings.json\\`. Start Claude Code sessions here.\n\n${reposBlock(group)}\n\n## Multi-agent work\n\nUse the rucher tools (Carnet) for agent statuses and the journal, and the rucher skill for the workflow.\n\n## Shared instructions\n\n(what every session of this group should know)\n`;\n}\n\nexport function groupSettings(group: Group): string {\n return `${JSON.stringify({ permissions: { additionalDirectories: group.repos } }, null, 2)}\\n`;\n}\n\ninterface Settings {\n permissions?: { additionalDirectories?: string[] } & Record<string, unknown>;\n [key: string]: unknown;\n}\n\nconst instructionsFile: ContextFile = {\n path: 'CLAUDE.md',\n sync(group): ContextFileResult {\n const path = join(group.root, 'CLAUDE.md');\n if (!existsSync(path)) {\n writeFileSync(path, groupInstructions(group));\n return 'written';\n }\n const content = readFileSync(path, 'utf8');\n const next = replaceReposBlock(content, group);\n if (next === null) return 'no-markers';\n if (next === content) return 'unchanged';\n writeFileSync(path, next);\n return 'updated';\n },\n};\n\nconst settingsFile: ContextFile = {\n path: '.claude/settings.json',\n sync(group, mode): ContextFileResult {\n const path = join(group.root, '.claude', 'settings.json');\n if (!existsSync(path)) {\n if (group.repos.length === 0) return 'unchanged';\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, groupSettings(group));\n return 'written';\n }\n const settings = JSON.parse(readFileSync(path, 'utf8')) as Settings;\n const current = settings.permissions?.additionalDirectories ?? [];\n const next =\n mode === 'exact'\n ? group.repos\n : [...current, ...group.repos.filter((repo) => !current.includes(repo))];\n if (next.length === current.length && next.every((repo, index) => repo === current[index])) {\n return 'unchanged';\n }\n settings.permissions = { ...settings.permissions, additionalDirectories: next };\n writeFileSync(path, `${JSON.stringify(settings, null, 2)}\\n`);\n return 'updated';\n },\n};\n\nexport const claudeCode: Host = {\n id: 'claude-code',\n displayName: 'Claude Code',\n processId(env) {\n const processId = Number(env.CLAUDE_PID);\n return Number.isInteger(processId) && processId > 0 ? processId : null;\n },\n sessionIdFromEnvironment(env) {\n return env.CLAUDE_CODE_SESSION_ID || undefined;\n },\n projectDirectory(env) {\n return env.CLAUDE_PROJECT_DIR || undefined;\n },\n parseSessionPayload(json): HostSessionPayload {\n const data = payloadObject(json);\n return {\n session_id: data.session_id as string,\n cwd: text(data, 'cwd'),\n source: text(data, 'source'),\n session_title: text(data, 'session_title'),\n };\n },\n parsePromptPayload(json): HostPromptPayload {\n const data = payloadObject(json);\n return {\n session_id: data.session_id as string,\n cwd: text(data, 'cwd'),\n session_title: text(data, 'session_title'),\n transcript_path: text(data, 'transcript_path'),\n };\n },\n parseSubagentPayload(json): HostSubagentPayload {\n const data = payloadObject(json);\n const agentId = text(data, 'agent_id');\n if (!agentId) throw new TypeError('Hook payload has no agent_id');\n return {\n session_id: data.session_id as string,\n agent_id: agentId,\n agent_type: text(data, 'agent_type'),\n cwd: text(data, 'cwd'),\n };\n },\n // `/rename` fires no hook of its own; the next prompt carries the new title.\n promptTitle(payload) {\n return (\n payload.session_title ??\n (payload.transcript_path ? transcriptTitle(payload.transcript_path) : undefined)\n );\n },\n resumeCommand(session) {\n return { command: 'claude', args: ['--resume', session.id] };\n },\n renameInstruction(name) {\n return `Ask the human to run /rename ${name} in Claude Code so the titles match.`;\n },\n contextFiles: [settingsFile, instructionsFile],\n};\n","import { CarnetError } from '../errors.ts';\nimport type { Group } from '../group/locate.ts';\nimport type { Session } from '../sessions.ts';\nimport { claudeCode } from './claude-code.ts';\n\nexport interface HostSessionPayload {\n session_id: string;\n cwd?: string | undefined;\n source?: string | undefined;\n session_title?: string | undefined;\n}\n\nexport interface HostPromptPayload {\n session_id: string;\n cwd?: string | undefined;\n session_title?: string | undefined;\n transcript_path?: string | undefined;\n}\n\nexport interface HostSubagentPayload {\n session_id: string;\n agent_id: string;\n agent_type?: string | undefined;\n cwd?: string | undefined;\n}\n\nexport interface ResumeCommand {\n command: string;\n args: string[];\n}\n\nexport interface ContextFile {\n path: string;\n sync(group: Group, mode: 'merge' | 'exact'): ContextFileResult;\n}\n\nexport type ContextFileResult = 'written' | 'updated' | 'unchanged' | 'no-markers';\n\nexport interface Host {\n id: string;\n displayName: string;\n processId(env: NodeJS.ProcessEnv): number | null;\n sessionIdFromEnvironment(env: NodeJS.ProcessEnv): string | undefined;\n projectDirectory(env: NodeJS.ProcessEnv): string | undefined;\n parseSessionPayload(json: string): HostSessionPayload;\n parsePromptPayload(json: string): HostPromptPayload;\n parseSubagentPayload(json: string): HostSubagentPayload;\n promptTitle(payload: HostPromptPayload): string | undefined;\n resumeCommand(session: Session): ResumeCommand;\n renameInstruction(name: string): string;\n contextFiles: readonly ContextFile[];\n}\n\nexport const DEFAULT_HOST = 'claude-code';\n\nconst HOSTS: Readonly<Record<string, Host>> = { [claudeCode.id]: claudeCode };\n\nexport function allHosts(): Host[] {\n return Object.values(HOSTS);\n}\n\nexport function hostById(id: string): Host {\n const host = HOSTS[id];\n if (!host) {\n throw new CarnetError(\n 'UNKNOWN_HOST',\n `Unknown host \"${id}\" (available: ${Object.keys(HOSTS).join(', ')})`,\n );\n }\n return host;\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nexport interface GroupReference {\n name: string;\n path: string;\n}\n\ninterface RegistryFile {\n groups: GroupReference[];\n}\n\nexport function rucherHome(env: NodeJS.ProcessEnv = process.env): string {\n return env.RUCHER_HOME ?? join(homedir(), '.rucher');\n}\n\nfunction registryPath(env?: NodeJS.ProcessEnv): string {\n return join(rucherHome(env), 'groups.json');\n}\n\nexport function readRegistry(env?: NodeJS.ProcessEnv): GroupReference[] {\n const file = registryPath(env);\n if (!existsSync(file)) return [];\n const parsed = JSON.parse(readFileSync(file, 'utf8')) as Partial<RegistryFile>;\n return parsed.groups ?? [];\n}\n\nfunction writeRegistry(groups: GroupReference[], env?: NodeJS.ProcessEnv): GroupReference[] {\n groups.sort(\n (left, right) => left.name.localeCompare(right.name) || left.path.localeCompare(right.path),\n );\n mkdirSync(rucherHome(env), { recursive: true });\n writeFileSync(\n registryPath(env),\n `${JSON.stringify({ groups } satisfies RegistryFile, null, 2)}\\n`,\n );\n return groups;\n}\n\nexport function registerGroup(\n reference: GroupReference,\n env?: NodeJS.ProcessEnv,\n): GroupReference[] {\n return writeRegistry(\n [...readRegistry(env).filter((group) => group.path !== reference.path), reference],\n env,\n );\n}\n\nexport function unregisterGroups(paths: string[], env?: NodeJS.ProcessEnv): GroupReference[] {\n return writeRegistry(\n readRegistry(env).filter((group) => !paths.includes(group.path)),\n env,\n );\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { basename, dirname, join, relative, resolve } from 'node:path';\nimport { CarnetError } from '../errors.ts';\nimport { allHosts, DEFAULT_HOST } from '../hosts/host.ts';\nimport { readRegistry } from './registry.ts';\n\nexport const GROUP_DIRECTORY = '.rucher';\nexport const REPORTS_DIRECTORY = 'reports';\nexport const GROUP_FILE = 'group.json';\nexport const DB_FILE = 'carnet.db';\n\nexport interface Group {\n root: string;\n name: string;\n repos: string[];\n hosts: string[];\n dbPath: string;\n}\n\nexport interface GroupFile {\n name: string;\n repos: string[];\n hosts?: string[] | undefined;\n}\n\nexport function isInside(directory: string, path: string): boolean {\n const relativePath = relative(directory, path);\n return (\n relativePath === '' ||\n (!relativePath.startsWith('..') && !resolve(relativePath).startsWith(relativePath))\n );\n}\n\nexport function repoNameFor(group: Group, path: string | undefined): string | null {\n if (!path) return null;\n const repo = group.repos.find((repo) => isInside(repo, resolve(path)));\n return repo ? basename(repo) : null;\n}\n\nexport function repoPathFor(group: Group, path: string | undefined | null): string | null {\n if (!path) return null;\n const absolutePath = resolve(path);\n const repo = group.repos.find((candidate) => isInside(candidate, absolutePath));\n if (!repo) return null;\n const relativePath = relative(repo, absolutePath);\n return relativePath ? join(basename(repo), relativePath) : basename(repo);\n}\n\nexport function findGroupRoot(from: string): string | null {\n let directory = resolve(from);\n for (;;) {\n // The registry home is also named .rucher, only group.json marks a group.\n if (isGroupRoot(directory)) return directory;\n const parent = dirname(directory);\n if (parent === directory) return null;\n directory = parent;\n }\n}\n\nexport function reportsDirectoryOf(group: Group): string {\n return join(group.root, GROUP_DIRECTORY, REPORTS_DIRECTORY);\n}\n\nexport function reportPathFor(group: Group, agentName: string): string {\n return join(reportsDirectoryOf(group), `${agentName}.md`);\n}\n\nexport function isGroupRoot(directory: string): boolean {\n return existsSync(join(directory, GROUP_DIRECTORY, GROUP_FILE));\n}\n\nexport function loadGroup(root: string): Group {\n const file = JSON.parse(\n readFileSync(join(root, GROUP_DIRECTORY, GROUP_FILE), 'utf8'),\n ) as GroupFile;\n return {\n root,\n name: file.name,\n repos: (file.repos ?? []).map((repo) => resolve(root, repo)),\n hosts: file.hosts?.length ? file.hosts : [DEFAULT_HOST],\n dbPath: join(root, GROUP_DIRECTORY, DB_FILE),\n };\n}\n\nexport interface LocateOptions {\n cwd?: string;\n env?: NodeJS.ProcessEnv;\n}\n\nfunction groupByRepo(path: string, env?: NodeJS.ProcessEnv): Group | null {\n for (const reference of readRegistry(env)) {\n if (!isGroupRoot(reference.path)) continue;\n const group = loadGroup(reference.path);\n if (group.repos.some((repo) => isInside(repo, path))) return group;\n }\n return null;\n}\n\nexport function locateGroup({ cwd = process.cwd(), env = process.env }: LocateOptions = {}): Group {\n const projectDirectories = allHosts()\n .map((host) => host.projectDirectory(env))\n .filter((directory): directory is string => directory !== undefined);\n const candidates = [resolve(cwd), ...projectDirectories.map((directory) => resolve(directory))];\n for (const directory of candidates) {\n const root = findGroupRoot(directory);\n if (root) return loadGroup(root);\n }\n for (const directory of candidates) {\n const group = groupByRepo(directory, env);\n if (group) return group;\n }\n throw new CarnetError(\n 'NO_GROUP',\n `No Rucher group for ${cwd}: run \"rucher init\" in a meta directory, or \"rucher group add <this repo>\" there`,\n );\n}\n","import { type Ctx, nowIso } from './context.ts';\nimport { CarnetError } from './errors.ts';\nimport { type Group, repoNameFor } from './group/locate.ts';\nimport { DEFAULT_HOST, type HostSessionPayload } from './hosts/host.ts';\n\nexport type SessionStatus = 'temporary' | 'active' | 'dropped';\n\nexport interface Session {\n id: string;\n name: string | null;\n purpose: string | null;\n status: SessionStatus;\n cwd: string | null;\n repo: string | null;\n host: string;\n hostPid: number | null;\n hookedAt: string | null;\n createdAt: string;\n lastActiveAt: string;\n}\n\ninterface SessionRow {\n id: string;\n name: string | null;\n purpose: string | null;\n status: SessionStatus;\n cwd: string | null;\n repo: string | null;\n host: string;\n host_pid: number | null;\n hooked_at: string | null;\n created_at: string;\n last_active_at: string;\n}\n\nexport type HookSessionPayload = HostSessionPayload;\n\nexport interface HookEnv {\n host?: string | undefined;\n processId?: number | null | undefined;\n RUCHER_PURGE_DAYS?: string | undefined;\n}\n\nexport interface ListSessionsOptions {\n includeTemporary?: boolean | undefined;\n includeDropped?: boolean | undefined;\n}\n\nconst COLUMNS =\n 'id, name, purpose, status, cwd, repo, host, host_pid, hooked_at, created_at, last_active_at';\n\nfunction toSession(row: SessionRow): Session {\n return {\n id: row.id,\n name: row.name,\n purpose: row.purpose,\n status: row.status,\n cwd: row.cwd,\n repo: row.repo,\n host: row.host,\n hostPid: row.host_pid,\n hookedAt: row.hooked_at,\n createdAt: row.created_at,\n lastActiveAt: row.last_active_at,\n };\n}\n\nexport function getSession(ctx: Ctx, id: string): Session | undefined {\n const row = ctx.db.prepare(`SELECT ${COLUMNS} FROM sessions WHERE id = ?`).get(id) as\n | SessionRow\n | undefined;\n return row && toSession(row);\n}\n\nexport function requireSession(ctx: Ctx, id: string): Session {\n const session = getSession(ctx, id);\n if (!session) throw new CarnetError('SESSION_NOT_FOUND', `Unknown session ${id}`);\n return session;\n}\n\nexport function ensureSession(ctx: Ctx, id: string, cwd?: string, group?: Group): Session {\n const existing = getSession(ctx, id);\n if (existing) return existing;\n const at = nowIso(ctx);\n ctx.db\n .prepare(\n \"INSERT INTO sessions (id, status, cwd, repo, created_at, last_active_at) VALUES (?, 'temporary', ?, ?, ?, ?)\",\n )\n .run(id, cwd ?? null, group ? repoNameFor(group, cwd) : null, at, at);\n return requireSession(ctx, id);\n}\n\nfunction titleAvailable(ctx: Ctx, id: string, title: string): boolean {\n const owner = sessionByName(ctx, title);\n return !owner || owner.id === id;\n}\n\nexport function adoptTitle(ctx: Ctx, id: string, rawTitle: string | undefined): Session {\n const session = requireSession(ctx, id);\n const title = rawTitle?.trim() || null;\n if (title === null || title === session.name || !titleAvailable(ctx, id, title)) return session;\n ctx.db\n .prepare(\n `UPDATE sessions\n SET name = ?, status = CASE WHEN status = 'temporary' THEN 'active' ELSE status END\n WHERE id = ?`,\n )\n .run(title, id);\n return requireSession(ctx, id);\n}\n\nexport function upsertFromHook(\n ctx: Ctx,\n group: Group,\n payload: HookSessionPayload,\n env: HookEnv = {},\n): Session {\n const at = nowIso(ctx);\n const host = env.host ?? DEFAULT_HOST;\n const hostPid = env.processId ?? null;\n const repo = repoNameFor(group, payload.cwd);\n const existing = getSession(ctx, payload.session_id);\n if (!existing) {\n ctx.db\n .prepare(\n `INSERT INTO sessions (id, status, cwd, repo, host, host_pid, hooked_at, created_at, last_active_at)\n VALUES (?, 'temporary', ?, ?, ?, ?, ?, ?, ?)`,\n )\n .run(payload.session_id, payload.cwd ?? null, repo, host, hostPid, at, at, at);\n } else {\n ctx.db\n .prepare(\n `UPDATE sessions\n SET last_active_at = ?, hooked_at = ?, cwd = ?, repo = ?, host = ?, host_pid = ?\n WHERE id = ?`,\n )\n .run(\n at,\n at,\n payload.cwd ?? existing.cwd,\n repo ?? existing.repo,\n host,\n hostPid ?? existing.hostPid,\n payload.session_id,\n );\n }\n return adoptTitle(ctx, payload.session_id, payload.session_title);\n}\n\nexport function touchSession(ctx: Ctx, id: string): void {\n ctx.db.prepare('UPDATE sessions SET last_active_at = ? WHERE id = ?').run(nowIso(ctx), id);\n}\n\nexport function nameSession(ctx: Ctx, id: string, name: string, purpose?: string): Session {\n requireSession(ctx, id);\n try {\n ctx.db\n .prepare(\n `UPDATE sessions\n SET name = ?, purpose = COALESCE(?, purpose), last_active_at = ?,\n status = CASE WHEN status = 'temporary' THEN 'active' ELSE status END\n WHERE id = ?`,\n )\n .run(name, purpose ?? null, nowIso(ctx), id);\n } catch (error) {\n if (error instanceof Error && error.message.includes('UNIQUE')) {\n throw new CarnetError('SESSION_NAME_TAKEN', `A session named \"${name}\" already exists`);\n }\n throw error;\n }\n return requireSession(ctx, id);\n}\n\nexport function listSessions(ctx: Ctx, options: ListSessionsOptions = {}): Session[] {\n const excluded: SessionStatus[] = [];\n if (!options.includeTemporary) excluded.push('temporary');\n if (!options.includeDropped) excluded.push('dropped');\n const placeholders = excluded.map(() => '?').join(', ');\n const where = excluded.length ? `WHERE status NOT IN (${placeholders})` : '';\n const rows = ctx.db\n .prepare(`SELECT ${COLUMNS} FROM sessions ${where} ORDER BY last_active_at DESC`)\n .all(...excluded) as unknown as unknown as SessionRow[];\n return rows.map(toSession);\n}\n\nexport function dropSession(ctx: Ctx, id: string): Session {\n requireSession(ctx, id);\n const at = nowIso(ctx);\n ctx.db\n .prepare(\n \"UPDATE agents SET status = 'closed', updated_at = ? WHERE session = ? AND status != 'closed'\",\n )\n .run(at, id);\n ctx.db\n .prepare(\"UPDATE sessions SET status = 'dropped', last_active_at = ? WHERE id = ?\")\n .run(at, id);\n return requireSession(ctx, id);\n}\n\nexport function defaultSession(ctx: Ctx): Session | undefined {\n const row = ctx.db\n .prepare(\n `SELECT ${COLUMNS} FROM sessions WHERE status != 'dropped' ORDER BY last_active_at DESC LIMIT 1`,\n )\n .get() as SessionRow | undefined;\n return row && toSession(row);\n}\n\nexport function sessionByName(ctx: Ctx, name: string): Session | undefined {\n const row = ctx.db.prepare(`SELECT ${COLUMNS} FROM sessions WHERE name = ?`).get(name) as\n | SessionRow\n | undefined;\n return row && toSession(row);\n}\n\nexport const MINIMUM_ID_PREFIX = 4;\n\nexport function shortId(id: string): string {\n return id.slice(0, 8);\n}\n\nexport function findSession(ctx: Ctx, reference: string): Session {\n const byName = sessionByName(ctx, reference);\n if (byName) return byName;\n const byId = getSession(ctx, reference);\n if (byId) return byId;\n const isIdPrefix = /^[0-9a-f-]+$/i.test(reference);\n if (isIdPrefix && reference.length >= MINIMUM_ID_PREFIX) {\n const rows = ctx.db\n .prepare(`SELECT ${COLUMNS} FROM sessions WHERE id LIKE ? ORDER BY last_active_at DESC`)\n .all(`${reference.toLowerCase()}%`) as unknown as SessionRow[];\n const [only, ...others] = rows;\n if (only && others.length === 0) return toSession(only);\n if (only) {\n const matches = rows.map((row) => shortId(row.id)).join(', ');\n throw new CarnetError(\n 'SESSION_AMBIGUOUS',\n `Session id \"${reference}\" matches several sessions: ${matches}`,\n );\n }\n }\n const hint =\n isIdPrefix && reference.length < MINIMUM_ID_PREFIX\n ? ` (an id prefix needs at least ${MINIMUM_ID_PREFIX} characters)`\n : '';\n throw new CarnetError('SESSION_NOT_FOUND', `Unknown session \"${reference}\"${hint}`);\n}\n\nexport function sessionByHostProcess(\n ctx: Ctx,\n host: string,\n processId: number,\n): Session | undefined {\n const row = ctx.db\n .prepare(\n `SELECT ${COLUMNS} FROM sessions\n WHERE host = ? AND host_pid = ? AND hooked_at IS NOT NULL\n ORDER BY hooked_at DESC LIMIT 1`,\n )\n .get(host, processId) as SessionRow | undefined;\n return row && toSession(row);\n}\n","import { type Ctx, nowIso } from './context.ts';\nimport { CarnetError } from './errors.ts';\nimport { requireSession, type Session } from './sessions.ts';\n\nexport interface MergeResult {\n session: Session;\n movedAgents: string[];\n closedAgents: string[];\n movedEvents: number;\n movedTable: boolean;\n}\n\nfunction namesOf(ctx: Ctx, session: string): string[] {\n const rows = ctx.db\n .prepare('SELECT name FROM agents WHERE session = ?')\n .all(session) as unknown as {\n name: string;\n }[];\n return rows.map((row) => row.name);\n}\n\nexport function mergeSessions(ctx: Ctx, fromId: string, intoId: string): MergeResult {\n if (fromId === intoId) {\n throw new CarnetError('SESSION_MERGE_INVALID', 'Cannot merge a session into itself');\n }\n const from = requireSession(ctx, fromId);\n const into = requireSession(ctx, intoId);\n const at = nowIso(ctx);\n const taken = new Set(namesOf(ctx, intoId));\n const fromNames = namesOf(ctx, fromId);\n const movedAgents = fromNames.filter((name) => !taken.has(name));\n const closedAgents = fromNames.filter((name) => taken.has(name));\n ctx.db.exec('BEGIN');\n try {\n const moveAgent = ctx.db.prepare(\n 'UPDATE agents SET session = ?, updated_at = ? WHERE session = ? AND name = ?',\n );\n for (const name of movedAgents) moveAgent.run(intoId, at, fromId, name);\n ctx.db\n .prepare(\n \"UPDATE agents SET status = 'closed', updated_at = ? WHERE session = ? AND status != 'closed'\",\n )\n .run(at, fromId);\n const movedEvents = Number(\n ctx.db.prepare('UPDATE events SET session = ? WHERE session = ?').run(intoId, fromId).changes,\n );\n const intoHasTable =\n ctx.db.prepare('SELECT 1 FROM table_items WHERE session = ?').get(intoId) !== undefined;\n const movedTable =\n !intoHasTable &&\n Number(\n ctx.db.prepare('UPDATE table_items SET session = ? WHERE session = ?').run(intoId, fromId)\n .changes,\n ) > 0;\n ctx.db.prepare('DELETE FROM table_items WHERE session = ?').run(fromId);\n const keepName = into.name === null && from.name !== null;\n ctx.db\n .prepare(\n \"UPDATE sessions SET status = 'dropped', name = NULL, last_active_at = ? WHERE id = ?\",\n )\n .run(at, fromId);\n ctx.db\n .prepare(\n `UPDATE sessions\n SET name = COALESCE(?, name), purpose = COALESCE(purpose, ?), last_active_at = ?,\n status = CASE WHEN ? AND status = 'temporary' THEN 'active' ELSE status END\n WHERE id = ?`,\n )\n .run(keepName ? from.name : null, from.purpose, at, keepName ? 1 : 0, intoId);\n ctx.db.exec('COMMIT');\n return {\n session: requireSession(ctx, intoId),\n movedAgents,\n closedAgents,\n movedEvents,\n movedTable,\n };\n } catch (error) {\n ctx.db.exec('ROLLBACK');\n throw error;\n }\n}\n","import type { Ctx } from './context.ts';\n\nexport const DEFAULT_PURGE_DAYS = 3;\nconst DAY_MILLISECONDS = 24 * 60 * 60 * 1000;\n\nexport function purgeDays(value: string | undefined): number {\n const days = Number(value);\n return Number.isFinite(days) && days > 0 ? days : DEFAULT_PURGE_DAYS;\n}\n\n// Only rows without events go: the journal is never deleted. A dropped session's agents are all\n// closed, and agent_start always logs an event, so a dropped session without events has none left.\nconst PURGEABLE = `\n id != ? AND last_active_at < ?\n AND NOT EXISTS (SELECT 1 FROM events WHERE events.session = sessions.id)\n AND (\n status = 'dropped'\n OR (status = 'temporary' AND NOT EXISTS (SELECT 1 FROM agents WHERE agents.session = sessions.id))\n )`;\n\nexport function purgeSessions(ctx: Ctx, keepId: string, days: number): string[] {\n const cutoff = new Date(ctx.now().getTime() - days * DAY_MILLISECONDS).toISOString();\n ctx.db.exec('BEGIN');\n try {\n const rows = ctx.db\n .prepare(`SELECT id FROM sessions WHERE ${PURGEABLE}`)\n .all(keepId, cutoff) as unknown as { id: string }[];\n const ids = rows.map((row) => row.id);\n for (const table of ['table_items', 'agents']) {\n const statement = ctx.db.prepare(`DELETE FROM ${table} WHERE session = ?`);\n for (const id of ids) statement.run(id);\n }\n const deleteSession = ctx.db.prepare('DELETE FROM sessions WHERE id = ?');\n for (const id of ids) deleteSession.run(id);\n ctx.db.exec('COMMIT');\n return ids;\n } catch (error) {\n ctx.db.exec('ROLLBACK');\n throw error;\n }\n}\n","import type { TableItem } from '../table.ts';\nimport { EMOJI, LEGEND, MAIN_WORKING_EMOJI } from './model.ts';\nimport type { StatusLine, StatusView } from './view.ts';\n\nexport const MAIN_WORKING_SUFFIX = '(main working, not ready)';\n\nexport function renderTableLine(item: TableItem): string {\n if (item.mainWorking) return `${MAIN_WORKING_EMOJI} ${item.text} ${MAIN_WORKING_SUFFIX}`;\n return `${EMOJI[item.status ?? 'discussing']} ${item.text}`;\n}\n\nexport function renderAgentLine(line: StatusLine): string {\n return line.note ? `${line.emoji} ${line.name} — ${line.note}` : `${line.emoji} ${line.name}`;\n}\n\nexport function renderSections(view: StatusView): string {\n const out: string[] = [];\n if (view.table) {\n out.push('On the table', ` ${renderTableLine(view.table)}`, '');\n }\n out.push('Waiting on you');\n if (view.waiting.length === 0) out.push(' nothing');\n view.waiting.forEach((line, index) => {\n out.push(` ${index + 1}. ${renderAgentLine(line)}`);\n });\n out.push('', 'Running');\n if (view.running.length === 0) out.push(' nothing');\n for (const line of view.running) out.push(` ${renderAgentLine(line)}`);\n return out.join('\\n');\n}\n\nexport function renderStatus(view: StatusView): string {\n return [view.time, '', renderSections(view), '', LEGEND].join('\\n');\n}\n","import { type Ctx, nowIso } from './context.ts';\nimport { CarnetError } from './errors.ts';\nimport { type UserActionStatus, isUserActionStatus } from './status/model.ts';\nimport { sanitizeNote } from './status/note.ts';\n\nexport interface TableItem {\n session: string;\n text: string;\n status: UserActionStatus | null;\n mainWorking: boolean;\n updatedAt: string;\n}\n\nexport interface SetTableInput {\n text: string;\n mainWorking: boolean;\n status?: UserActionStatus | undefined;\n}\n\ninterface TableRow {\n session: string;\n text: string;\n status: UserActionStatus | null;\n main_working: number;\n updated_at: string;\n}\n\nfunction toItem(row: TableRow): TableItem {\n return {\n session: row.session,\n text: row.text,\n status: row.status,\n mainWorking: row.main_working === 1,\n updatedAt: row.updated_at,\n };\n}\n\nexport function getTable(ctx: Ctx, session: string): TableItem | undefined {\n const row = ctx.db.prepare('SELECT * FROM table_items WHERE session = ?').get(session) as\n | TableRow\n | undefined;\n return row && toItem(row);\n}\n\nexport function setTable(ctx: Ctx, session: string, input: SetTableInput): TableItem {\n if (input.mainWorking && input.status) {\n throw new CarnetError(\n 'TABLE_STATUS_WITH_MAIN_WORKING',\n 'A user-action status cannot be set while main is working',\n );\n }\n if (input.status !== undefined && !isUserActionStatus(input.status)) {\n throw new CarnetError('INVALID_STATUS', `Invalid table status \"${String(input.status)}\"`);\n }\n ctx.db\n .prepare(\n `INSERT INTO table_items (session, text, status, main_working, updated_at) VALUES (?, ?, ?, ?, ?)\n ON CONFLICT(session) DO UPDATE SET text = excluded.text, status = excluded.status,\n main_working = excluded.main_working, updated_at = excluded.updated_at`,\n )\n .run(\n session,\n sanitizeNote(input.text),\n input.status ?? null,\n input.mainWorking ? 1 : 0,\n nowIso(ctx),\n );\n return getTable(ctx, session) as TableItem;\n}\n\nexport function clearTable(ctx: Ctx, session: string): void {\n ctx.db.prepare('DELETE FROM table_items WHERE session = ?').run(session);\n}\n","import { type Agent, listAgents } from '../agents.ts';\nimport type { Ctx } from '../context.ts';\nimport type { Session } from '../sessions.ts';\nimport { getTable, type TableItem } from '../table.ts';\nimport { type AgentStatus, EMOJI, isUserActionStatus } from './model.ts';\n\nexport interface StatusLine {\n name: string;\n status: AgentStatus;\n emoji: string;\n note: string | null;\n}\n\nexport interface StatusView {\n time: string;\n sessionName: string | null;\n table: TableItem | null;\n waiting: StatusLine[];\n running: StatusLine[];\n}\n\nconst BUCKET: Partial<Record<AgentStatus, number>> = { answer: 0, review: 1 };\n\nfunction bucketOf(status: AgentStatus): number {\n return BUCKET[status] ?? 2;\n}\n\nfunction compareWaiting(left: Agent, right: Agent): number {\n if (left.queueOrder !== null || right.queueOrder !== null) {\n if (left.queueOrder === null) return 1;\n if (right.queueOrder === null) return -1;\n return left.queueOrder - right.queueOrder;\n }\n return (\n bucketOf(left.status) - bucketOf(right.status) || left.startedAt.localeCompare(right.startedAt)\n );\n}\n\nfunction toLine(agent: Agent): StatusLine {\n return {\n name: agent.name,\n status: agent.status,\n emoji: agent.status === 'closed' ? '' : EMOJI[agent.status],\n note: agent.note,\n };\n}\n\nexport function formatClock(date: Date): string {\n const hours = String(date.getHours()).padStart(2, '0');\n const minutes = String(date.getMinutes()).padStart(2, '0');\n return `${hours}:${minutes}`;\n}\n\nexport function buildStatusView(ctx: Ctx, session: Session): StatusView {\n const agents = listAgents(ctx, session.id);\n const waiting = agents\n .filter((agent) => isUserActionStatus(agent.status))\n .sort(compareWaiting)\n .map(toLine);\n const running = agents.filter((agent) => agent.status === 'running').map(toLine);\n return {\n time: formatClock(ctx.now()),\n sessionName: session.name,\n table: getTable(ctx, session.id) ?? null,\n waiting,\n running,\n };\n}\n","import { type Agent, type AgentRow, publishPending, toAgent } from './agents.ts';\nimport { type Ctx, nowIso } from './context.ts';\nimport { logEvent } from './events.ts';\n\nexport const PAIRING_WINDOW_MILLISECONDS = 60_000;\nexport const STOPPED_NOTE = 'stopped without agent_done';\n\nfunction byHostAgentId(ctx: Ctx, session: string, hostAgentId: string): Agent | undefined {\n const row = ctx.db\n .prepare('SELECT * FROM agents WHERE session = ? AND host_agent_id = ?')\n .get(session, hostAgentId) as AgentRow | undefined;\n return row && toAgent(row);\n}\n\n// SubagentStart fires in dispatch order, and main registers each fork in an earlier message.\nexport function pairSubagent(ctx: Ctx, session: string, hostAgentId: string): Agent | undefined {\n const paired = byHostAgentId(ctx, session, hostAgentId);\n if (paired) return paired;\n const since = new Date(ctx.now().getTime() - PAIRING_WINDOW_MILLISECONDS).toISOString();\n const row = ctx.db\n .prepare(\n `SELECT * FROM agents\n WHERE session = ? AND status = 'running' AND host_agent_id IS NULL AND started_at >= ?\n ORDER BY started_at ASC, rowid ASC LIMIT 1`,\n )\n .get(session, since) as AgentRow | undefined;\n if (!row) return undefined;\n ctx.db\n .prepare('UPDATE agents SET host_agent_id = ? WHERE session = ? AND name = ?')\n .run(hostAgentId, session, row.name);\n return byHostAgentId(ctx, session, hostAgentId);\n}\n\nexport function stopSubagent(ctx: Ctx, session: string, hostAgentId: string): Agent | undefined {\n const agent = byHostAgentId(ctx, session, hostAgentId);\n if (!agent || agent.status !== 'running') return undefined;\n if (agent.pendingStatus) {\n publishPending(ctx, session, agent.name);\n return byHostAgentId(ctx, session, hostAgentId);\n }\n ctx.db\n .prepare(\n \"UPDATE agents SET status = 'review', note = ?, updated_at = ? WHERE session = ? AND name = ?\",\n )\n .run(STOPPED_NOTE, nowIso(ctx), session, agent.name);\n logEvent(ctx, session, { type: 'finished', agent: agent.name, note: STOPPED_NOTE });\n return byHostAgentId(ctx, session, hostAgentId);\n}\n","import type { DatabaseSync } from 'node:sqlite';\nimport {\n dropAgent,\n finishAgent,\n type FinishAgentInput,\n getAgent,\n listAgents,\n type ListAgentsOptions,\n reorderQueue,\n startAgent,\n type StartAgentInput,\n updateAgent,\n type UpdateAgentInput,\n} from './agents.ts';\nimport type { Ctx } from './context.ts';\nimport { openDatabase } from './db/open.ts';\nimport { logEvent, type LogEventInput, readEvents } from './events.ts';\nimport { type Group, type LocateOptions, locateGroup } from './group/locate.ts';\nimport { mergeSessions } from './merge.ts';\nimport { purgeSessions } from './purge.ts';\nimport {\n defaultSession,\n dropSession,\n ensureSession,\n getSession,\n type HookEnv,\n type HookSessionPayload,\n listSessions,\n type ListSessionsOptions,\n nameSession,\n adoptTitle,\n requireSession,\n findSession,\n type Session,\n sessionByHostProcess,\n sessionByName,\n touchSession,\n upsertFromHook,\n} from './sessions.ts';\nimport { renderSections, renderStatus } from './status/render.ts';\nimport { buildStatusView } from './status/view.ts';\nimport { pairSubagent, stopSubagent } from './subagents.ts';\nimport { clearTable, getTable, setTable, type SetTableInput } from './table.ts';\n\nexport interface CarnetOptions {\n now?: () => Date;\n}\n\nexport class Carnet {\n readonly db: DatabaseSync;\n private readonly ctx: Ctx;\n\n constructor(\n readonly group: Group,\n db: DatabaseSync,\n options: CarnetOptions = {},\n ) {\n this.db = db;\n this.ctx = { db, now: options.now ?? (() => new Date()) };\n }\n\n readonly sessions = {\n get: (id: string) => getSession(this.ctx, id),\n find: (reference: string) => findSession(this.ctx, reference),\n require: (id: string) => requireSession(this.ctx, id),\n ensure: (id: string, cwd?: string) => ensureSession(this.ctx, id, cwd, this.group),\n upsertFromHook: (payload: HookSessionPayload, env?: HookEnv) =>\n upsertFromHook(this.ctx, this.group, payload, env),\n touch: (id: string) => touchSession(this.ctx, id),\n merge: (fromId: string, intoId: string) => mergeSessions(this.ctx, fromId, intoId),\n purge: (keepId: string, days: number) => purgeSessions(this.ctx, keepId, days),\n name: (id: string, name: string, purpose?: string) => nameSession(this.ctx, id, name, purpose),\n adoptTitle: (id: string, title: string | undefined) => adoptTitle(this.ctx, id, title),\n list: (options?: ListSessionsOptions) => listSessions(this.ctx, options),\n drop: (id: string) => dropSession(this.ctx, id),\n default: () => defaultSession(this.ctx),\n byName: (name: string) => sessionByName(this.ctx, name),\n byHostProcess: (host: string, processId: number) =>\n sessionByHostProcess(this.ctx, host, processId),\n };\n\n readonly agents = {\n get: (session: string, name: string) => getAgent(this.ctx, session, name),\n start: (session: string, input: StartAgentInput) => startAgent(this.ctx, session, input),\n update: (session: string, name: string, input: UpdateAgentInput) =>\n updateAgent(this.ctx, session, name, input),\n finish: (session: string, name: string, input: FinishAgentInput) =>\n finishAgent(this.ctx, session, name, input),\n drop: (session: string, name: string, reason?: string) =>\n dropAgent(this.ctx, session, name, reason),\n list: (session: string, options?: ListAgentsOptions) => listAgents(this.ctx, session, options),\n reorderQueue: (session: string, names: string[]) => reorderQueue(this.ctx, session, names),\n pairSubagent: (session: string, hostAgentId: string) =>\n pairSubagent(this.ctx, session, hostAgentId),\n stopSubagent: (session: string, hostAgentId: string) =>\n stopSubagent(this.ctx, session, hostAgentId),\n };\n\n readonly table = {\n get: (session: string) => getTable(this.ctx, session),\n set: (session: string, input: SetTableInput) => setTable(this.ctx, session, input),\n clear: (session: string) => clearTable(this.ctx, session),\n };\n\n readonly events = {\n log: (session: string, input: LogEventInput) => logEvent(this.ctx, session, input),\n read: (session: string, limit?: number) => readEvents(this.ctx, { session, limit }),\n };\n\n readonly status = {\n view: (session: Session) => buildStatusView(this.ctx, session),\n render: (session: Session) => renderStatus(buildStatusView(this.ctx, session)),\n sections: (session: Session) => renderSections(buildStatusView(this.ctx, session)),\n };\n\n close(): void {\n this.db.close();\n }\n}\n\nexport interface OpenCarnetOptions extends LocateOptions, CarnetOptions {\n dbPath?: string;\n}\n\nexport function openCarnet(options: OpenCarnetOptions = {}): Carnet {\n const group = locateGroup(options);\n return new Carnet(group, openDatabase(options.dbPath ?? group.dbPath), options);\n}\n","import type { Carnet } from './carnet.ts';\nimport type { CarnetEvent } from './events.ts';\nimport type { Session } from './sessions.ts';\nimport { formatClock } from './status/view.ts';\n\nexport const RESUME_EVENTS = 20;\nexport const RESUME_MAX_CHARACTERS = 2000;\n\nexport interface ResumeContextOptions {\n events?: number;\n maxCharacters?: number;\n}\n\nexport function formatEventLine(event: CarnetEvent): string {\n return [\n formatClock(new Date(event.at)),\n event.type,\n event.agent,\n event.link ?? event.note,\n event.commit?.slice(0, 7),\n ]\n .filter(Boolean)\n .join(' ');\n}\n\nexport function sessionLabel(session: Session): string {\n const name = session.name ?? session.id;\n return session.purpose ? `${name} (${session.purpose})` : name;\n}\n\nexport function resumeContext(\n carnet: Carnet,\n session: Session,\n { events = RESUME_EVENTS, maxCharacters = RESUME_MAX_CHARACTERS }: ResumeContextOptions = {},\n): string {\n const head = `Rucher session ${sessionLabel(session)}, state when resumed:\\n\\n${carnet.status.render(session)}`;\n const lines = carnet.events.read(session.id, events).map(formatEventLine);\n const build = () =>\n lines.length ? `${head}\\n\\nLast events\\n${lines.map((line) => ` ${line}`).join('\\n')}` : head;\n let text = build();\n // Drop the oldest events first: the latest steps matter most when picking up work.\n while (text.length > maxCharacters && lines.length > 0) {\n lines.shift();\n text = build();\n }\n return text.length > maxCharacters ? `${text.slice(0, maxCharacters - 1)}…` : text;\n}\n"],"mappings":";;;;;AAOA,SAAgB,OAAO,KAAkB;CACvC,OAAO,IAAI,IAAI,CAAC,CAAC,YAAY;AAC/B;;ACOA,IAAa,cAAb,cAAiC,MAAM;CAE1B;CADX,YACE,MACA,SACA;EACA,MAAM,OAAO;EAHJ,KAAA,OAAA;EAIT,KAAK,OAAO;CACd;AACF;;ACrBA,MAAa,cAAc;CACzB;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAuCA,SAAgB,YAAY,OAAoC;CAC9D,OAAO,OAAO,UAAU,YAAa,YAAkC,SAAS,KAAK;AACvF;AAEA,SAAS,QAAQ,KAA4B;CAC3C,OAAO;EACL,IAAI,IAAI;EACR,SAAS,IAAI;EACb,IAAI,IAAI;EACR,MAAM,IAAI;EACV,OAAO,IAAI;EACX,QAAQ,IAAI;EACZ,MAAM,IAAI;EACV,MAAM,IAAI;CACZ;AACF;AAEA,SAAgB,SAAS,KAAU,SAAiB,OAAmC;CACrF,IAAI,CAAC,YAAY,MAAM,IAAI,GACzB,MAAM,IAAI,YAAY,sBAAsB,uBAAuB,OAAO,MAAM,IAAI,EAAE,EAAE;CAE1F,MAAM,SAAS,IAAI,GAChB,QACC,oGACF,CAAC,CACA,IACC,SACA,OAAO,GAAG,GACV,MAAM,MACN,MAAM,SAAS,MACf,MAAM,UAAU,MAChB,MAAM,QAAQ,MACd,MAAM,QAAQ,IAChB;CAIF,OAAO,QAHK,IAAI,GACb,QAAQ,mCAAmC,CAAC,CAC5C,IAAI,OAAO,eACG,CAAC;AACpB;AAEA,SAAgB,WAAW,KAAU,EAAE,SAAS,QAAQ,MAAwC;CAM9F,OALa,IAAI,GACd,QACC,iGACF,CAAC,CACA,IAAI,SAAS,KACN,CAAC,CAAC,IAAI,OAAO;AACzB;;ACjGA,MAAa,iBAAiB;CAC5B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAIA,MAAa,uBAAuB;CAClC;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAIA,MAAa,QAAwD;CACnE,SAAS;CACT,YAAY;CACZ,QAAQ;CACR,QAAQ;CACR,OAAO;CACP,MAAM;CACN,QAAQ;CACR,QAAQ;AACV;AAEA,MAAa,qBAAqB;AAElC,MAAa,SACX;AAEF,SAAgB,cAAc,OAAsC;CAClE,OAAO,OAAO,UAAU,YAAa,eAAqC,SAAS,KAAK;AAC1F;AAEA,SAAgB,mBAAmB,OAA2C;CAC5E,OAAO,OAAO,UAAU,YAAa,qBAA2C,SAAS,KAAK;AAChG;AAEA,MAAa,sBAAsB,CAAC,WAAW,GAAG,oBAAoB;;AClDtE,MAAa,WAAW;AAExB,MAAM,cAAc;AACpB,MAAM,mBAAmB;AAEzB,SAAS,cAAc,OAAwB;CAC7C,IAAI,YAAY,KAAK,KAAK,GAAG,OAAO;CACpC,MAAM,UAAU,MAAM,MAAM,GAAG,CAAC,CAAC,SAAS;CAC1C,OAAO,WAAW,KAAM,YAAY,KAAK,iBAAiB,KAAK,KAAK;AACtE;AAEA,SAAgB,aAAa,MAAsB;CACjD,MAAM,UAAU,KACb,WAAW,KAAK,EAAE,CAAC,CACnB,MAAM,KAAK,CAAC,CACZ,QAAQ,UAAU,SAAS,CAAC,cAAc,KAAK,CAAC,CAAC,CACjD,KAAK,GAAG,CAAC,CACT,KAAK;CACR,IAAI,QAAQ,UAAA,IAAoB,OAAO;CACvC,OAAO,GAAG,QAAQ,MAAM,GAAG,EAAY,CAAC,CAAC,QAAQ,EAAE;AACrD;;AC0CA,SAAgB,QAAQ,KAAsB;CAC5C,OAAO;EACL,SAAS,IAAI;EACb,MAAM,IAAI;EACV,MAAM,IAAI;EACV,QAAQ,IAAI;EACZ,MAAM,IAAI;EACV,YAAY,IAAI;EAChB,YAAY,IAAI;EAChB,UAAU,IAAI;EACd,YAAY,IAAI;EAChB,aAAa,IAAI;EACjB,eAAe,IAAI;EACnB,QAAQ,IAAI;EACZ,WAAW,IAAI;EACf,WAAW,IAAI;CACjB;AACF;AAEA,MAAa,iBAAiB;AAC9B,MAAa,gCAAgC;AAE7C,SAAgB,eAAe,KAAU,SAAiB,MAAoB;CAC5E,MAAM,MAAM,IAAI,GACb,QAAQ,+EAA+E,CAAC,CACxF,IAAI,SAAS,IAAI;CACpB,IAAI,CAAC,KAAK,gBAAgB;CAC1B,IAAI,GACD,QACC,8HACF,CAAC,CACA,IAAI,OAAO,GAAG,GAAG,SAAS,IAAI;CACjC,SAAS,KAAK,SAAS;EAAE,MAAM;EAAY,OAAO;EAAM,MAAM,IAAI,eAAe,KAAA;CAAU,CAAC;AAC9F;AAGA,SAAS,eAAe,KAAU,SAAuB;CACvD,MAAM,0BAAS,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,QAAQ,IAAI,6BAA6B,EAAA,CAAE,YAAY;CACzF,MAAM,OAAO,IAAI,GACd,QACC,2FACF,CAAC,CACA,IAAI,SAAS,MAAM;CACtB,KAAK,MAAM,OAAO,MAAM,eAAe,KAAK,SAAS,IAAI,IAAI;AAC/D;AAEA,SAAgB,SAAS,KAAU,SAAiB,MAAiC;CACnF,eAAe,KAAK,OAAO;CAC3B,MAAM,MAAM,IAAI,GACb,QAAQ,qDAAqD,CAAC,CAC9D,IAAI,SAAS,IAAI;CACpB,OAAO,OAAO,QAAQ,GAAG;AAC3B;AAEA,SAAS,aAAa,KAAU,SAAiB,MAAqB;CACpE,MAAM,QAAQ,SAAS,KAAK,SAAS,IAAI;CACzC,IAAI,CAAC,OAAO,MAAM,IAAI,YAAY,mBAAmB,aAAa,KAAK,kBAAkB;CACzF,OAAO;AACT;AAEA,SAAgB,WAAW,KAAU,SAAiB,OAA+B;CACnF,MAAM,OAAO,aAAa,MAAM,IAAI;CACpC,MAAM,WAAW,SAAS,KAAK,SAAS,IAAI;CAC5C,IAAI,YAAY,SAAS,WAAW,UAClC,MAAM,IAAI,YAAY,gBAAgB,UAAU,KAAK,kCAAkC;CAEzF,IAAI,UACF,IAAI,GAAG,QAAQ,mDAAmD,CAAC,CAAC,IAAI,SAAS,IAAI;CAEvF,MAAM,KAAK,OAAO,GAAG;CACrB,IAAI,GACD,QACC;+CAEF,CAAC,CACA,IAAI,SAAS,MAAM,MAAM,MAAM,MAAM,cAAc,MAAM,MAAM,YAAY,MAAM,IAAI,EAAE;CAC1F,SAAS,KAAK,SAAS;EAAE,MAAM;EAAW,OAAO;EAAM,MAAM,MAAM;CAAK,CAAC;CACzE,OAAO,aAAa,KAAK,SAAS,IAAI;AACxC;AAEA,SAAgB,YACd,KACA,SACA,MACA,OACO;CACP,MAAM,UAAU,aAAa,KAAK,SAAS,IAAI;CAC/C,IAAI,CAAC,cAAc,MAAM,MAAM,KAAK,MAAM,WAAW,UACnD,MAAM,IAAI,YACR,kBACA,yBAAyB,MAAM,OAAO,2BACxC;CAEF,MAAM,OAAO,MAAM,SAAS,KAAA,IAAY,OAAO,aAAa,MAAM,IAAI;CACtE,IAAI,QAAQ,eAAe,eAAe,KAAK,SAAS,IAAI;CAC5D,IAAI,GACD,QACC,kNACF,CAAC,CACA,IAAI,MAAM,MAAM,QAAQ,MAAM,QAAQ,MAAM,cAAc,MAAM,OAAO,GAAG,GAAG,SAAS,IAAI;CAC7F,OAAO,aAAa,KAAK,SAAS,IAAI;AACxC;AAEA,SAAgB,YACd,KACA,SACA,MACA,OACO;CACP,MAAM,QAAQ,aAAa,KAAK,SAAS,IAAI;CAC7C,MAAM,SAAsB,MAAM,eAAe,WAAW;CAC5D,MAAM,KAAK,OAAO,GAAG;CAErB,IAAI,GACD,QACC,6HACF,CAAC,CACA,IAAI,QAAQ,gBAAgB,MAAM,YAAY,IAAI,IAAI,SAAS,IAAI;CACtE,IAAI,CAAC,MAAM,aAAa,eAAe,KAAK,SAAS,IAAI;CACzD,OAAO,aAAa,KAAK,SAAS,IAAI;AACxC;AAEA,SAAgB,UAAU,KAAU,SAAiB,MAAc,QAAwB;CACzF,aAAa,KAAK,SAAS,IAAI;CAC/B,IAAI,GACD,QACC,wGACF,CAAC,CACA,IAAI,OAAO,GAAG,GAAG,SAAS,IAAI;CACjC,SAAS,KAAK,SAAS;EAAE,MAAM;EAAW,OAAO;EAAM,MAAM;CAAO,CAAC;CACrE,OAAO,aAAa,KAAK,SAAS,IAAI;AACxC;AAEA,SAAgB,WAAW,KAAU,SAAiB,UAA6B,CAAC,GAAY;CAC9F,eAAe,KAAK,OAAO;CAC3B,MAAM,QAAQ,QAAQ,gBAAgB,KAAK;CAI3C,OAHa,IAAI,GACd,QAAQ,0CAA0C,MAAM,mCAAmC,CAAC,CAC5F,IAAI,OACG,CAAC,CAAC,IAAI,OAAO;AACzB;AAEA,SAAgB,aAAa,KAAU,SAAiB,OAA0B;CAChF,MAAM,KAAK,OAAO,GAAG;CACrB,IAAI,GAAG,QAAQ,wDAAwD,CAAC,CAAC,IAAI,OAAO;CACpF,MAAM,SAAS,IAAI,GAAG,QACpB,kFACF;CACA,MAAM,SAAS,MAAM,UAAU;EAC7B,OAAO,IAAI,QAAQ,GAAG,IAAI,SAAS,aAAa,IAAI,CAAC;CACvD,CAAC;CACD,OAAO,WAAW,KAAK,OAAO;AAChC;;ACpNA,MAAa,aAAgC;CAC3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqDA;;;CAGA;;;;CAIA;;;;CAIA;;;CAGA;;;;;;;AAOF;AAEA,SAAgB,cAAc,IAA0B;CAEtD,OADY,GAAG,QAAQ,qBAAqB,CAAC,CAAC,IACrC,CAAC,CAAC;AACb;AAEA,SAAgB,QAAQ,IAAwB;CAC9C,MAAM,UAAU,cAAc,EAAE;CAChC,KAAK,MAAM,CAAC,SAAS,cAAc,WAAW,QAAQ,GAAG;EACvD,IAAI,UAAU,SAAS;EACvB,GAAG,KAAK,OAAO;EACf,IAAI;GACF,GAAG,KAAK,SAAS;GACjB,GAAG,KAAK,yBAAyB,UAAU,GAAG;GAC9C,GAAG,KAAK,QAAQ;EAClB,SAAS,OAAO;GACd,GAAG,KAAK,UAAU;GAClB,MAAM;EACR;CACF;AACF;;AC/FA,MAAa,YAAY;AAEzB,SAAgB,aAAa,MAA4B;CACvD,MAAM,KAAK,IAAI,aAAa,IAAI;CAChC,IAAI,SAAA,YACF,GAAG,KAAK,2BAA2B;CAErC,GAAG,KAAK,0BAA0B;CAClC,GAAG,KAAK,4BAA4B;CACpC,QAAQ,EAAE;CACV,OAAO;AACT;;ACOA,MAAM,wBAAwB;AAC9B,MAAM,gBAAgB;AAEtB,MAAa,cAAc;AAC3B,MAAa,YAAY;AAEzB,SAAS,cAAc,MAAuC;CAC5D,MAAM,OAAO,KAAK,MAAM,IAAI;CAC5B,IAAI,OAAO,KAAK,eAAe,YAAY,CAAC,KAAK,YAC/C,MAAM,IAAI,UAAU,gCAAgC;CAEtD,OAAO;AACT;AAEA,SAAS,KAAK,MAA+B,KAAiC;CAC5E,MAAM,QAAQ,KAAK;CACnB,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;AAC7C;AAEA,SAAgB,gBAAgB,MAAkC;CAChE,IAAI;CACJ,IAAI;EACF,aAAa,SAAS,MAAM,GAAG;EAC/B,MAAM,OAAO,UAAU,UAAU,CAAC,CAAC;EACnC,MAAM,SAAS,KAAK,IAAI,MAAM,qBAAqB;EACnD,MAAM,SAAS,OAAO,MAAM,MAAM;EAClC,SAAS,YAAY,QAAQ,GAAG,QAAQ,OAAO,MAAM;EAErD,MAAM,OAAO,CADI,GAAG,OAAO,SAAS,MAAM,CAAC,CAAC,SAAS,aAAa,CAC/C,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG;EAC9B,OAAO,SAAS,KAAA,IAAY,KAAA,IAAa,KAAK,MAAM,IAAI;CAC1D,QAAQ;EACN;CACF,UAAU;EACR,IAAI,eAAe,KAAA,GAAW,UAAU,UAAU;CACpD;AACF;AAEA,SAAgB,WAAW,OAAsB;CAC/C,MAAM,QAAQ,MAAM,MAAM,SACtB,MAAM,MAAM,KAAK,SAAS,OAAO,SAAS,IAAI,EAAE,MAAM,MAAM,CAAC,CAAC,KAAK,IAAI,IACvE;CACJ,OAAO,GAAG,YAAY,gBAAgB,MAAM,IAAI;AAClD;AAEA,SAAgB,kBAAkB,SAAiB,OAA6B;CAC9E,MAAM,QAAQ,QAAQ,QAAQ,WAAW;CACzC,MAAM,MAAM,QAAQ,QAAQ,SAAS;CACrC,IAAI,UAAU,MAAM,QAAQ,MAAM,MAAM,OAAO,OAAO;CACtD,OAAO,QAAQ,MAAM,GAAG,KAAK,IAAI,WAAW,KAAK,IAAI,QAAQ,MAAM,MAAM,EAAgB;AAC3F;AAEA,SAAgB,kBAAkB,OAAsB;CACtD,OAAO,KAAK,MAAM,KAAK;;wBAED,MAAM,KAAK;;EAEjC,WAAW,KAAK,EAAE;;;;;;;;;;AAUpB;AAEA,SAAgB,cAAc,OAAsB;CAClD,OAAO,GAAG,KAAK,UAAU,EAAE,aAAa,EAAE,uBAAuB,MAAM,MAAM,EAAE,GAAG,MAAM,CAAC,EAAE;AAC7F;AAiDA,MAAa,aAAmB;CAC9B,IAAI;CACJ,aAAa;CACb,UAAU,KAAK;EACb,MAAM,YAAY,OAAO,IAAI,UAAU;EACvC,OAAO,OAAO,UAAU,SAAS,KAAK,YAAY,IAAI,YAAY;CACpE;CACA,yBAAyB,KAAK;EAC5B,OAAO,IAAI,0BAA0B,KAAA;CACvC;CACA,iBAAiB,KAAK;EACpB,OAAO,IAAI,sBAAsB,KAAA;CACnC;CACA,oBAAoB,MAA0B;EAC5C,MAAM,OAAO,cAAc,IAAI;EAC/B,OAAO;GACL,YAAY,KAAK;GACjB,KAAK,KAAK,MAAM,KAAK;GACrB,QAAQ,KAAK,MAAM,QAAQ;GAC3B,eAAe,KAAK,MAAM,eAAe;EAC3C;CACF;CACA,mBAAmB,MAAyB;EAC1C,MAAM,OAAO,cAAc,IAAI;EAC/B,OAAO;GACL,YAAY,KAAK;GACjB,KAAK,KAAK,MAAM,KAAK;GACrB,eAAe,KAAK,MAAM,eAAe;GACzC,iBAAiB,KAAK,MAAM,iBAAiB;EAC/C;CACF;CACA,qBAAqB,MAA2B;EAC9C,MAAM,OAAO,cAAc,IAAI;EAC/B,MAAM,UAAU,KAAK,MAAM,UAAU;EACrC,IAAI,CAAC,SAAS,MAAM,IAAI,UAAU,8BAA8B;EAChE,OAAO;GACL,YAAY,KAAK;GACjB,UAAU;GACV,YAAY,KAAK,MAAM,YAAY;GACnC,KAAK,KAAK,MAAM,KAAK;EACvB;CACF;CAEA,YAAY,SAAS;EACnB,OACE,QAAQ,kBACP,QAAQ,kBAAkB,gBAAgB,QAAQ,eAAe,IAAI,KAAA;CAE1E;CACA,cAAc,SAAS;EACrB,OAAO;GAAE,SAAS;GAAU,MAAM,CAAC,YAAY,QAAQ,EAAE;EAAE;CAC7D;CACA,kBAAkB,MAAM;EACtB,OAAO,gCAAgC,KAAK;CAC9C;CACA,cAAc,CAAC;EA/Ef,MAAM;EACN,KAAK,OAAO,MAAyB;GACnC,MAAM,OAAO,KAAK,MAAM,MAAM,WAAW,eAAe;GACxD,IAAI,CAAC,WAAW,IAAI,GAAG;IACrB,IAAI,MAAM,MAAM,WAAW,GAAG,OAAO;IACrC,UAAU,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;IAC5C,cAAc,MAAM,cAAc,KAAK,CAAC;IACxC,OAAO;GACT;GACA,MAAM,WAAW,KAAK,MAAM,aAAa,MAAM,MAAM,CAAC;GACtD,MAAM,UAAU,SAAS,aAAa,yBAAyB,CAAC;GAChE,MAAM,OACJ,SAAS,UACL,MAAM,QACN,CAAC,GAAG,SAAS,GAAG,MAAM,MAAM,QAAQ,SAAS,CAAC,QAAQ,SAAS,IAAI,CAAC,CAAC;GAC3E,IAAI,KAAK,WAAW,QAAQ,UAAU,KAAK,OAAO,MAAM,UAAU,SAAS,QAAQ,MAAM,GACvF,OAAO;GAET,SAAS,cAAc;IAAE,GAAG,SAAS;IAAa,uBAAuB;GAAK;GAC9E,cAAc,MAAM,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,EAAE,GAAG;GAC5D,OAAO;EACT;CA0De,GAAc;EAhG7B,MAAM;EACN,KAAK,OAA0B;GAC7B,MAAM,OAAO,KAAK,MAAM,MAAM,WAAW;GACzC,IAAI,CAAC,WAAW,IAAI,GAAG;IACrB,cAAc,MAAM,kBAAkB,KAAK,CAAC;IAC5C,OAAO;GACT;GACA,MAAM,UAAU,aAAa,MAAM,MAAM;GACzC,MAAM,OAAO,kBAAkB,SAAS,KAAK;GAC7C,IAAI,SAAS,MAAM,OAAO;GAC1B,IAAI,SAAS,SAAS,OAAO;GAC7B,cAAc,MAAM,IAAI;GACxB,OAAO;EACT;CAmF6B,CAAgB;AAC/C;;AC/IA,MAAa,eAAe;AAE5B,MAAM,QAAwC,GAAG,WAAW,KAAK,WAAW;AAE5E,SAAgB,WAAmB;CACjC,OAAO,OAAO,OAAO,KAAK;AAC5B;AAEA,SAAgB,SAAS,IAAkB;CACzC,MAAM,OAAO,MAAM;CACnB,IAAI,CAAC,MACH,MAAM,IAAI,YACR,gBACA,iBAAiB,GAAG,gBAAgB,OAAO,KAAK,KAAK,CAAC,CAAC,KAAK,IAAI,EAAE,EACpE;CAEF,OAAO;AACT;;ACzDA,SAAgB,WAAW,MAAyB,QAAQ,KAAa;CACvE,OAAO,IAAI,eAAe,KAAK,QAAQ,GAAG,SAAS;AACrD;AAEA,SAAS,aAAa,KAAiC;CACrD,OAAO,KAAK,WAAW,GAAG,GAAG,aAAa;AAC5C;AAEA,SAAgB,aAAa,KAA2C;CACtE,MAAM,OAAO,aAAa,GAAG;CAC7B,IAAI,CAAC,WAAW,IAAI,GAAG,OAAO,CAAC;CAE/B,OADe,KAAK,MAAM,aAAa,MAAM,MAAM,CACvC,CAAC,CAAC,UAAU,CAAC;AAC3B;AAEA,SAAS,cAAc,QAA0B,KAA2C;CAC1F,OAAO,MACJ,MAAM,UAAU,KAAK,KAAK,cAAc,MAAM,IAAI,KAAK,KAAK,KAAK,cAAc,MAAM,IAAI,CAC5F;CACA,UAAU,WAAW,GAAG,GAAG,EAAE,WAAW,KAAK,CAAC;CAC9C,cACE,aAAa,GAAG,GAChB,GAAG,KAAK,UAAU,EAAE,OAAO,GAA0B,MAAM,CAAC,EAAE,GAChE;CACA,OAAO;AACT;AAEA,SAAgB,cACd,WACA,KACkB;CAClB,OAAO,cACL,CAAC,GAAG,aAAa,GAAG,CAAC,CAAC,QAAQ,UAAU,MAAM,SAAS,UAAU,IAAI,GAAG,SAAS,GACjF,GACF;AACF;AAEA,SAAgB,iBAAiB,OAAiB,KAA2C;CAC3F,OAAO,cACL,aAAa,GAAG,CAAC,CAAC,QAAQ,UAAU,CAAC,MAAM,SAAS,MAAM,IAAI,CAAC,GAC/D,GACF;AACF;;ACjDA,MAAa,kBAAkB;AAC/B,MAAa,oBAAoB;AACjC,MAAa,aAAa;AAC1B,MAAa,UAAU;AAgBvB,SAAgB,SAAS,WAAmB,MAAuB;CACjE,MAAM,eAAe,SAAS,WAAW,IAAI;CAC7C,OACE,iBAAiB,MAChB,CAAC,aAAa,WAAW,IAAI,KAAK,CAAC,QAAQ,YAAY,CAAC,CAAC,WAAW,YAAY;AAErF;AAEA,SAAgB,YAAY,OAAc,MAAyC;CACjF,IAAI,CAAC,MAAM,OAAO;CAClB,MAAM,OAAO,MAAM,MAAM,MAAM,SAAS,SAAS,MAAM,QAAQ,IAAI,CAAC,CAAC;CACrE,OAAO,OAAO,SAAS,IAAI,IAAI;AACjC;AAEA,SAAgB,YAAY,OAAc,MAAgD;CACxF,IAAI,CAAC,MAAM,OAAO;CAClB,MAAM,eAAe,QAAQ,IAAI;CACjC,MAAM,OAAO,MAAM,MAAM,MAAM,cAAc,SAAS,WAAW,YAAY,CAAC;CAC9E,IAAI,CAAC,MAAM,OAAO;CAClB,MAAM,eAAe,SAAS,MAAM,YAAY;CAChD,OAAO,eAAe,KAAK,SAAS,IAAI,GAAG,YAAY,IAAI,SAAS,IAAI;AAC1E;AAEA,SAAgB,cAAc,MAA6B;CACzD,IAAI,YAAY,QAAQ,IAAI;CAC5B,SAAS;EAEP,IAAI,YAAY,SAAS,GAAG,OAAO;EACnC,MAAM,SAAS,QAAQ,SAAS;EAChC,IAAI,WAAW,WAAW,OAAO;EACjC,YAAY;CACd;AACF;AAEA,SAAgB,mBAAmB,OAAsB;CACvD,OAAO,KAAK,MAAM,MAAM,iBAAiB,iBAAiB;AAC5D;AAEA,SAAgB,cAAc,OAAc,WAA2B;CACrE,OAAO,KAAK,mBAAmB,KAAK,GAAG,GAAG,UAAU,IAAI;AAC1D;AAEA,SAAgB,YAAY,WAA4B;CACtD,OAAO,WAAW,KAAK,WAAW,iBAAiB,UAAU,CAAC;AAChE;AAEA,SAAgB,UAAU,MAAqB;CAC7C,MAAM,OAAO,KAAK,MAChB,aAAa,KAAK,MAAM,iBAAiB,UAAU,GAAG,MAAM,CAC9D;CACA,OAAO;EACL;EACA,MAAM,KAAK;EACX,QAAQ,KAAK,SAAS,CAAC,EAAA,CAAG,KAAK,SAAS,QAAQ,MAAM,IAAI,CAAC;EAC3D,OAAO,KAAK,OAAO,SAAS,KAAK,QAAQ,CAAC,YAAY;EACtD,QAAQ,KAAK,MAAM,iBAAiB,OAAO;CAC7C;AACF;AAOA,SAAS,YAAY,MAAc,KAAuC;CACxE,KAAK,MAAM,aAAa,aAAa,GAAG,GAAG;EACzC,IAAI,CAAC,YAAY,UAAU,IAAI,GAAG;EAClC,MAAM,QAAQ,UAAU,UAAU,IAAI;EACtC,IAAI,MAAM,MAAM,MAAM,SAAS,SAAS,MAAM,IAAI,CAAC,GAAG,OAAO;CAC/D;CACA,OAAO;AACT;AAEA,SAAgB,YAAY,EAAE,MAAM,QAAQ,IAAI,GAAG,MAAM,QAAQ,QAAuB,CAAC,GAAU;CACjG,MAAM,qBAAqB,SAAS,CAAC,CAClC,KAAK,SAAS,KAAK,iBAAiB,GAAG,CAAC,CAAC,CACzC,QAAQ,cAAmC,cAAc,KAAA,CAAS;CACrE,MAAM,aAAa,CAAC,QAAQ,GAAG,GAAG,GAAG,mBAAmB,KAAK,cAAc,QAAQ,SAAS,CAAC,CAAC;CAC9F,KAAK,MAAM,aAAa,YAAY;EAClC,MAAM,OAAO,cAAc,SAAS;EACpC,IAAI,MAAM,OAAO,UAAU,IAAI;CACjC;CACA,KAAK,MAAM,aAAa,YAAY;EAClC,MAAM,QAAQ,YAAY,WAAW,GAAG;EACxC,IAAI,OAAO,OAAO;CACpB;CACA,MAAM,IAAI,YACR,YACA,uBAAuB,IAAI,iFAC7B;AACF;;ACnEA,MAAM,UACJ;AAEF,SAAS,UAAU,KAA0B;CAC3C,OAAO;EACL,IAAI,IAAI;EACR,MAAM,IAAI;EACV,SAAS,IAAI;EACb,QAAQ,IAAI;EACZ,KAAK,IAAI;EACT,MAAM,IAAI;EACV,MAAM,IAAI;EACV,SAAS,IAAI;EACb,UAAU,IAAI;EACd,WAAW,IAAI;EACf,cAAc,IAAI;CACpB;AACF;AAEA,SAAgB,WAAW,KAAU,IAAiC;CACpE,MAAM,MAAM,IAAI,GAAG,QAAQ,UAAU,QAAQ,4BAA4B,CAAC,CAAC,IAAI,EAAE;CAGjF,OAAO,OAAO,UAAU,GAAG;AAC7B;AAEA,SAAgB,eAAe,KAAU,IAAqB;CAC5D,MAAM,UAAU,WAAW,KAAK,EAAE;CAClC,IAAI,CAAC,SAAS,MAAM,IAAI,YAAY,qBAAqB,mBAAmB,IAAI;CAChF,OAAO;AACT;AAEA,SAAgB,cAAc,KAAU,IAAY,KAAc,OAAwB;CACxF,MAAM,WAAW,WAAW,KAAK,EAAE;CACnC,IAAI,UAAU,OAAO;CACrB,MAAM,KAAK,OAAO,GAAG;CACrB,IAAI,GACD,QACC,8GACF,CAAC,CACA,IAAI,IAAI,OAAO,MAAM,QAAQ,YAAY,OAAO,GAAG,IAAI,MAAM,IAAI,EAAE;CACtE,OAAO,eAAe,KAAK,EAAE;AAC/B;AAEA,SAAS,eAAe,KAAU,IAAY,OAAwB;CACpE,MAAM,QAAQ,cAAc,KAAK,KAAK;CACtC,OAAO,CAAC,SAAS,MAAM,OAAO;AAChC;AAEA,SAAgB,WAAW,KAAU,IAAY,UAAuC;CACtF,MAAM,UAAU,eAAe,KAAK,EAAE;CACtC,MAAM,QAAQ,UAAU,KAAK,KAAK;CAClC,IAAI,UAAU,QAAQ,UAAU,QAAQ,QAAQ,CAAC,eAAe,KAAK,IAAI,KAAK,GAAG,OAAO;CACxF,IAAI,GACD,QACC;;oBAGF,CAAC,CACA,IAAI,OAAO,EAAE;CAChB,OAAO,eAAe,KAAK,EAAE;AAC/B;AAEA,SAAgB,eACd,KACA,OACA,SACA,MAAe,CAAC,GACP;CACT,MAAM,KAAK,OAAO,GAAG;CACrB,MAAM,OAAO,IAAI,QAAA;CACjB,MAAM,UAAU,IAAI,aAAa;CACjC,MAAM,OAAO,YAAY,OAAO,QAAQ,GAAG;CAC3C,MAAM,WAAW,WAAW,KAAK,QAAQ,UAAU;CACnD,IAAI,CAAC,UACH,IAAI,GACD,QACC;sDAEF,CAAC,CACA,IAAI,QAAQ,YAAY,QAAQ,OAAO,MAAM,MAAM,MAAM,SAAS,IAAI,IAAI,EAAE;MAE/E,IAAI,GACD,QACC;;sBAGF,CAAC,CACA,IACC,IACA,IACA,QAAQ,OAAO,SAAS,KACxB,QAAQ,SAAS,MACjB,MACA,WAAW,SAAS,SACpB,QAAQ,UACV;CAEJ,OAAO,WAAW,KAAK,QAAQ,YAAY,QAAQ,aAAa;AAClE;AAEA,SAAgB,aAAa,KAAU,IAAkB;CACvD,IAAI,GAAG,QAAQ,qDAAqD,CAAC,CAAC,IAAI,OAAO,GAAG,GAAG,EAAE;AAC3F;AAEA,SAAgB,YAAY,KAAU,IAAY,MAAc,SAA2B;CACzF,eAAe,KAAK,EAAE;CACtB,IAAI;EACF,IAAI,GACD,QACC;;;sBAIF,CAAC,CACA,IAAI,MAAM,WAAW,MAAM,OAAO,GAAG,GAAG,EAAE;CAC/C,SAAS,OAAO;EACd,IAAI,iBAAiB,SAAS,MAAM,QAAQ,SAAS,QAAQ,GAC3D,MAAM,IAAI,YAAY,sBAAsB,oBAAoB,KAAK,iBAAiB;EAExF,MAAM;CACR;CACA,OAAO,eAAe,KAAK,EAAE;AAC/B;AAEA,SAAgB,aAAa,KAAU,UAA+B,CAAC,GAAc;CACnF,MAAM,WAA4B,CAAC;CACnC,IAAI,CAAC,QAAQ,kBAAkB,SAAS,KAAK,WAAW;CACxD,IAAI,CAAC,QAAQ,gBAAgB,SAAS,KAAK,SAAS;CACpD,MAAM,eAAe,SAAS,UAAU,GAAG,CAAC,CAAC,KAAK,IAAI;CACtD,MAAM,QAAQ,SAAS,SAAS,wBAAwB,aAAa,KAAK;CAI1E,OAHa,IAAI,GACd,QAAQ,UAAU,QAAQ,iBAAiB,MAAM,8BAA8B,CAAC,CAChF,IAAI,GAAG,QACA,CAAC,CAAC,IAAI,SAAS;AAC3B;AAEA,SAAgB,YAAY,KAAU,IAAqB;CACzD,eAAe,KAAK,EAAE;CACtB,MAAM,KAAK,OAAO,GAAG;CACrB,IAAI,GACD,QACC,8FACF,CAAC,CACA,IAAI,IAAI,EAAE;CACb,IAAI,GACD,QAAQ,yEAAyE,CAAC,CAClF,IAAI,IAAI,EAAE;CACb,OAAO,eAAe,KAAK,EAAE;AAC/B;AAEA,SAAgB,eAAe,KAA+B;CAC5D,MAAM,MAAM,IAAI,GACb,QACC,UAAU,QAAQ,8EACpB,CAAC,CACA,IAAI;CACP,OAAO,OAAO,UAAU,GAAG;AAC7B;AAEA,SAAgB,cAAc,KAAU,MAAmC;CACzE,MAAM,MAAM,IAAI,GAAG,QAAQ,UAAU,QAAQ,8BAA8B,CAAC,CAAC,IAAI,IAAI;CAGrF,OAAO,OAAO,UAAU,GAAG;AAC7B;AAEA,MAAa,oBAAoB;AAEjC,SAAgB,QAAQ,IAAoB;CAC1C,OAAO,GAAG,MAAM,GAAG,CAAC;AACtB;AAEA,SAAgB,YAAY,KAAU,WAA4B;CAChE,MAAM,SAAS,cAAc,KAAK,SAAS;CAC3C,IAAI,QAAQ,OAAO;CACnB,MAAM,OAAO,WAAW,KAAK,SAAS;CACtC,IAAI,MAAM,OAAO;CACjB,MAAM,aAAa,gBAAgB,KAAK,SAAS;CACjD,IAAI,cAAc,UAAU,UAAA,GAA6B;EACvD,MAAM,OAAO,IAAI,GACd,QAAQ,UAAU,QAAQ,4DAA4D,CAAC,CACvF,IAAI,GAAG,UAAU,YAAY,EAAE,EAAE;EACpC,MAAM,CAAC,MAAM,GAAG,UAAU;EAC1B,IAAI,QAAQ,OAAO,WAAW,GAAG,OAAO,UAAU,IAAI;EACtD,IAAI,MAEF,MAAM,IAAI,YACR,qBACA,eAAe,UAAU,8BAHX,KAAK,KAAK,QAAQ,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,IAGC,GACzD;CAEJ;CAKA,MAAM,IAAI,YAAY,qBAAqB,oBAAoB,UAAU,GAHvE,cAAc,UAAU,SAAA,IACpB,gDACA,IAC4E;AACpF;AAEA,SAAgB,qBACd,KACA,MACA,WACqB;CACrB,MAAM,MAAM,IAAI,GACb,QACC,UAAU,QAAQ;;uCAGpB,CAAC,CACA,IAAI,MAAM,SAAS;CACtB,OAAO,OAAO,UAAU,GAAG;AAC7B;;ACzPA,SAAS,QAAQ,KAAU,SAA2B;CAMpD,OALa,IAAI,GACd,QAAQ,2CAA2C,CAAC,CACpD,IAAI,OAGG,CAAC,CAAC,KAAK,QAAQ,IAAI,IAAI;AACnC;AAEA,SAAgB,cAAc,KAAU,QAAgB,QAA6B;CACnF,IAAI,WAAW,QACb,MAAM,IAAI,YAAY,yBAAyB,oCAAoC;CAErF,MAAM,OAAO,eAAe,KAAK,MAAM;CACvC,MAAM,OAAO,eAAe,KAAK,MAAM;CACvC,MAAM,KAAK,OAAO,GAAG;CACrB,MAAM,QAAQ,IAAI,IAAI,QAAQ,KAAK,MAAM,CAAC;CAC1C,MAAM,YAAY,QAAQ,KAAK,MAAM;CACrC,MAAM,cAAc,UAAU,QAAQ,SAAS,CAAC,MAAM,IAAI,IAAI,CAAC;CAC/D,MAAM,eAAe,UAAU,QAAQ,SAAS,MAAM,IAAI,IAAI,CAAC;CAC/D,IAAI,GAAG,KAAK,OAAO;CACnB,IAAI;EACF,MAAM,YAAY,IAAI,GAAG,QACvB,8EACF;EACA,KAAK,MAAM,QAAQ,aAAa,UAAU,IAAI,QAAQ,IAAI,QAAQ,IAAI;EACtE,IAAI,GACD,QACC,8FACF,CAAC,CACA,IAAI,IAAI,MAAM;EACjB,MAAM,cAAc,OAClB,IAAI,GAAG,QAAQ,iDAAiD,CAAC,CAAC,IAAI,QAAQ,MAAM,CAAC,CAAC,OACxF;EAGA,MAAM,aACJ,EAFA,IAAI,GAAG,QAAQ,6CAA6C,CAAC,CAAC,IAAI,MAAM,MAAM,KAAA,MAG9E,OACE,IAAI,GAAG,QAAQ,sDAAsD,CAAC,CAAC,IAAI,QAAQ,MAAM,CAAC,CACvF,OACL,IAAI;EACN,IAAI,GAAG,QAAQ,2CAA2C,CAAC,CAAC,IAAI,MAAM;EACtE,MAAM,WAAW,KAAK,SAAS,QAAQ,KAAK,SAAS;EACrD,IAAI,GACD,QACC,sFACF,CAAC,CACA,IAAI,IAAI,MAAM;EACjB,IAAI,GACD,QACC;;;sBAIF,CAAC,CACA,IAAI,WAAW,KAAK,OAAO,MAAM,KAAK,SAAS,IAAI,WAAW,IAAI,GAAG,MAAM;EAC9E,IAAI,GAAG,KAAK,QAAQ;EACpB,OAAO;GACL,SAAS,eAAe,KAAK,MAAM;GACnC;GACA;GACA;GACA;EACF;CACF,SAAS,OAAO;EACd,IAAI,GAAG,KAAK,UAAU;EACtB,MAAM;CACR;AACF;;AC/EA,MAAa,qBAAqB;AAClC,MAAM,mBAAmB;AAEzB,SAAgB,UAAU,OAAmC;CAC3D,MAAM,OAAO,OAAO,KAAK;CACzB,OAAO,OAAO,SAAS,IAAI,KAAK,OAAO,IAAI,OAAA;AAC7C;AAIA,MAAM,YAAY;;;;;;;AAQlB,SAAgB,cAAc,KAAU,QAAgB,MAAwB;CAC9E,MAAM,0BAAS,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,QAAQ,IAAI,OAAO,gBAAgB,EAAA,CAAE,YAAY;CACnF,IAAI,GAAG,KAAK,OAAO;CACnB,IAAI;EAIF,MAAM,MAHO,IAAI,GACd,QAAQ,iCAAiC,WAAW,CAAC,CACrD,IAAI,QAAQ,MACA,CAAC,CAAC,KAAK,QAAQ,IAAI,EAAE;EACpC,KAAK,MAAM,SAAS,CAAC,eAAe,QAAQ,GAAG;GAC7C,MAAM,YAAY,IAAI,GAAG,QAAQ,eAAe,MAAM,mBAAmB;GACzE,KAAK,MAAM,MAAM,KAAK,UAAU,IAAI,EAAE;EACxC;EACA,MAAM,gBAAgB,IAAI,GAAG,QAAQ,mCAAmC;EACxE,KAAK,MAAM,MAAM,KAAK,cAAc,IAAI,EAAE;EAC1C,IAAI,GAAG,KAAK,QAAQ;EACpB,OAAO;CACT,SAAS,OAAO;EACd,IAAI,GAAG,KAAK,UAAU;EACtB,MAAM;CACR;AACF;;ACpCA,MAAa,sBAAsB;AAEnC,SAAgB,gBAAgB,MAAyB;CACvD,IAAI,KAAK,aAAa,OAAO,GAAG,mBAAmB,GAAG,KAAK,KAAK,GAAG;CACnE,OAAO,GAAG,MAAM,KAAK,UAAU,cAAc,GAAG,KAAK;AACvD;AAEA,SAAgB,gBAAgB,MAA0B;CACxD,OAAO,KAAK,OAAO,GAAG,KAAK,MAAM,GAAG,KAAK,KAAK,KAAK,KAAK,SAAS,GAAG,KAAK,MAAM,GAAG,KAAK;AACzF;AAEA,SAAgB,eAAe,MAA0B;CACvD,MAAM,MAAgB,CAAC;CACvB,IAAI,KAAK,OACP,IAAI,KAAK,gBAAgB,KAAK,gBAAgB,KAAK,KAAK,KAAK,EAAE;CAEjE,IAAI,KAAK,gBAAgB;CACzB,IAAI,KAAK,QAAQ,WAAW,GAAG,IAAI,KAAK,WAAW;CACnD,KAAK,QAAQ,SAAS,MAAM,UAAU;EACpC,IAAI,KAAK,KAAK,QAAQ,EAAE,IAAI,gBAAgB,IAAI,GAAG;CACrD,CAAC;CACD,IAAI,KAAK,IAAI,SAAS;CACtB,IAAI,KAAK,QAAQ,WAAW,GAAG,IAAI,KAAK,WAAW;CACnD,KAAK,MAAM,QAAQ,KAAK,SAAS,IAAI,KAAK,KAAK,gBAAgB,IAAI,GAAG;CACtE,OAAO,IAAI,KAAK,IAAI;AACtB;AAEA,SAAgB,aAAa,MAA0B;CACrD,OAAO;EAAC,KAAK;EAAM;EAAI,eAAe,IAAI;EAAG;EAAI;CAAM,CAAC,CAAC,KAAK,IAAI;AACpE;;ACNA,SAAS,OAAO,KAA0B;CACxC,OAAO;EACL,SAAS,IAAI;EACb,MAAM,IAAI;EACV,QAAQ,IAAI;EACZ,aAAa,IAAI,iBAAiB;EAClC,WAAW,IAAI;CACjB;AACF;AAEA,SAAgB,SAAS,KAAU,SAAwC;CACzE,MAAM,MAAM,IAAI,GAAG,QAAQ,6CAA6C,CAAC,CAAC,IAAI,OAAO;CAGrF,OAAO,OAAO,OAAO,GAAG;AAC1B;AAEA,SAAgB,SAAS,KAAU,SAAiB,OAAiC;CACnF,IAAI,MAAM,eAAe,MAAM,QAC7B,MAAM,IAAI,YACR,kCACA,0DACF;CAEF,IAAI,MAAM,WAAW,KAAA,KAAa,CAAC,mBAAmB,MAAM,MAAM,GAChE,MAAM,IAAI,YAAY,kBAAkB,yBAAyB,OAAO,MAAM,MAAM,EAAE,EAAE;CAE1F,IAAI,GACD,QACC;;gFAGF,CAAC,CACA,IACC,SACA,aAAa,MAAM,IAAI,GACvB,MAAM,UAAU,MAChB,MAAM,cAAc,IAAI,GACxB,OAAO,GAAG,CACZ;CACF,OAAO,SAAS,KAAK,OAAO;AAC9B;AAEA,SAAgB,WAAW,KAAU,SAAuB;CAC1D,IAAI,GAAG,QAAQ,2CAA2C,CAAC,CAAC,IAAI,OAAO;AACzE;;ACnDA,MAAM,SAA+C;CAAE,QAAQ;CAAG,QAAQ;AAAE;AAE5E,SAAS,SAAS,QAA6B;CAC7C,OAAO,OAAO,WAAW;AAC3B;AAEA,SAAS,eAAe,MAAa,OAAsB;CACzD,IAAI,KAAK,eAAe,QAAQ,MAAM,eAAe,MAAM;EACzD,IAAI,KAAK,eAAe,MAAM,OAAO;EACrC,IAAI,MAAM,eAAe,MAAM,OAAO;EACtC,OAAO,KAAK,aAAa,MAAM;CACjC;CACA,OACE,SAAS,KAAK,MAAM,IAAI,SAAS,MAAM,MAAM,KAAK,KAAK,UAAU,cAAc,MAAM,SAAS;AAElG;AAEA,SAAS,OAAO,OAA0B;CACxC,OAAO;EACL,MAAM,MAAM;EACZ,QAAQ,MAAM;EACd,OAAO,MAAM,WAAW,WAAW,KAAK,MAAM,MAAM;EACpD,MAAM,MAAM;CACd;AACF;AAEA,SAAgB,YAAY,MAAoB;CAG9C,OAAO,GAFO,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,GAAG,GAEpC,EAAE,GADA,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,SAAS,GAAG,GAC7B;AAC3B;AAEA,SAAgB,gBAAgB,KAAU,SAA8B;CACtE,MAAM,SAAS,WAAW,KAAK,QAAQ,EAAE;CACzC,MAAM,UAAU,OACb,QAAQ,UAAU,mBAAmB,MAAM,MAAM,CAAC,CAAC,CACnD,KAAK,cAAc,CAAC,CACpB,IAAI,MAAM;CACb,MAAM,UAAU,OAAO,QAAQ,UAAU,MAAM,WAAW,SAAS,CAAC,CAAC,IAAI,MAAM;CAC/E,OAAO;EACL,MAAM,YAAY,IAAI,IAAI,CAAC;EAC3B,aAAa,QAAQ;EACrB,OAAO,SAAS,KAAK,QAAQ,EAAE,KAAK;EACpC;EACA;CACF;AACF;;AC/DA,MAAa,8BAA8B;AAC3C,MAAa,eAAe;AAE5B,SAAS,cAAc,KAAU,SAAiB,aAAwC;CACxF,MAAM,MAAM,IAAI,GACb,QAAQ,8DAA8D,CAAC,CACvE,IAAI,SAAS,WAAW;CAC3B,OAAO,OAAO,QAAQ,GAAG;AAC3B;AAGA,SAAgB,aAAa,KAAU,SAAiB,aAAwC;CAC9F,MAAM,SAAS,cAAc,KAAK,SAAS,WAAW;CACtD,IAAI,QAAQ,OAAO;CACnB,MAAM,yBAAQ,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,QAAQ,IAAI,2BAA2B,EAAA,CAAE,YAAY;CACtF,MAAM,MAAM,IAAI,GACb,QACC;;kDAGF,CAAC,CACA,IAAI,SAAS,KAAK;CACrB,IAAI,CAAC,KAAK,OAAO,KAAA;CACjB,IAAI,GACD,QAAQ,oEAAoE,CAAC,CAC7E,IAAI,aAAa,SAAS,IAAI,IAAI;CACrC,OAAO,cAAc,KAAK,SAAS,WAAW;AAChD;AAEA,SAAgB,aAAa,KAAU,SAAiB,aAAwC;CAC9F,MAAM,QAAQ,cAAc,KAAK,SAAS,WAAW;CACrD,IAAI,CAAC,SAAS,MAAM,WAAW,WAAW,OAAO,KAAA;CACjD,IAAI,MAAM,eAAe;EACvB,eAAe,KAAK,SAAS,MAAM,IAAI;EACvC,OAAO,cAAc,KAAK,SAAS,WAAW;CAChD;CACA,IAAI,GACD,QACC,8FACF,CAAC,CACA,IAAI,cAAc,OAAO,GAAG,GAAG,SAAS,MAAM,IAAI;CACrD,SAAS,KAAK,SAAS;EAAE,MAAM;EAAY,OAAO,MAAM;EAAM,MAAM;CAAa,CAAC;CAClF,OAAO,cAAc,KAAK,SAAS,WAAW;AAChD;;ACCA,IAAa,SAAb,MAAoB;CAKP;CAJX;CACA;CAEA,YACE,OACA,IACA,UAAyB,CAAC,GAC1B;EAHS,KAAA,QAAA;EAIT,KAAK,KAAK;EACV,KAAK,MAAM;GAAE;GAAI,KAAK,QAAQ,8BAAc,IAAI,KAAK;EAAG;CAC1D;CAEA,WAAoB;EAClB,MAAM,OAAe,WAAW,KAAK,KAAK,EAAE;EAC5C,OAAO,cAAsB,YAAY,KAAK,KAAK,SAAS;EAC5D,UAAU,OAAe,eAAe,KAAK,KAAK,EAAE;EACpD,SAAS,IAAY,QAAiB,cAAc,KAAK,KAAK,IAAI,KAAK,KAAK,KAAK;EACjF,iBAAiB,SAA6B,QAC5C,eAAe,KAAK,KAAK,KAAK,OAAO,SAAS,GAAG;EACnD,QAAQ,OAAe,aAAa,KAAK,KAAK,EAAE;EAChD,QAAQ,QAAgB,WAAmB,cAAc,KAAK,KAAK,QAAQ,MAAM;EACjF,QAAQ,QAAgB,SAAiB,cAAc,KAAK,KAAK,QAAQ,IAAI;EAC7E,OAAO,IAAY,MAAc,YAAqB,YAAY,KAAK,KAAK,IAAI,MAAM,OAAO;EAC7F,aAAa,IAAY,UAA8B,WAAW,KAAK,KAAK,IAAI,KAAK;EACrF,OAAO,YAAkC,aAAa,KAAK,KAAK,OAAO;EACvE,OAAO,OAAe,YAAY,KAAK,KAAK,EAAE;EAC9C,eAAe,eAAe,KAAK,GAAG;EACtC,SAAS,SAAiB,cAAc,KAAK,KAAK,IAAI;EACtD,gBAAgB,MAAc,cAC5B,qBAAqB,KAAK,KAAK,MAAM,SAAS;CAClD;CAEA,SAAkB;EAChB,MAAM,SAAiB,SAAiB,SAAS,KAAK,KAAK,SAAS,IAAI;EACxE,QAAQ,SAAiB,UAA2B,WAAW,KAAK,KAAK,SAAS,KAAK;EACvF,SAAS,SAAiB,MAAc,UACtC,YAAY,KAAK,KAAK,SAAS,MAAM,KAAK;EAC5C,SAAS,SAAiB,MAAc,UACtC,YAAY,KAAK,KAAK,SAAS,MAAM,KAAK;EAC5C,OAAO,SAAiB,MAAc,WACpC,UAAU,KAAK,KAAK,SAAS,MAAM,MAAM;EAC3C,OAAO,SAAiB,YAAgC,WAAW,KAAK,KAAK,SAAS,OAAO;EAC7F,eAAe,SAAiB,UAAoB,aAAa,KAAK,KAAK,SAAS,KAAK;EACzF,eAAe,SAAiB,gBAC9B,aAAa,KAAK,KAAK,SAAS,WAAW;EAC7C,eAAe,SAAiB,gBAC9B,aAAa,KAAK,KAAK,SAAS,WAAW;CAC/C;CAEA,QAAiB;EACf,MAAM,YAAoB,SAAS,KAAK,KAAK,OAAO;EACpD,MAAM,SAAiB,UAAyB,SAAS,KAAK,KAAK,SAAS,KAAK;EACjF,QAAQ,YAAoB,WAAW,KAAK,KAAK,OAAO;CAC1D;CAEA,SAAkB;EAChB,MAAM,SAAiB,UAAyB,SAAS,KAAK,KAAK,SAAS,KAAK;EACjF,OAAO,SAAiB,UAAmB,WAAW,KAAK,KAAK;GAAE;GAAS;EAAM,CAAC;CACpF;CAEA,SAAkB;EAChB,OAAO,YAAqB,gBAAgB,KAAK,KAAK,OAAO;EAC7D,SAAS,YAAqB,aAAa,gBAAgB,KAAK,KAAK,OAAO,CAAC;EAC7E,WAAW,YAAqB,eAAe,gBAAgB,KAAK,KAAK,OAAO,CAAC;CACnF;CAEA,QAAc;EACZ,KAAK,GAAG,MAAM;CAChB;AACF;AAMA,SAAgB,WAAW,UAA6B,CAAC,GAAW;CAClE,MAAM,QAAQ,YAAY,OAAO;CACjC,OAAO,IAAI,OAAO,OAAO,aAAa,QAAQ,UAAU,MAAM,MAAM,GAAG,OAAO;AAChF;;AC1HA,MAAa,gBAAgB;AAC7B,MAAa,wBAAwB;AAOrC,SAAgB,gBAAgB,OAA4B;CAC1D,OAAO;EACL,YAAY,IAAI,KAAK,MAAM,EAAE,CAAC;EAC9B,MAAM;EACN,MAAM;EACN,MAAM,QAAQ,MAAM;EACpB,MAAM,QAAQ,MAAM,GAAG,CAAC;CAC1B,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,IAAI;AACd;AAEA,SAAgB,aAAa,SAA0B;CACrD,MAAM,OAAO,QAAQ,QAAQ,QAAQ;CACrC,OAAO,QAAQ,UAAU,GAAG,KAAK,IAAI,QAAQ,QAAQ,KAAK;AAC5D;AAEA,SAAgB,cACd,QACA,SACA,EAAE,SAAA,IAAwB,gBAAgB,0BAAgD,CAAC,GACnF;CACR,MAAM,OAAO,kBAAkB,aAAa,OAAO,EAAE,2BAA2B,OAAO,OAAO,OAAO,OAAO;CAC5G,MAAM,QAAQ,OAAO,OAAO,KAAK,QAAQ,IAAI,MAAM,CAAC,CAAC,IAAI,eAAe;CACxE,MAAM,cACJ,MAAM,SAAS,GAAG,KAAK,mBAAmB,MAAM,KAAK,SAAS,KAAK,MAAM,CAAC,CAAC,KAAK,IAAI,MAAM;CAC5F,IAAI,OAAO,MAAM;CAEjB,OAAO,KAAK,SAAS,iBAAiB,MAAM,SAAS,GAAG;EACtD,MAAM,MAAM;EACZ,OAAO,MAAM;CACf;CACA,OAAO,KAAK,SAAS,gBAAgB,GAAG,KAAK,MAAM,GAAG,gBAAgB,CAAC,EAAE,KAAK;AAChF"}
package/package.json CHANGED
@@ -1,6 +1,57 @@
1
1
  {
2
2
  "name": "@rucher/carnet",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "Shared state for Rucher: sessions, agents, status and journal in SQLite",
5
+ "keywords": [
6
+ "agents",
7
+ "claude-code",
8
+ "mcp",
9
+ "multi-agent",
10
+ "sqlite"
11
+ ],
12
+ "homepage": "https://github.com/Florian-Varrin/Rucher/tree/master/packages/carnet#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/Florian-Varrin/Rucher/issues"
15
+ },
16
+ "license": "MIT",
17
+ "author": "Florian Varrin",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/Florian-Varrin/Rucher.git",
21
+ "directory": "packages/carnet"
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "README.md",
26
+ "LICENSE"
27
+ ],
28
+ "type": "module",
29
+ "sideEffects": false,
30
+ "exports": {
31
+ ".": "./dist/index.mjs",
32
+ "./mcp": "./dist/mcp.mjs",
33
+ "./package.json": "./package.json"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "dependencies": {
39
+ "@modelcontextprotocol/server": "^2.4.0",
40
+ "zod": "^4.2.0"
41
+ },
42
+ "devDependencies": {
43
+ "@modelcontextprotocol/client": "^2.4.0",
44
+ "@types/node": "^24",
45
+ "typescript": "^7.0.2",
46
+ "vite-plus": "1.4.0"
47
+ },
48
+ "engines": {
49
+ "node": ">=24"
50
+ },
51
+ "scripts": {
52
+ "build": "vp pack",
53
+ "dev": "vp pack --watch",
54
+ "test": "vp test",
55
+ "check": "vp check"
56
+ }
6
57
  }