@dylanrussell/agent-router 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +211 -0
- package/assets/logo.svg +45 -0
- package/dist/cli.d.ts +23 -0
- package/dist/cli.js +1259 -0
- package/dist/cli.js.map +1 -0
- package/dist/plugin.d.ts +30 -0
- package/dist/plugin.js +787 -0
- package/dist/plugin.js.map +1 -0
- package/dist/tui.d.ts +99 -0
- package/dist/tui.js +1048 -0
- package/dist/tui.js.map +1 -0
- package/package.json +95 -0
package/dist/tui.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/config.ts","../src/core/errors.ts","../src/core/paths.ts","../src/core/schema.ts","../src/core/atomic-write.ts","../src/core/stack-manager.ts","../src/core/frontmatter.ts","../src/core/history.ts","../src/core/backups.ts","../src/core/state.ts","../src/core/validator.ts","../src/tui/actions.ts","../src/tui/dialogs.ts","../src/tui/render.ts","../src/tui/store.ts","../src/tui/view.ts","../src/tui/index.ts"],"sourcesContent":["/**\n * Read `${routerHome}/config.json` — agent-router's own settings file.\n *\n * Why a file when env vars already exist? `agentsDir`/`stacksDir` must agree\n * across two execution contexts: the CLI (your shell) and the plugin (inside\n * opencode). An env var has to be exported in BOTH or the contexts disagree\n * and drift returns. A file in a fixed location is read identically by both,\n * so a single declaration wins everywhere.\n *\n * Precedence (highest first): explicit `resolvePaths` option → this file →\n * env var → built-in default. The file beats env because it is the more\n * deliberate, version-controllable declaration.\n *\n * The schema is strict (fail closed) — this file is ours, like state.json.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport { IOError, ValidationError } from \"./errors.js\";\nimport { type ResolvePathsOptions, type RouterPaths, resolvePaths } from \"./paths.js\";\nimport { type ConfigFile, ConfigFileSchema } from \"./schema.js\";\n\nexport const CONFIG_FILE_NAME = \"config.json\";\n\n/** Expand a leading `~` or `~/` to the user's home directory. */\nexport function expandHome(p: string): string {\n if (p === \"~\") return homedir();\n if (p.startsWith(\"~/\")) return path.join(homedir(), p.slice(2));\n return p;\n}\n\n/** Read `${routerHome}/config.json`. Returns null if absent. Throws on parse/schema error. */\nexport async function readConfigFile(routerHome: string): Promise<ConfigFile | null> {\n const configPath = path.join(routerHome, CONFIG_FILE_NAME);\n if (!existsSync(configPath)) return null;\n\n let raw: string;\n try {\n raw = await readFile(configPath, \"utf8\");\n } catch (cause) {\n throw new IOError(`Failed to read ${configPath}: ${(cause as Error).message}`, cause);\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (cause) {\n throw new ValidationError(\n `agent-router config.json is not valid JSON: ${(cause as Error).message}`,\n configPath,\n );\n }\n\n const result = ConfigFileSchema.safeParse(parsed);\n if (!result.success) {\n throw new ValidationError(\n `agent-router config.json failed validation: ${result.error.issues.map((i) => i.message).join(\"; \")}`,\n configPath,\n result.error.issues.map((i) => `${i.path.join(\".\")}: ${i.message}`),\n );\n }\n return result.data;\n}\n\n/** Expand `~` and anchor relative paths at `routerHome`. */\nfunction normalizeConfigPath(routerHome: string, p: string): string {\n const expanded = expandHome(p);\n return path.isAbsolute(expanded) ? expanded : path.resolve(routerHome, expanded);\n}\n\n/**\n * Like `resolvePaths`, but first reads `config.json` so its `agentsDir` /\n * `stacksDir` take effect. This is what the CLI and plugin call —\n * `resolvePaths` stays a pure, I/O-free function for tests and callers that\n * supply their own paths.\n *\n * Explicit `options.agentsDir` / `options.stacksDir` still win over the file.\n */\nexport async function resolvePathsWithConfig(\n options: ResolvePathsOptions = {},\n): Promise<RouterPaths> {\n const base = resolvePaths(options);\n\n const config = await readConfigFile(base.routerHome);\n if (!config) return base;\n\n const next: ResolvePathsOptions = { ...options };\n if (options.agentsDir === undefined && config.agentsDir) {\n (next as { agentsDir?: string }).agentsDir = normalizeConfigPath(\n base.routerHome,\n config.agentsDir,\n );\n }\n if (options.stacksDir === undefined && config.stacksDir) {\n (next as { stacksDir?: string }).stacksDir = normalizeConfigPath(\n base.routerHome,\n config.stacksDir,\n );\n }\n return resolvePaths(next);\n}\n","/**\n * Typed errors used across the agent-router core.\n *\n * Why typed errors instead of plain `Error`? The CLI maps each subclass to a\n * specific exit code (see `src/cli.ts`); the plugin tools map them to\n * structured tool-error responses opencode can render. Untyped Errors would\n * force string-matching at the boundary, which is brittle.\n *\n * All errors below carry an `exitCode` static so the CLI can do\n * `if (err instanceof RouterError) process.exit(err.constructor.exitCode)`.\n */\n\nexport abstract class RouterError extends Error {\n /** CLI exit code for this error class. Subclasses override. */\n static readonly exitCode: number = 1;\n override readonly name: string = \"RouterError\";\n}\n\n/** User passed a stack name that doesn't exist on disk. */\nexport class StackNotFoundError extends RouterError {\n static override readonly exitCode = 2;\n override readonly name = \"StackNotFoundError\";\n constructor(\n public readonly stackName: string,\n public readonly available: ReadonlyArray<string>,\n ) {\n super(\n `Stack \"${stackName}\" not found. Available: ${available.length ? available.join(\", \") : \"(none)\"}.`,\n );\n }\n}\n\n/**\n * An agent `.md` file a stack references is missing, or exists but has no\n * frontmatter `model:` line to rewrite. `apply` is strict: it fails before\n * writing anything rather than leaving the suite half-switched.\n */\nexport class AgentFileError extends RouterError {\n static override readonly exitCode = 2;\n override readonly name = \"AgentFileError\";\n constructor(\n public readonly agentName: string,\n public readonly filePath: string,\n reason: string,\n ) {\n super(`Agent \"${agentName}\" (${filePath}): ${reason}`);\n }\n}\n\n/** state.json missing or corrupt and the operation needs it. */\nexport class NoActiveStackError extends RouterError {\n static override readonly exitCode = 1;\n override readonly name = \"NoActiveStackError\";\n constructor(message = \"No active stack. Run `agent-router init` first.\") {\n super(message);\n }\n}\n\n/** A JSON file on disk failed schema validation. */\nexport class ValidationError extends RouterError {\n static override readonly exitCode = 4;\n override readonly name = \"ValidationError\";\n constructor(\n message: string,\n public readonly path?: string,\n public readonly issues?: ReadonlyArray<string>,\n ) {\n super(message);\n }\n}\n\n/** Model IDs in a stack are not reachable through current opencode auth. */\nexport class ModelValidationError extends RouterError {\n static override readonly exitCode = 4;\n override readonly name = \"ModelValidationError\";\n constructor(\n public readonly stackName: string,\n public readonly missing: ReadonlyArray<{ path: string; modelId: string }>,\n ) {\n super(\n `Stack \"${stackName}\" references ${missing.length} unreachable model ID${missing.length === 1 ? \"\" : \"s\"}.`,\n );\n }\n}\n\n/** Filesystem op failed (read/write/permission/etc.). */\nexport class IOError extends RouterError {\n static override readonly exitCode = 3;\n override readonly name = \"IOError\";\n readonly causedBy: unknown;\n constructor(message: string, causedBy?: unknown) {\n super(message);\n this.causedBy = causedBy;\n }\n}\n\n/** User passed bad arguments or attempted a refused operation (e.g. rm active without --force). */\nexport class UserError extends RouterError {\n static override readonly exitCode = 1;\n override readonly name = \"UserError\";\n}\n","/**\n * Path resolution for agent-router.\n *\n * Three roots matter:\n * 1. `opencodeConfigDir` — where `opencode.json`, `tui.json`, and the\n * native `agents/` directory live.\n * Default: `${XDG_CONFIG_HOME:-~/.config}/opencode`.\n *\n * 2. `agentsDir` — the directory of agent `.md` files whose frontmatter\n * `model:` lines agent-router rewrites.\n * Default: `${opencodeConfigDir}/agents`.\n * Override: `AGENT_ROUTER_AGENTS_DIR` env var, or `agentsDir` in\n * config.json (see config.ts).\n *\n * 3. `routerHome` — machine-local state (state.json, history).\n * Default: `${opencodeConfigDir}/agent-router`.\n * Override: `AGENT_ROUTER_HOME` env var (`OMO_ROUTER_HOME` is honored as\n * a legacy fallback for one release).\n *\n * Stacks are config, history is state — so `stacksDir` is separately\n * overridable (`AGENT_ROUTER_STACKS_DIR` env / `stacksDir` in config.json),\n * letting users keep stacks in a dotfiles-managed location while history\n * stays machine-local. Default: `${routerHome}/stacks`.\n *\n * Tests rely on the env overrides to fully isolate from the user's real\n * config. Never hard-code paths elsewhere — go through `resolvePaths()`.\n */\n\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\n\nexport interface RouterPaths {\n /** Directory housing `opencode.json`, `tui.json`, and `agents/`. */\n readonly opencodeConfigDir: string;\n /** `${opencodeConfigDir}/opencode.json`. */\n readonly opencodeJsonPath: string;\n /**\n * `${opencodeConfigDir}/tui.json` — opencode's TUI config. Since opencode\n * 1.17 the TUI loads its plugins from THIS file's `plugin` array, not from\n * `opencode.json`. `init` patches both so the sidebar half of agent-router\n * loads alongside the server half.\n */\n readonly tuiJsonPath: string;\n /** Directory of agent `.md` files (frontmatter `model:` lines are the live target). */\n readonly agentsDir: string;\n /** Directory `${opencodeConfigDir}/.backups` for installer-style backups. */\n readonly opencodeBackupsDir: string;\n /** Root for agent-router state (overridable via `AGENT_ROUTER_HOME`). */\n readonly routerHome: string;\n /** `${routerHome}/state.json`. */\n readonly statePath: string;\n /** Directory of named stack files (overridable via `AGENT_ROUTER_STACKS_DIR`). */\n readonly stacksDir: string;\n /** `${routerHome}/history` — rolling switch history. */\n readonly historyDir: string;\n}\n\nexport interface ResolvePathsOptions {\n /**\n * Override `opencodeConfigDir`. Used by tests that don't want to touch\n * `~/.config/opencode`.\n */\n readonly opencodeConfigDir?: string;\n /** Direct override for `routerHome`. Wins over both default and env. */\n readonly routerHome?: string;\n /** Direct override for `agentsDir`. Wins over both default and env. */\n readonly agentsDir?: string;\n /** Direct override for `stacksDir`. Wins over both default and env. */\n readonly stacksDir?: string;\n /**\n * Optional environment map for testability. Production callers omit this and\n * we read from `process.env`.\n */\n readonly env?: Readonly<Record<string, string | undefined>>;\n}\n\n/**\n * Compute all paths used by agent-router. Pure — no filesystem I/O.\n *\n * Resolution order (each): explicit option → env var → default.\n * - `routerHome`: `AGENT_ROUTER_HOME` (legacy `OMO_ROUTER_HOME`) → `${opencodeConfigDir}/agent-router`\n * - `agentsDir`: `AGENT_ROUTER_AGENTS_DIR` → `${opencodeConfigDir}/agents`\n * - `stacksDir`: `AGENT_ROUTER_STACKS_DIR` → `${routerHome}/stacks`\n */\nexport function resolvePaths(options: ResolvePathsOptions = {}): RouterPaths {\n const env = options.env ?? process.env;\n\n const opencodeConfigDir =\n options.opencodeConfigDir ??\n path.join(env.XDG_CONFIG_HOME ?? path.join(homedir(), \".config\"), \"opencode\");\n\n const routerHome =\n options.routerHome ??\n env.AGENT_ROUTER_HOME ??\n env.OMO_ROUTER_HOME ??\n path.join(opencodeConfigDir, \"agent-router\");\n\n const agentsDir =\n options.agentsDir ?? env.AGENT_ROUTER_AGENTS_DIR ?? path.join(opencodeConfigDir, \"agents\");\n\n const stacksDir =\n options.stacksDir ?? env.AGENT_ROUTER_STACKS_DIR ?? path.join(routerHome, \"stacks\");\n\n return {\n opencodeConfigDir,\n opencodeJsonPath: path.join(opencodeConfigDir, \"opencode.json\"),\n tuiJsonPath: path.join(opencodeConfigDir, \"tui.json\"),\n agentsDir,\n opencodeBackupsDir: path.join(opencodeConfigDir, \".backups\"),\n routerHome,\n statePath: path.join(routerHome, \"state.json\"),\n stacksDir,\n historyDir: path.join(routerHome, \"history\"),\n };\n}\n","/**\n * Zod schemas for agent-router's own files.\n *\n * Two philosophies:\n *\n * 1. `state.json` and `config.json` are OURS — strict schema, fail closed.\n * We control every byte; if malformed something corrupted it and we\n * should refuse to proceed rather than silently rebuild and lose pointers.\n *\n * 2. `stacks/*.json` are user-authored. We validate only the shape we care\n * about (an `agents` record whose entries carry a `model` string) and\n * pass unknown keys through verbatim so future additions survive\n * round-trips.\n */\n\nimport { z } from \"zod\";\n\n/* ------------------------------------------------------------------------- *\n * state.json *\n * ------------------------------------------------------------------------- */\n\nexport const StateFileSchema = z\n .object({\n version: z.literal(1),\n active: z.string().min(1),\n previousActive: z.string().min(1).nullable(),\n lastSwitchedAt: z.string().min(1),\n })\n .strict();\n\nexport type StateFile = z.infer<typeof StateFileSchema>;\n\n/* ------------------------------------------------------------------------- *\n * stack files *\n * ------------------------------------------------------------------------- */\n\n/**\n * The smallest commitment a stack entry makes: it must have a `model` string.\n * Unknown keys ride along unchanged.\n */\nexport const AgentEntrySchema = z\n .object({\n model: z.string().min(1),\n })\n .passthrough();\n\nexport type AgentEntry = z.infer<typeof AgentEntrySchema>;\n\n/**\n * A stack maps agent names (the `<name>.md` files in the agents dir) to the\n * model each should run on. `apply` rewrites each file's frontmatter `model:`\n * line to match; `capture` builds one of these from the current frontmatter.\n */\nexport const StackFileSchema = z\n .object({\n agents: z.record(z.string(), AgentEntrySchema),\n })\n .passthrough()\n .refine((s) => Object.keys(s.agents).length > 0, {\n message: \"Stack file must map at least one agent in `agents`.\",\n });\n\nexport type StackFile = z.infer<typeof StackFileSchema>;\n\n/* ------------------------------------------------------------------------- *\n * config.json (agent-router's own settings — strict, fail closed) *\n * ------------------------------------------------------------------------- */\n\nexport const ConfigFileSchema = z\n .object({\n /** Where the agent .md files live. Default: `${opencodeConfigDir}/agents`. */\n agentsDir: z.string().min(1).optional(),\n /** Where named stacks live. Default: `${routerHome}/stacks`. */\n stacksDir: z.string().min(1).optional(),\n })\n .strict();\n\nexport type ConfigFile = z.infer<typeof ConfigFileSchema>;\n\n/**\n * Loose schema for `opencode.json` / `tui.json`. We only read/edit `plugin`\n * (array). Everything else is preserved.\n */\nexport const OpencodeJsonSchema = z\n .object({\n plugin: z.array(z.string()).optional(),\n })\n .passthrough();\n\nexport type OpencodeJson = z.infer<typeof OpencodeJsonSchema>;\n","/**\n * Atomic file writes via tmp-then-rename.\n *\n * Why atomic? The user's agent `.md` files may be read concurrently by:\n * - opencode loading agents during startup,\n * - a parallel `agent-router` invocation in another terminal.\n *\n * A non-atomic `writeFile` truncates first, so a crash mid-write leaves an\n * empty / partial file and the next opencode startup blows up. Tmp-then-rename\n * guarantees readers see either the old contents or the new contents — never\n * a half-written file. POSIX rename(2) is atomic on the same filesystem.\n *\n * The tmp file lives next to the destination (same dir = same FS) and has a\n * `.artmp-<pid>-<rand>` suffix so concurrent writers don't collide.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { mkdir, realpath, rename, unlink, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { IOError } from \"./errors.js\";\n\n/**\n * Resolve symlinks before writing. rename(2) onto a symlink replaces the\n * link itself, not its target — which silently destroys dotfile-manager\n * setups (e.g. chezmoi's `~/.config/opencode/tui.json -> ~/.agents/tui.json`).\n * Writing to the resolved real path keeps the link intact and also keeps the\n * tmp file on the same filesystem as the actual destination, which rename's\n * atomicity requires.\n */\nasync function resolveDestination(destPath: string): Promise<string> {\n try {\n return await realpath(destPath);\n } catch {\n try {\n const dir = await realpath(path.dirname(destPath));\n return path.join(dir, path.basename(destPath));\n } catch {\n return destPath;\n }\n }\n}\n\n/**\n * Write `contents` to `destPath` atomically. Creates parent directories if\n * missing. Follows symlinks (writes through to the target). On failure cleans\n * up the tmp file (best-effort).\n *\n * @param destPath Absolute destination path.\n * @param contents UTF-8 string to write. Use `JSON.stringify` for JSON.\n */\nexport async function atomicWriteFile(destPath: string, contents: string): Promise<void> {\n await mkdir(path.dirname(destPath), { recursive: true });\n const dest = await resolveDestination(destPath);\n const dir = path.dirname(dest);\n\n const tmp = path.join(\n dir,\n `.${path.basename(dest)}.artmp-${process.pid}-${randomBytes(4).toString(\"hex\")}`,\n );\n\n try {\n await writeFile(tmp, contents, { encoding: \"utf8\", mode: 0o644 });\n await rename(tmp, dest);\n } catch (cause) {\n await unlink(tmp).catch(() => {\n // Best-effort cleanup. The tmp suffix means leftover files are\n // recognizable (`.artmp-*`) and harmless if we miss one.\n });\n throw new IOError(`Atomic write failed for ${destPath}: ${(cause as Error).message}`, cause);\n }\n}\n\n/**\n * Convenience: stringify with 2-space indent + trailing newline (POSIX\n * text-file convention; also keeps `git diff` clean), then atomic-write.\n */\nexport async function atomicWriteJson(destPath: string, value: unknown): Promise<void> {\n const text = `${JSON.stringify(value, null, 2)}\\n`;\n await atomicWriteFile(destPath, text);\n}\n","/**\n * The orchestration layer. Composes paths + state + history + validator +\n * frontmatter I/O into the operations the CLI and plugin tools call.\n *\n * Public surface:\n * - `listStacks` list available stack names\n * - `readStack` parse a stack file\n * - `getActiveStackName` read state.json\n * - `applyStack` the big one: validate + preflight + history + rewrite frontmatter + update state\n * - `back` compute target from state.previousActive and call applyStack\n * - `captureStack` read current frontmatter models into a new stack\n * - `removeStack` / `importStack` / `exportStack`\n *\n * Every write goes through `atomicWriteFile` / `atomicWriteJson` so concurrent\n * readers see consistent state, and symlinked agent files (dotfile setups)\n * survive intact.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { copyFile, mkdir, readFile, readdir, unlink } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { atomicWriteFile, atomicWriteJson } from \"./atomic-write.js\";\nimport {\n IOError,\n type RouterError,\n StackNotFoundError,\n UserError,\n ValidationError,\n} from \"./errors.js\";\nimport { readAgentFileStrict, readAgentModels, setFrontmatterModel } from \"./frontmatter.js\";\nimport { appendHistory, listHistory, trimHistory } from \"./history.js\";\nimport type { RouterPaths } from \"./paths.js\";\nimport { type StackFile, StackFileSchema } from \"./schema.js\";\nimport { readState, writeState } from \"./state.js\";\nimport { type ValidateOptions, validateStackOrThrow } from \"./validator.js\";\n\n/* ------------------------------------------------------------------------- *\n * read-only helpers *\n * ------------------------------------------------------------------------- */\n\nexport async function listStacks(paths: RouterPaths): Promise<string[]> {\n if (!existsSync(paths.stacksDir)) return [];\n let names: string[];\n try {\n names = await readdir(paths.stacksDir);\n } catch (cause) {\n throw new IOError(`Failed to read stacks dir: ${(cause as Error).message}`, cause);\n }\n return names\n .filter((n) => n.endsWith(\".json\"))\n .map((n) => n.slice(0, -\".json\".length))\n .sort();\n}\n\nexport function stackPath(paths: RouterPaths, name: string): string {\n return path.join(paths.stacksDir, `${name}.json`);\n}\n\n/** Read and validate a stack file. Throws `StackNotFoundError` or `ValidationError`. */\nexport async function readStack(paths: RouterPaths, name: string): Promise<StackFile> {\n const filePath = stackPath(paths, name);\n if (!existsSync(filePath)) {\n throw new StackNotFoundError(name, await listStacks(paths));\n }\n const raw = await readFile(filePath, \"utf8\").catch((cause: Error) => {\n throw new IOError(`Failed to read ${filePath}: ${cause.message}`, cause);\n });\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (cause) {\n throw new ValidationError(\n `Stack \"${name}\" is not valid JSON: ${(cause as Error).message}`,\n filePath,\n );\n }\n const result = StackFileSchema.safeParse(parsed);\n if (!result.success) {\n throw new ValidationError(\n `Stack \"${name}\" failed validation: ${result.error.issues.map((i) => i.message).join(\"; \")}`,\n filePath,\n result.error.issues.map((i) => `${i.path.join(\".\")}: ${i.message}`),\n );\n }\n return result.data;\n}\n\n/** Raw file contents (string) — used when we want to forward bytes verbatim. */\nexport async function readStackRaw(paths: RouterPaths, name: string): Promise<string> {\n const filePath = stackPath(paths, name);\n if (!existsSync(filePath)) {\n throw new StackNotFoundError(name, await listStacks(paths));\n }\n return readFile(filePath, \"utf8\");\n}\n\nexport async function getActiveStackName(paths: RouterPaths): Promise<string | null> {\n const state = await readState(paths.statePath);\n return state?.active ?? null;\n}\n\n/* ------------------------------------------------------------------------- *\n * applyStack — the central operation *\n * ------------------------------------------------------------------------- */\n\nexport interface ApplyOptions {\n /** Default true. Set false to skip the pre-apply model-validation gate. */\n readonly validate?: boolean;\n /** Default false. When true, apply even if validation finds missing models. */\n readonly forceInvalid?: boolean;\n /** Plumbed to validator for tests / mocks. */\n readonly validateOptions?: ValidateOptions;\n}\n\nexport interface ApplyResult {\n readonly previous: string | null;\n readonly current: string;\n /** Agents whose frontmatter model actually changed. */\n readonly changed: ReadonlyArray<string>;\n readonly historyId: string;\n readonly restartRequired: true;\n}\n\n/**\n * Apply stack `name` to the agent files. The algorithm:\n *\n * 1. Read + schema-validate the target stack (throws if missing).\n * 2. Validate the stack's model IDs against `opencode models` (unless\n * --no-validate).\n * 3. STRICT PRE-FLIGHT: read every agent file the stack references. Any\n * missing file or missing `model:` line aborts BEFORE any write — the\n * suite is never left half-switched.\n * 4. Append the displaced mapping (current models of ALL agent files, as a\n * capture-shaped JSON) to history.\n * 5. Atomic-write each agent file whose model differs from the target.\n * 6. Update state.json.\n * 7. Trim history to 20.\n */\nexport async function applyStack(\n paths: RouterPaths,\n name: string,\n options: ApplyOptions = {},\n): Promise<ApplyResult> {\n const validate = options.validate ?? true;\n const forceInvalid = options.forceInvalid ?? false;\n\n const target = await readStack(paths, name);\n\n if (validate && !forceInvalid) {\n await validateStackOrThrow(name, target, options.validateOptions);\n }\n\n const pending: Array<{ agent: string; filePath: string; next: string | null }> = [];\n for (const [agent, entry] of Object.entries(target.agents)) {\n const { filePath, content, model } = await readAgentFileStrict(paths.agentsDir, agent);\n pending.push({\n agent,\n filePath,\n next: model === entry.model ? null : setFrontmatterModel(content, entry.model),\n });\n }\n\n const prevState = await readState(paths.statePath);\n const prevActive = prevState?.active ?? null;\n\n const displaced = { agents: modelsToStackAgents(await readAgentModels(paths.agentsDir)) };\n const historyId = await appendHistory(\n paths.historyDir,\n prevActive ?? \"(none)\",\n name,\n `${JSON.stringify(displaced, null, 2)}\\n`,\n );\n\n const changed: string[] = [];\n for (const p of pending) {\n if (p.next === null) continue;\n await atomicWriteFile(p.filePath, p.next);\n changed.push(p.agent);\n }\n\n await writeState(paths.statePath, {\n version: 1,\n active: name,\n previousActive: prevActive,\n lastSwitchedAt: new Date().toISOString(),\n });\n\n await trimHistory(paths.historyDir).catch(() => {});\n\n return {\n previous: prevActive,\n current: name,\n changed,\n historyId,\n restartRequired: true,\n };\n}\n\nfunction modelsToStackAgents(models: Record<string, string>): Record<string, { model: string }> {\n const out: Record<string, { model: string }> = {};\n for (const k of Object.keys(models).sort()) {\n const model = models[k];\n if (model !== undefined) out[k] = { model };\n }\n return out;\n}\n\n/* ------------------------------------------------------------------------- *\n * back *\n * ------------------------------------------------------------------------- */\n\n/**\n * Undo last N switches (default 1). Implemented in terms of `applyStack` so\n * each step records a fresh history entry — invariants stay consistent.\n *\n * For N=1 we simply target `state.previousActive`. For N>1 we walk history\n * backward to find the n-th previous fromStack (best-effort: history can\n * truncate, in which case we stop at whatever we have).\n */\nexport async function back(\n paths: RouterPaths,\n n = 1,\n options: ApplyOptions = {},\n): Promise<ApplyResult> {\n if (n < 1) throw new UserError(\"`back -n` must be at least 1.\");\n const state = await readState(paths.statePath);\n if (!state || !state.previousActive) {\n throw new UserError(\"No previous stack to revert to.\");\n }\n if (n === 1) return applyStack(paths, state.previousActive, options);\n const entries = await listHistory(paths.historyDir);\n if (entries.length < n) {\n throw new UserError(\n `Cannot go back ${n} steps; only ${entries.length} switch${entries.length === 1 ? \"\" : \"es\"} in history.`,\n );\n }\n // entries[0] is the newest = the snapshot taken right before the LAST switch.\n // entries[n-1].fromStack is the stack that was active before the n-th-most-recent switch.\n const target = entries[n - 1]?.fromStack;\n if (!target || target === \"(none)\") {\n throw new UserError(`Cannot go back ${n} steps to a non-stack target (\"${target}\").`);\n }\n return applyStack(paths, target, options);\n}\n\n/* ------------------------------------------------------------------------- *\n * capture / remove / import / export *\n * ------------------------------------------------------------------------- */\n\nconst STACK_NAME_RE = /^[A-Za-z0-9._-]+$/;\n\nfunction assertValidStackName(name: string): void {\n if (!STACK_NAME_RE.test(name)) {\n throw new UserError(\n `Stack name \"${name}\" contains invalid characters. Allowed: A-Z a-z 0-9 . _ -`,\n );\n }\n}\n\nexport interface CaptureOptions {\n /** When true, overwrite an existing stack of the same name. */\n readonly force?: boolean;\n}\n\nexport interface CaptureResult {\n readonly name: string;\n readonly path: string;\n /** Number of agents captured. */\n readonly agents: number;\n}\n\n/**\n * Snapshot the current frontmatter models of every agent file into a new\n * stack. This replaces both the old snapshot-back concept and seed stacks —\n * your real, working setup is always one `capture` away from being a stack.\n */\nexport async function captureStack(\n paths: RouterPaths,\n name: string,\n options: CaptureOptions = {},\n): Promise<CaptureResult> {\n assertValidStackName(name);\n const dest = stackPath(paths, name);\n if (existsSync(dest) && !options.force) {\n throw new UserError(`Stack \"${name}\" already exists. Use --force to overwrite.`);\n }\n const models = await readAgentModels(paths.agentsDir);\n const agents = modelsToStackAgents(models);\n if (Object.keys(agents).length === 0) {\n throw new UserError(\n `No agent .md files with a frontmatter \\`model:\\` line found in ${paths.agentsDir}.`,\n );\n }\n await mkdir(paths.stacksDir, { recursive: true });\n await atomicWriteJson(dest, { agents });\n return { name, path: dest, agents: Object.keys(agents).length };\n}\n\nexport async function removeStack(\n paths: RouterPaths,\n name: string,\n options: { readonly force?: boolean } = {},\n): Promise<void> {\n const filePath = stackPath(paths, name);\n if (!existsSync(filePath)) {\n throw new StackNotFoundError(name, await listStacks(paths));\n }\n const active = await getActiveStackName(paths);\n if (active === name && !options.force) {\n throw new UserError(`\"${name}\" is the active stack. Use --force to remove anyway.`);\n }\n await unlink(filePath);\n}\n\nexport async function importStack(\n paths: RouterPaths,\n name: string,\n fromFile: string,\n options: { readonly force?: boolean } = {},\n): Promise<void> {\n assertValidStackName(name);\n const dest = stackPath(paths, name);\n if (existsSync(dest) && !options.force) {\n throw new UserError(`Stack \"${name}\" already exists. Use --force to overwrite.`);\n }\n if (!existsSync(fromFile)) {\n throw new UserError(`Import file does not exist: ${fromFile}`);\n }\n await mkdir(paths.stacksDir, { recursive: true });\n await copyFile(fromFile, dest);\n}\n\nexport async function exportStack(paths: RouterPaths, name: string, toFile: string): Promise<void> {\n const filePath = stackPath(paths, name);\n if (!existsSync(filePath)) {\n throw new StackNotFoundError(name, await listStacks(paths));\n }\n await mkdir(path.dirname(toFile), { recursive: true });\n await copyFile(filePath, toFile);\n}\n\n/** Helper used by callers to test for any of our typed errors uniformly. */\nexport function isRouterError(e: unknown): e is RouterError {\n return (\n e instanceof Error &&\n typeof (e as { name?: string }).name === \"string\" &&\n /Error$/.test((e as Error).name)\n );\n}\n","/**\n * Frontmatter `model:` line handling — the read/write layer between stacks\n * and the agent `.md` files opencode loads.\n *\n * Contract (see README): an agent file starts with a YAML frontmatter block\n * delimited by `---` lines, containing exactly one top-level `model:` key.\n * agent-router rewrites ONLY that line; the prompt body and every other\n * frontmatter key are owned by the user and never touched.\n *\n * Parsing is deliberately line-based rather than a full YAML round-trip:\n * a YAML parser would re-serialize the whole block and clobber the user's\n * comments, key order, and formatting. A targeted line replacement cannot.\n * Only top-level (column-0) `model:` keys match, so nested keys like\n * `options.model` are never confused for the agent model.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { readFile, readdir } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { AgentFileError, IOError } from \"./errors.js\";\n\n/** Matches the frontmatter block at the very start of the file. */\nconst FRONTMATTER_RE = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---(?:\\r?\\n|$)/;\n\n/** Matches a top-level `model:` line inside frontmatter (multiline mode). */\nconst MODEL_LINE_RE = /^model:[ \\t]*(.*)$/m;\n\n/** Strip a trailing YAML comment (` # ...`) and surrounding quotes/space. */\nfunction cleanModelValue(raw: string): string {\n let v = raw;\n const hash = v.search(/[ \\t]#/);\n if (hash >= 0) v = v.slice(0, hash);\n v = v.trim();\n if (\n (v.startsWith('\"') && v.endsWith('\"') && v.length >= 2) ||\n (v.startsWith(\"'\") && v.endsWith(\"'\") && v.length >= 2)\n ) {\n v = v.slice(1, -1);\n }\n return v;\n}\n\n/**\n * Extract the frontmatter `model:` value from agent file content.\n * Returns null when the file has no frontmatter or no model line.\n */\nexport function getFrontmatterModel(content: string): string | null {\n const fm = FRONTMATTER_RE.exec(content);\n if (!fm?.[1]) return null;\n const line = MODEL_LINE_RE.exec(fm[1]);\n if (!line) return null;\n const value = cleanModelValue(line[1] ?? \"\");\n return value.length > 0 ? value : null;\n}\n\n/**\n * Return new content with the frontmatter `model:` line replaced by\n * `model: <model>`. Throws (plain Error — callers wrap with context) when\n * there is no frontmatter or no model line to replace.\n */\nexport function setFrontmatterModel(content: string, model: string): string {\n const fm = FRONTMATTER_RE.exec(content);\n if (!fm?.[1]) throw new Error(\"no frontmatter block\");\n const block = fm[1];\n if (!MODEL_LINE_RE.test(block)) throw new Error(\"no `model:` line in frontmatter\");\n // Replacement callback sidesteps `$`-pattern interpretation in the model id.\n const nextBlock = block.replace(MODEL_LINE_RE, () => `model: ${model}`);\n // Splice the edited block back into the matched frontmatter by index so no\n // string in the file is ever treated as a pattern.\n const blockStart = fm[0].indexOf(block);\n const nextFm = fm[0].slice(0, blockStart) + nextBlock + fm[0].slice(blockStart + block.length);\n return nextFm + content.slice(fm[0].length);\n}\n\n/** Absolute path of `<agentsDir>/<name>.md`. */\nexport function agentFilePath(agentsDir: string, name: string): string {\n return path.join(agentsDir, `${name}.md`);\n}\n\n/** List agent names (`.md` basenames) in the agents dir, sorted. */\nexport async function listAgentFiles(agentsDir: string): Promise<string[]> {\n if (!existsSync(agentsDir)) return [];\n let names: string[];\n try {\n names = await readdir(agentsDir);\n } catch (cause) {\n throw new IOError(`Failed to read agents dir: ${(cause as Error).message}`, cause);\n }\n return names\n .filter((n) => n.endsWith(\".md\"))\n .map((n) => n.slice(0, -\".md\".length))\n .sort();\n}\n\n/**\n * Read the current agent → model mapping from every `.md` file in the agents\n * dir. Files without a frontmatter `model:` line are skipped (they aren't\n * routable agents — e.g. docs accidentally living there).\n */\nexport async function readAgentModels(agentsDir: string): Promise<Record<string, string>> {\n const out: Record<string, string> = {};\n for (const name of await listAgentFiles(agentsDir)) {\n const filePath = agentFilePath(agentsDir, name);\n let content: string;\n try {\n content = await readFile(filePath, \"utf8\");\n } catch (cause) {\n throw new IOError(`Failed to read ${filePath}: ${(cause as Error).message}`, cause);\n }\n const model = getFrontmatterModel(content);\n if (model !== null) out[name] = model;\n }\n return out;\n}\n\n/**\n * Read one agent file strictly: throws `AgentFileError` when the file is\n * missing or has no rewritable `model:` line. Returns the raw content plus\n * the current model, ready for `setFrontmatterModel`.\n */\nexport async function readAgentFileStrict(\n agentsDir: string,\n name: string,\n): Promise<{ filePath: string; content: string; model: string }> {\n const filePath = agentFilePath(agentsDir, name);\n if (!existsSync(filePath)) {\n throw new AgentFileError(name, filePath, \"file does not exist\");\n }\n let content: string;\n try {\n content = await readFile(filePath, \"utf8\");\n } catch (cause) {\n throw new IOError(`Failed to read ${filePath}: ${(cause as Error).message}`, cause);\n }\n const model = getFrontmatterModel(content);\n if (model === null) {\n throw new AgentFileError(name, filePath, \"no frontmatter `model:` line to rewrite\");\n }\n return { filePath, content, model };\n}\n","/**\n * Switch history: rolling log of the agent→model mappings each apply\n * displaced.\n *\n * Every `agent-router use <new>` writes the *current* (about-to-be-displaced)\n * mapping here — capture-shaped JSON — under a filename that encodes the\n * timestamp and the from→to transition. `agent-router back` walks this\n * directory's names to compute multi-step reverts.\n *\n * Why filenames carry the metadata (vs. a separate index.json):\n * - `ls -t history/` already gives chronological order.\n * - One-file-per-event means concurrent CLIs can't corrupt an index.\n * - The user can delete history entries manually if they care to.\n */\n\nimport { mkdir, readdir, unlink } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { atomicWriteFile } from \"./atomic-write.js\";\nimport { timestampStamp } from \"./backups.js\";\nimport { IOError } from \"./errors.js\";\n\nexport interface HistoryEntry {\n /** Filename without `.json` extension. Sortable: lex order = chronological. */\n readonly id: string;\n /** Absolute path to the JSON content file. */\n readonly path: string;\n /** Timestamp parsed from filename. */\n readonly timestamp: string;\n /** Stack name that *was* active when this snapshot was taken. May be a sentinel. */\n readonly fromStack: string;\n /** Stack name being switched *to* when this snapshot was taken. */\n readonly toStack: string;\n}\n\n/**\n * Filename format: `<isoStamp>__<from>-to-<to>.json`.\n *\n * `<isoStamp>` uses dashes instead of `:`/`.` so it's path-safe (see\n * `backups.timestampStamp`). `__` separates timestamp from labels so the\n * parser can find it unambiguously even if stack names contain `-`.\n */\nfunction filename(fromStack: string, toStack: string, when: Date = new Date()): string {\n const stamp = timestampStamp(when);\n // Stack names are user-supplied. Sanitize lightly so filenames stay\n // reasonable. We allow alphanumerics, dash, underscore, dot, and\n // parentheses (for the `(none)` label). Everything else collapses to `_`.\n const safe = (s: string) => s.replace(/[^A-Za-z0-9._()-]/g, \"_\");\n return `${stamp}__${safe(fromStack)}-to-${safe(toStack)}.json`;\n}\n\n/** Parse a filename back into structured metadata. Returns null on mismatch. */\nexport function parseHistoryFilename(name: string): Omit<HistoryEntry, \"path\"> | null {\n if (!name.endsWith(\".json\")) return null;\n const stem = name.slice(0, -\".json\".length);\n const sep = stem.indexOf(\"__\");\n if (sep < 0) return null;\n const stamp = stem.slice(0, sep);\n const rest = stem.slice(sep + 2);\n const toIdx = rest.lastIndexOf(\"-to-\");\n if (toIdx < 0) return null;\n return {\n id: stem,\n timestamp: stamp,\n fromStack: rest.slice(0, toIdx),\n toStack: rest.slice(toIdx + \"-to-\".length),\n };\n}\n\n/**\n * Append a snapshot of `displacedContent` to history under a deterministic\n * filename derived from `fromStack`/`toStack` and the current time.\n *\n * @returns The id (filename stem) of the created entry.\n */\nexport async function appendHistory(\n historyDir: string,\n fromStack: string,\n toStack: string,\n displacedContent: string,\n): Promise<string> {\n await mkdir(historyDir, { recursive: true });\n const name = filename(fromStack, toStack);\n await atomicWriteFile(path.join(historyDir, name), displacedContent);\n return name.slice(0, -\".json\".length);\n}\n\n/** List history entries newest-first. Returns empty array if dir missing. */\nexport async function listHistory(historyDir: string): Promise<HistoryEntry[]> {\n let names: string[];\n try {\n names = await readdir(historyDir);\n } catch (cause) {\n if ((cause as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n throw new IOError(`Failed to read history dir: ${(cause as Error).message}`, cause);\n }\n const entries: HistoryEntry[] = [];\n for (const n of names) {\n const parsed = parseHistoryFilename(n);\n if (parsed) entries.push({ ...parsed, path: path.join(historyDir, n) });\n }\n // Sort descending by timestamp (which is in lex-sortable ISO form).\n entries.sort((a, b) => (a.timestamp < b.timestamp ? 1 : a.timestamp > b.timestamp ? -1 : 0));\n return entries;\n}\n\n/**\n * Trim history to at most `keep` entries, deleting oldest files first.\n * Default 20 mirrors the plan. Returns the ids of deleted entries.\n */\nexport async function trimHistory(historyDir: string, keep = 20): Promise<string[]> {\n const all = await listHistory(historyDir);\n if (all.length <= keep) return [];\n const toDelete = all.slice(keep);\n const deleted: string[] = [];\n for (const e of toDelete) {\n try {\n await unlink(e.path);\n deleted.push(e.id);\n } catch (cause) {\n // Best-effort: log silently and keep going. A leftover history file is\n // harmless beyond a bit of disk; failing the whole switch over a stale\n // unlink would be worse UX.\n void cause;\n }\n }\n return deleted;\n}\n","/**\n * One-shot timestamped backups of opencode config files.\n *\n * Backups land under `~/.config/opencode/.backups/<stamp>/` — the same\n * convention other opencode installers use, so files dropped here are\n * recognizable to the user.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { copyFile, mkdir } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { IOError } from \"./errors.js\";\n\n/** Format: `2026-05-04T13-22-01-000Z` — ISO with `:` and `.` swapped to `-` so it's path-safe. */\nexport function timestampStamp(date: Date = new Date()): string {\n return date.toISOString().replace(/[:.]/g, \"-\");\n}\n\n/**\n * Copy `files` (those that exist) into `${backupsRoot}/<stamp>/`.\n *\n * @param backupsRoot Typically `${opencodeConfigDir}/.backups`.\n * @param files Absolute paths to back up. Missing files are skipped silently.\n * @returns Absolute path of the timestamped backup directory.\n */\nexport async function backupFiles(\n backupsRoot: string,\n files: ReadonlyArray<string>,\n): Promise<string> {\n const stamp = timestampStamp();\n const dir = path.join(backupsRoot, stamp);\n await mkdir(dir, { recursive: true });\n\n for (const f of files) {\n if (!existsSync(f)) continue;\n try {\n await copyFile(f, path.join(dir, path.basename(f)));\n } catch (cause) {\n throw new IOError(`Backup copy failed for ${f}: ${(cause as Error).message}`, cause);\n }\n }\n return dir;\n}\n","/**\n * state.json read/write.\n *\n * `state.json` is the single pointer that says which stack is currently\n * \"active\" (i.e. its models were most recently applied to the agent files'\n * frontmatter). Every CLI command that switches stacks updates this file\n * atomically so concurrent CLIs and the plugin agree on what's active.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { atomicWriteJson } from \"./atomic-write.js\";\nimport { IOError, ValidationError } from \"./errors.js\";\nimport { type StateFile, StateFileSchema } from \"./schema.js\";\n\n/**\n * Read state.json. Returns `null` if the file doesn't exist (uninitialized).\n * Throws `ValidationError` if the file exists but is malformed.\n */\nexport async function readState(statePath: string): Promise<StateFile | null> {\n if (!existsSync(statePath)) return null;\n let raw: string;\n try {\n raw = await readFile(statePath, \"utf8\");\n } catch (cause) {\n throw new IOError(`Failed to read ${statePath}: ${(cause as Error).message}`, cause);\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (cause) {\n throw new ValidationError(\n `state.json is not valid JSON: ${(cause as Error).message}`,\n statePath,\n );\n }\n const result = StateFileSchema.safeParse(parsed);\n if (!result.success) {\n throw new ValidationError(\n `state.json failed validation: ${result.error.issues.map((i) => i.message).join(\"; \")}`,\n statePath,\n result.error.issues.map((i) => `${i.path.join(\".\")}: ${i.message}`),\n );\n }\n return result.data;\n}\n\n/** Atomic write of `state.json`. Validates before writing — defensive. */\nexport async function writeState(statePath: string, state: StateFile): Promise<void> {\n const result = StateFileSchema.safeParse(state);\n if (!result.success) {\n throw new ValidationError(\n `Refusing to write invalid state.json: ${result.error.issues.map((i) => i.message).join(\"; \")}`,\n statePath,\n );\n }\n await atomicWriteJson(statePath, result.data);\n}\n","/**\n * Validate that every model ID referenced by a stack file is reachable\n * through the user's current opencode auth.\n *\n * Strategy: shell out to `opencode models`, capture the line-per-id list,\n * compare to the IDs found in the stack. The model catalogue is\n * auth-state-dependent — a user without an Anthropic key won't see\n * `anthropic/...` IDs even though they exist on the registry — so this gives\n * us a real \"will this work\" answer instead of a registry sniff.\n *\n * For tests, callers can inject a fake `runOpencodeModels` that returns\n * canned text. See `tests/fixtures/opencode-models-output.txt`.\n */\n\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { ModelValidationError } from \"./errors.js\";\nimport type { StackFile } from \"./schema.js\";\n\nconst execFileAsync = promisify(execFile);\n\nexport interface ValidateOptions {\n /**\n * Override the model lister. Defaults to running `opencode models` via\n * `execFile`. Tests pass a static string.\n */\n readonly runOpencodeModels?: () => Promise<string>;\n}\n\nexport interface MissingModel {\n /** Dotted location inside the stack — e.g., `agents.oracle.model`. */\n readonly path: string;\n /** The unreachable model ID. */\n readonly modelId: string;\n}\n\nexport interface ValidateResult {\n readonly ok: boolean;\n readonly checked: number;\n readonly missing: ReadonlyArray<MissingModel>;\n /** Models reachable through current auth, parsed from `opencode models`. */\n readonly available: ReadonlySet<string>;\n}\n\nconst DEFAULT_RUNNER = async (): Promise<string> => {\n const { stdout } = await execFileAsync(\"opencode\", [\"models\"], {\n encoding: \"utf8\",\n timeout: 30_000,\n maxBuffer: 4 * 1024 * 1024,\n });\n return stdout;\n};\n\n/** Parse `opencode models` stdout into a Set of model IDs. */\nexport function parseModelList(stdout: string): Set<string> {\n const out = new Set<string>();\n for (const raw of stdout.split(/\\r?\\n/)) {\n const line = raw.trim();\n if (!line) continue;\n if (line.startsWith(\"#\")) continue;\n out.add(line);\n }\n return out;\n}\n\n/** Collect every `(path, modelId)` pair from a stack's `agents` record. */\nexport function collectModelRefs(stack: StackFile): Array<MissingModel> {\n const refs: Array<MissingModel> = [];\n for (const [k, v] of Object.entries(stack.agents)) {\n refs.push({ path: `agents.${k}.model`, modelId: v.model });\n }\n return refs;\n}\n\n/**\n * Validate `stack` against the current opencode model catalogue.\n *\n * Returns a structured result; never throws on validation failure (for use\n * in CLI/plugin contexts that want to format the output themselves).\n */\nexport async function validateStack(\n stack: StackFile,\n options: ValidateOptions = {},\n): Promise<ValidateResult> {\n const runner = options.runOpencodeModels ?? DEFAULT_RUNNER;\n const stdout = await runner();\n const available = parseModelList(stdout);\n const refs = collectModelRefs(stack);\n const missing = refs.filter((r) => !available.has(r.modelId));\n return { ok: missing.length === 0, checked: refs.length, missing, available };\n}\n\n/**\n * Convenience: validate and throw `ModelValidationError` on failure. Used by\n * the pre-apply gate in `stack-manager.applyStack`.\n */\nexport async function validateStackOrThrow(\n stackName: string,\n stack: StackFile,\n options: ValidateOptions = {},\n): Promise<ValidateResult> {\n const result = await validateStack(stack, options);\n if (!result.ok) throw new ModelValidationError(stackName, result.missing);\n return result;\n}\n","/**\n * Pure logic behind the TUI dialogs — no opentui/api access, fully\n * unit-testable. Dialog flows in dialogs.ts call these.\n */\n\nimport type { StackFile } from \"../core/schema.js\";\n\nexport interface ModelTarget {\n readonly agent: string;\n readonly model: string;\n}\n\nexport function targetLabel(target: Pick<ModelTarget, \"agent\">): string {\n return target.agent;\n}\n\nexport function listModelTargets(stack: StackFile): ModelTarget[] {\n return Object.entries(stack.agents).map(([agent, entry]) => ({\n agent,\n model: entry.model,\n }));\n}\n\n/**\n * Replace the model of one agent entry, preserving every other key\n * (unknown passthrough fields) untouched.\n */\nexport function applyModelEdit(stack: StackFile, agent: string, model: string): StackFile {\n const entry = stack.agents[agent];\n if (!entry) {\n throw new Error(`No entry \"${agent}\" in stack`);\n }\n return {\n ...stack,\n agents: {\n ...stack.agents,\n [agent]: { ...entry, model },\n },\n };\n}\n\n/**\n * Extract `provider/model` IDs from the TUI host's provider catalog\n * (`api.state.provider`). Shape-defensive: the SDK type evolves, so treat it\n * as unknown and pull only what looks right. Returns [] when nothing usable.\n */\nexport function collectHostModels(providers: unknown): string[] {\n if (!Array.isArray(providers)) return [];\n const out = new Set<string>();\n for (const provider of providers) {\n if (typeof provider !== \"object\" || provider === null) continue;\n const record = provider as Record<string, unknown>;\n const id = typeof record.id === \"string\" ? record.id : undefined;\n if (!id) continue;\n const models = record.models;\n if (Array.isArray(models)) {\n for (const m of models) {\n if (typeof m === \"string\") out.add(`${id}/${m}`);\n else if (typeof m === \"object\" && m !== null) {\n const mid = (m as Record<string, unknown>).id;\n if (typeof mid === \"string\") out.add(`${id}/${mid}`);\n }\n }\n } else if (typeof models === \"object\" && models !== null) {\n for (const key of Object.keys(models)) out.add(`${id}/${key}`);\n }\n }\n return [...out].sort();\n}\n","/**\n * Dialog flows: switch, view, edit, back, validate. Thin glue between\n * opencode's DialogSelect/DialogConfirm and the core stack operations —\n * decision logic lives in actions.ts / core, so this file stays presentational.\n */\n\nimport { atomicWriteJson } from \"../core/atomic-write.js\";\nimport type { RouterPaths } from \"../core/paths.js\";\nimport { StackFileSchema } from \"../core/schema.js\";\nimport {\n applyStack,\n back,\n getActiveStackName,\n listStacks,\n readStack,\n stackPath,\n} from \"../core/stack-manager.js\";\nimport { readState } from \"../core/state.js\";\nimport { validateStack } from \"../core/validator.js\";\nimport {\n type ModelTarget,\n applyModelEdit,\n collectHostModels,\n listModelTargets,\n targetLabel,\n} from \"./actions.js\";\nimport type { RouterTuiApi, SelectOption } from \"./host.js\";\n\nexport interface DialogDeps {\n readonly api: RouterTuiApi;\n readonly paths: RouterPaths;\n readonly refresh: () => void;\n}\n\nexport function canOpenDialogs(api: RouterTuiApi): boolean {\n return Boolean(api.ui.DialogSelect && api.ui.dialog);\n}\n\nfunction toastError(api: RouterTuiApi, e: unknown): void {\n api.ui.toast({\n title: \"agent-router\",\n message: (e as Error).message ?? String(e),\n variant: \"error\",\n });\n}\n\nfunction openSelect(\n api: RouterTuiApi,\n props: Parameters<NonNullable<RouterTuiApi[\"ui\"][\"DialogSelect\"]>>[0],\n): void {\n const { DialogSelect, dialog } = api.ui;\n if (!DialogSelect || !dialog) return;\n dialog.replace(() => DialogSelect(props));\n}\n\nasync function pickStack(\n deps: DialogDeps,\n title: string,\n onPick: (name: string, active: string | null) => void,\n): Promise<void> {\n const [stacks, active] = await Promise.all([\n listStacks(deps.paths),\n getActiveStackName(deps.paths),\n ]);\n if (stacks.length === 0) {\n deps.api.ui.toast({\n title: \"agent-router\",\n message: \"No stacks found — run `agent-router init` first.\",\n variant: \"warning\",\n });\n return;\n }\n const options: SelectOption[] = stacks.map((name) => ({\n title: name,\n value: name,\n description: name === active ? \"● active\" : undefined,\n onSelect: () => onPick(name, active),\n }));\n openSelect(deps.api, { title, options, current: active ?? undefined });\n}\n\nexport function openStackSwitcher(deps: DialogDeps): void {\n void pickStack(deps, \"Switch stack\", (name, active) => {\n deps.api.ui.dialog?.clear();\n if (name === active) {\n deps.api.ui.toast({ title: \"agent-router\", message: `\"${name}\" is already active` });\n return;\n }\n deps.api.ui.toast({ title: \"agent-router\", message: `validating & switching to \"${name}\"…` });\n applyStack(deps.paths, name, { validate: true })\n .then((r) => {\n deps.refresh();\n deps.api.ui.toast({\n title: \"agent-router\",\n message: `switched to \"${r.current}\" — restart opencode to apply`,\n variant: \"success\",\n });\n })\n .catch((e) => toastError(deps.api, e));\n }).catch((e) => toastError(deps.api, e));\n}\n\nexport function openStackViewer(deps: DialogDeps): void {\n void pickStack(deps, \"View stack\", (name) => {\n readStack(deps.paths, name)\n .then((stack) => {\n const rows = listModelTargets(stack);\n const options: SelectOption[] = rows.map((row) => ({\n title: targetLabel(row),\n value: targetLabel(row),\n description: row.model,\n onSelect: () => deps.api.ui.dialog?.clear(),\n }));\n openSelect(deps.api, { title: `stack: ${name}`, options });\n })\n .catch((e) => toastError(deps.api, e));\n }).catch((e) => toastError(deps.api, e));\n}\n\nexport function openStackEditor(deps: DialogDeps): void {\n void pickStack(deps, \"Edit stack\", (name) => {\n readStack(deps.paths, name)\n .then((stack) => {\n const rows = listModelTargets(stack);\n const options: SelectOption[] = rows.map((row) => ({\n title: targetLabel(row),\n value: targetLabel(row),\n description: row.model,\n onSelect: () => openModelPicker(deps, name, row),\n }));\n openSelect(deps.api, { title: `edit ${name}: pick agent`, options });\n })\n .catch((e) => toastError(deps.api, e));\n }).catch((e) => toastError(deps.api, e));\n}\n\nfunction openModelPicker(deps: DialogDeps, stackName: string, row: ModelTarget): void {\n const models = collectHostModels(deps.api.state?.provider);\n if (models.length === 0) {\n deps.api.ui.dialog?.clear();\n deps.api.ui.toast({\n title: \"agent-router\",\n message: \"Model catalog unavailable — edit the stack file via `agent-router edit` instead.\",\n variant: \"warning\",\n });\n return;\n }\n const options: SelectOption[] = models.map((id) => ({\n title: id,\n value: id,\n onSelect: () => {\n deps.api.ui.dialog?.clear();\n saveModelEdit(deps, stackName, row, id).catch((e) => toastError(deps.api, e));\n },\n }));\n openSelect(deps.api, {\n title: `edit ${stackName}: ${targetLabel(row)}`,\n options,\n current: row.model,\n });\n}\n\nasync function saveModelEdit(\n deps: DialogDeps,\n stackName: string,\n row: ModelTarget,\n model: string,\n): Promise<void> {\n const stack = await readStack(deps.paths, stackName);\n const edited = StackFileSchema.parse(applyModelEdit(stack, row.agent, model));\n await atomicWriteJson(stackPath(deps.paths, stackName), edited);\n deps.refresh();\n const active = await getActiveStackName(deps.paths);\n const hint =\n active === stackName\n ? ` — run \\`agent-router use ${stackName}\\` + restart to apply to the agent files`\n : \"\";\n deps.api.ui.toast({\n title: \"agent-router\",\n message: `${stackName}: ${targetLabel(row)} → ${model}${hint}`,\n variant: \"success\",\n });\n}\n\nexport function openBackConfirm(deps: DialogDeps): void {\n readState(deps.paths.statePath)\n .then((state) => {\n if (!state?.previousActive) {\n deps.api.ui.toast({ title: \"agent-router\", message: \"No previous stack to revert to.\" });\n return;\n }\n const { DialogConfirm, dialog } = deps.api.ui;\n const revert = () => {\n dialog?.clear();\n back(deps.paths, 1)\n .then((r) => {\n deps.refresh();\n deps.api.ui.toast({\n title: \"agent-router\",\n message: `reverted to \"${r.current}\" — restart opencode to apply`,\n variant: \"success\",\n });\n })\n .catch((e) => toastError(deps.api, e));\n };\n if (!DialogConfirm || !dialog) {\n revert();\n return;\n }\n dialog.replace(() =>\n DialogConfirm({\n title: \"agent-router\",\n message: `Revert to \"${state.previousActive}\"?`,\n onConfirm: revert,\n onCancel: () => dialog.clear(),\n }),\n );\n })\n .catch((e) => toastError(deps.api, e));\n}\n\nexport function openValidator(deps: DialogDeps): void {\n void pickStack(deps, \"Validate stack\", (name) => {\n deps.api.ui.dialog?.clear();\n deps.api.ui.toast({ title: \"agent-router\", message: `validating \"${name}\"…` });\n readStack(deps.paths, name)\n .then((stack) => validateStack(stack))\n .then((result) => {\n if (result.ok) {\n deps.api.ui.toast({\n title: \"agent-router\",\n message: `\"${name}\" ok — ${result.checked} model refs reachable`,\n variant: \"success\",\n });\n return;\n }\n const sample = result.missing\n .slice(0, 3)\n .map((m) => m.modelId)\n .join(\", \");\n const extra = result.missing.length > 3 ? ` (+${result.missing.length - 3} more)` : \"\";\n deps.api.ui.toast({\n title: \"agent-router\",\n message: `\"${name}\": ${result.missing.length} unreachable — ${sample}${extra}`,\n variant: \"warning\",\n });\n })\n .catch((e) => toastError(deps.api, e));\n }).catch((e) => toastError(deps.api, e));\n}\n","/**\n * Materialize ViewNodes via @opentui/solid's imperative runtime.\n *\n * `SolidRuntime` is structural on purpose: opencode's host provides\n * @opentui/solid at runtime (we never bundle it), and depending on its\n * published types would pin us to a version the host may not match.\n */\n\nimport type { ViewNode } from \"./view.js\";\n\nexport interface SolidRuntime {\n createElement(tag: string): unknown;\n insert(parent: unknown, child: unknown): unknown;\n setProp(node: unknown, name: string, value: unknown): unknown;\n}\n\nexport function materialize(nodes: readonly ViewNode[], solid: SolidRuntime): unknown {\n const root = solid.createElement(\"box\");\n solid.setProp(root, \"flexDirection\", \"column\");\n for (const node of nodes) {\n solid.insert(root, materializeNode(node, solid));\n }\n return root;\n}\n\nfunction materializeNode(node: ViewNode, solid: SolidRuntime): unknown {\n const element = solid.createElement(node.kind);\n for (const [name, value] of Object.entries(node.props)) {\n if (value !== undefined) solid.setProp(element, name, value);\n }\n if (node.kind === \"text\") {\n solid.insert(element, node.text ?? \"\");\n }\n for (const child of node.children ?? []) {\n solid.insert(element, materializeNode(child, solid));\n }\n return element;\n}\n","/**\n * Snapshot + polling layer for the TUI sidebar.\n *\n * Polling (not fs.watch): agent-router writes state.json atomically via\n * temp-file + rename, and Bun's fs.watch drops the rename-to-target event\n * (verified: Bun 1.3.14 reports only the temp file, Node reports both), so a\n * watcher filtered on \"state.json\" never fires inside opencode. A 1.5s poll\n * of two tiny reads is the reliable alternative.\n */\n\nimport type { RouterPaths } from \"../core/paths.js\";\nimport { getActiveStackName, listStacks } from \"../core/stack-manager.js\";\n\nexport interface StackSnapshot {\n readonly active: string | null;\n readonly stacks: readonly string[];\n readonly key: string;\n}\n\nexport function snapshotKey(active: string | null, stacks: readonly string[]): string {\n return `${active ?? \"\\u0000\"}|${stacks.join(\",\")}`;\n}\n\n/** Error-tolerant read: never throws, degrades to `(none)` + empty list. */\nexport async function readStackSnapshot(paths: RouterPaths): Promise<StackSnapshot> {\n const [active, stacks] = await Promise.all([\n getActiveStackName(paths).catch(() => null),\n listStacks(paths).catch(() => [] as string[]),\n ]);\n return { active, stacks, key: snapshotKey(active, stacks) };\n}\n\nexport interface SidebarPollerOptions {\n readonly read: () => Promise<StackSnapshot>;\n readonly intervalMs: number;\n readonly initial: StackSnapshot;\n readonly onChange: (next: StackSnapshot, prev: StackSnapshot) => void;\n /** Injectable for tests; defaults to global setTimeout/clearTimeout. */\n readonly schedule?: (fn: () => void, ms: number) => unknown;\n readonly cancel?: (handle: unknown) => void;\n}\n\n/** Chained-timeout poller with in-flight guard. Returns a stop function. */\nexport function createSidebarPoller(options: SidebarPollerOptions): () => void {\n const schedule = options.schedule ?? ((fn, ms) => setTimeout(fn, ms) as unknown);\n const cancel =\n options.cancel ?? ((handle) => clearTimeout(handle as ReturnType<typeof setTimeout>));\n\n let current = options.initial;\n let disposed = false;\n let inFlight = false;\n let timer: unknown;\n\n const tick = async (): Promise<void> => {\n if (disposed) return;\n if (inFlight) {\n timer = schedule(tick, options.intervalMs);\n return;\n }\n inFlight = true;\n try {\n const next = await options.read();\n if (!disposed && next.key !== current.key) {\n const prev = current;\n current = next;\n options.onChange(next, prev);\n }\n } catch {\n /* transient read failure — retry next tick */\n } finally {\n inFlight = false;\n if (!disposed) timer = schedule(tick, options.intervalMs);\n }\n };\n\n timer = schedule(tick, options.intervalMs);\n\n return () => {\n disposed = true;\n if (timer !== undefined) cancel(timer);\n };\n}\n","/**\n * Pure view model for the sidebar — no opentui imports, fully unit-testable.\n * `materialize` in render.ts turns these nodes into real opentui elements.\n */\n\nimport type { StackSnapshot } from \"./store.js\";\n\nexport interface ViewNode {\n readonly kind: \"box\" | \"text\";\n readonly props: Readonly<Record<string, unknown>>;\n readonly text?: string;\n readonly children?: readonly ViewNode[];\n}\n\nexport interface SidebarTheme {\n readonly textMuted?: unknown;\n readonly warning?: unknown;\n readonly success?: unknown;\n}\n\nexport interface SidebarContext {\n /** Active stack when the TUI booted — differing means a restart is due. */\n readonly bootActive: string | null;\n readonly theme?: SidebarTheme | undefined;\n}\n\nfunction text(content: string, props: Record<string, unknown> = {}): ViewNode {\n return { kind: \"text\", props, text: content };\n}\n\nexport function restartRequired(snapshot: StackSnapshot, ctx: SidebarContext): boolean {\n return snapshot.active !== ctx.bootActive;\n}\n\nexport function buildSidebarNodes(snapshot: StackSnapshot, ctx: SidebarContext): ViewNode[] {\n const theme = ctx.theme ?? {};\n const nodes: ViewNode[] = [\n text(\"agent-router\", { fg: theme.textMuted }),\n text(` ▣ ${snapshot.active ?? \"(none)\"}`, { fg: theme.success }),\n ];\n if (restartRequired(snapshot, ctx)) {\n nodes.push(text(\" ⟳ restart required\", { fg: theme.warning }));\n }\n nodes.push(\n text(` ${snapshot.stacks.length} stack${snapshot.stacks.length === 1 ? \"\" : \"s\"}`, {\n fg: theme.textMuted,\n }),\n );\n return nodes;\n}\n","/**\n * TUI plugin entry for agent-router (opencode >= 1.17).\n *\n * Loaded by opencode's TUI runtime via package.json `exports[\"./tui\"]` and a\n * `tui.json` plugin[] entry (`agent-router init` writes it). Renders the\n * active stack into the sidebar, polls for external changes (CLI/agent\n * switches), and registers the /agent-* command set (switch, view, edit,\n * back, validate, status). Every host call is guarded so a TUI API change\n * degrades to missing features, never a crash — see host.ts for the\n * API-typing stance.\n */\n\nimport { resolvePathsWithConfig } from \"../core/config.js\";\nimport {\n type DialogDeps,\n canOpenDialogs,\n openBackConfirm,\n openStackEditor,\n openStackSwitcher,\n openStackViewer,\n openValidator,\n} from \"./dialogs.js\";\nimport type { RouterTuiApi, TuiCommandEntry } from \"./host.js\";\nimport { type SolidRuntime, materialize } from \"./render.js\";\nimport { createSidebarPoller, readStackSnapshot } from \"./store.js\";\nimport { buildSidebarNodes } from \"./view.js\";\n\nconst POLL_INTERVAL_MS = 1500;\nconst SIDEBAR_ORDER = 850;\n\n/**\n * TUI plugins have no visible stderr; opencode swallows console output. When\n * AGENT_ROUTER_TUI_DEBUG (legacy OMO_TUI_DEBUG) is set, append trace lines to\n * that file so failures inside tui() are diagnosable at all.\n */\nfunction debugLog(message: string): void {\n const target = process.env.AGENT_ROUTER_TUI_DEBUG ?? process.env.OMO_TUI_DEBUG;\n if (!target) return;\n import(\"node:fs\")\n .then((fs) => fs.appendFileSync(target, `${new Date().toISOString()} ${message}\\n`))\n .catch(() => {});\n}\n\nasync function importHostSolid(): Promise<SolidRuntime | null> {\n try {\n const mod = (await import(\"@opentui/solid\")) as Record<string, unknown>;\n if (typeof mod.createElement !== \"function\") {\n debugLog(\"solid module lacks createElement — treating as unavailable\");\n return null;\n }\n return mod as unknown as SolidRuntime;\n } catch (e) {\n debugLog(`solid import FAILED: ${(e as Error).message}`);\n return null;\n }\n}\n\nfunction buildCommands(api: RouterTuiApi, deps: DialogDeps, status: () => void): TuiCommandEntry[] {\n const commands: TuiCommandEntry[] = [\n {\n title: \"agent-router: status\",\n value: \"agent-router.status\",\n description: \"Show the active agent-router stack\",\n category: \"agent-router\",\n slash: { name: \"agent-status\" },\n onSelect: status,\n },\n ];\n if (!canOpenDialogs(api)) return commands;\n commands.push(\n {\n title: \"agent-router: switch stack\",\n value: \"agent-router.switch\",\n description: \"Apply a different model stack to your agents\",\n category: \"agent-router\",\n slash: { name: \"agent-switch\", aliases: [\"ar\"] },\n onSelect: () => openStackSwitcher(deps),\n },\n {\n title: \"agent-router: view stack\",\n value: \"agent-router.view\",\n description: \"Inspect a stack's agent → model assignments\",\n category: \"agent-router\",\n slash: { name: \"agent-view\" },\n onSelect: () => openStackViewer(deps),\n },\n {\n title: \"agent-router: edit stack\",\n value: \"agent-router.edit\",\n description: \"Reassign a model inside a stack\",\n category: \"agent-router\",\n slash: { name: \"agent-edit\" },\n onSelect: () => openStackEditor(deps),\n },\n {\n title: \"agent-router: undo last switch\",\n value: \"agent-router.back\",\n description: \"Revert to the previously active stack\",\n category: \"agent-router\",\n slash: { name: \"agent-back\" },\n onSelect: () => openBackConfirm(deps),\n },\n {\n title: \"agent-router: validate stack\",\n value: \"agent-router.validate\",\n description: \"Check a stack's model IDs against reachable models\",\n category: \"agent-router\",\n slash: { name: \"agent-validate\" },\n onSelect: () => openValidator(deps),\n },\n );\n return commands;\n}\n\nexport const tui = async (api: RouterTuiApi): Promise<void> => {\n debugLog(\"tui() entered\");\n const solid = await importHostSolid();\n if (!solid) return;\n\n const paths = await resolvePathsWithConfig().catch(() => null);\n if (!paths) return;\n\n let snapshot = await readStackSnapshot(paths);\n const bootActive = snapshot.active;\n const viewContext = () => ({ bootActive, theme: api.theme?.current });\n\n try {\n api.slots.register({\n order: SIDEBAR_ORDER,\n slots: {\n sidebar_content: () => materialize(buildSidebarNodes(snapshot, viewContext()), solid),\n },\n });\n debugLog(\"slots.register ok\");\n } catch (e) {\n debugLog(`slots.register FAILED: ${(e as Error).message}`);\n return;\n }\n api.renderer.requestRender();\n\n const applySnapshot = (next: typeof snapshot) => {\n snapshot = next;\n api.renderer.requestRender();\n };\n\n const refresh = () => {\n readStackSnapshot(paths)\n .then(applySnapshot)\n .catch(() => {});\n };\n\n const stopPolling = createSidebarPoller({\n read: () => readStackSnapshot(paths),\n intervalMs: POLL_INTERVAL_MS,\n initial: snapshot,\n onChange: (next, prev) => {\n applySnapshot(next);\n if (next.active !== prev.active) {\n const suffix = next.active === bootActive ? \"\" : \" — restart opencode to apply\";\n api.ui.toast({\n title: \"agent-router\",\n message: `stack → ${next.active ?? \"(none)\"}${suffix}`,\n variant: \"info\",\n });\n }\n },\n });\n\n const deps: DialogDeps = { api, paths, refresh };\n const status = () =>\n api.ui.toast({\n title: \"agent-router\",\n message: `active: ${snapshot.active ?? \"(none)\"} · stacks: ${snapshot.stacks.join(\", \") || \"(none)\"}`,\n variant: \"info\",\n });\n\n try {\n api.command?.register(() => buildCommands(api, deps, status));\n debugLog(`commands registered (dialogs=${canOpenDialogs(api)})`);\n } catch (e) {\n debugLog(`command.register FAILED: ${(e as Error).message}`);\n }\n\n api.lifecycle.onDispose(stopPolling);\n debugLog(`tui() init complete — active=${snapshot.active ?? \"(none)\"}`);\n};\n\nconst agentRouterTui = {\n id: \"agent-router:tui\",\n tui,\n};\n\nexport default agentRouterTui;\n"],"mappings":"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","names":["homedir","path","path","homedir","path","path","path","text","existsSync","copyFile","mkdir","readFile","readdir","unlink","path","existsSync","readFile","path","path","existsSync","readFile","mkdir","readdir","unlink","path","existsSync","mkdir","path","mkdir","path","readdir","unlink","existsSync","readFile","existsSync","readFile","existsSync","readdir","path","readFile"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dylanrussell/agent-router",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Switch the models assigned to your opencode agents. Named stacks applied to agent frontmatter. Plugin + CLI.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Dylan Russell",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/plugin.js",
|
|
9
|
+
"module": "./dist/plugin.js",
|
|
10
|
+
"types": "./dist/plugin.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/plugin.d.ts",
|
|
14
|
+
"import": "./dist/plugin.js"
|
|
15
|
+
},
|
|
16
|
+
"./tui": {
|
|
17
|
+
"types": "./dist/tui.d.ts",
|
|
18
|
+
"import": "./dist/tui.js"
|
|
19
|
+
},
|
|
20
|
+
"./cli": {
|
|
21
|
+
"types": "./dist/cli.d.ts",
|
|
22
|
+
"import": "./dist/cli.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"bin": {
|
|
26
|
+
"agent-router": "dist/cli.js"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"assets/logo.svg",
|
|
31
|
+
"README.md",
|
|
32
|
+
"LICENSE"
|
|
33
|
+
],
|
|
34
|
+
"homepage": "https://github.com/dylanrussellmd/agent-router#readme",
|
|
35
|
+
"bugs": "https://github.com/dylanrussellmd/agent-router/issues",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "git+https://github.com/dylanrussellmd/agent-router.git"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsup",
|
|
42
|
+
"typecheck": "tsc --noEmit",
|
|
43
|
+
"lint": "biome check src tests",
|
|
44
|
+
"lint:fix": "biome check --write src tests",
|
|
45
|
+
"format": "biome format --write src tests",
|
|
46
|
+
"test": "vitest run --coverage",
|
|
47
|
+
"test:unit": "vitest run tests/unit",
|
|
48
|
+
"test:cli": "vitest run tests/cli",
|
|
49
|
+
"test:plugin": "vitest run tests/plugin",
|
|
50
|
+
"test:watch": "vitest",
|
|
51
|
+
"prepublishOnly": "npm run lint && npm run typecheck && npm run build && npm run test"
|
|
52
|
+
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"@opencode-ai/plugin": "^1.14.0"
|
|
55
|
+
},
|
|
56
|
+
"peerDependenciesMeta": {
|
|
57
|
+
"@opencode-ai/plugin": {
|
|
58
|
+
"optional": true
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"cac": "^6.7.14",
|
|
63
|
+
"zod": "^4.0.0"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@biomejs/biome": "^1.9.4",
|
|
67
|
+
"@opencode-ai/plugin": "^1.17.15",
|
|
68
|
+
"@types/node": "^20.14.0",
|
|
69
|
+
"@vitest/coverage-v8": "^4.1.5",
|
|
70
|
+
"tsup": "^8.5.1",
|
|
71
|
+
"tsx": "^4.21.0",
|
|
72
|
+
"typescript": "^5.6.0",
|
|
73
|
+
"vitest": "^4.1.5"
|
|
74
|
+
},
|
|
75
|
+
"engines": {
|
|
76
|
+
"node": ">=20"
|
|
77
|
+
},
|
|
78
|
+
"keywords": [
|
|
79
|
+
"opencode",
|
|
80
|
+
"opencode-plugin",
|
|
81
|
+
"agents",
|
|
82
|
+
"model-router",
|
|
83
|
+
"llm-router"
|
|
84
|
+
],
|
|
85
|
+
"publishConfig": {
|
|
86
|
+
"access": "public"
|
|
87
|
+
},
|
|
88
|
+
"pnpm": {
|
|
89
|
+
"overrides": {
|
|
90
|
+
"uuid": ">=14.0.0",
|
|
91
|
+
"vite": ">=6.4.2",
|
|
92
|
+
"esbuild": ">=0.25.0"
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|