@codeam/shared 2.61.80 → 2.61.81
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +21 -18
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +21 -18
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/protocol/constants.ts","../src/protocol/renderToLines.ts","../src/protocol/remote-command.ts","../src/models/pricing.ts","../src/agents/registry.ts","../src/agents/identity.ts","../src/integrations/registry.ts","../src/integrations/branding.ts","../src/integrations/stack-detect.ts","../src/skills/code-review.ts","../src/skills/resolve-conflicts.ts","../src/skills/spec-driven-development.ts","../src/skills/code-naming.ts","../src/skills/registry.ts","../src/api-url.ts","../src/headroom/manifest.ts","../src/types/events.ts","../src/preview-prompts.ts"],"sourcesContent":["/**\n * Shared wire / lifecycle constants. The values here are bundled\n * into the CLI + VS Code extension at build time via tsup / esbuild\n * and mirrored in `apps/jetbrains-plugin/.../protocol/Constants.kt`\n * since Kotlin can't import an npm package.\n *\n * If you change one of these values, also update the Kotlin mirror.\n */\n\n/**\n * Discriminated chunk-protocol version sent as the\n * `X-Codeam-Protocol-Version` header on every authed request. The\n * backend uses this to opt into legacy translations or to reject\n * with 426 when the client is too far behind. Bumped in lockstep\n * with chunk-shape changes (e.g. when the `chrome_steps` chunk\n * type is added).\n */\nexport const PROTOCOL_VERSION = '2.0.0' as const;\n\n/**\n * The VS Code AgentOutputMonitor's loopback HTTP server bound to\n * 127.0.0.1 on this port — the observer JS in the IDE renderer\n * uses it to round-trip captured chat content back into the\n * extension host. The port is intentionally fixed (rather than\n * `listen(0)`) so the observer script can be a static constant\n * rather than dynamically rewriting itself per session.\n *\n * Multi-window collision is solved by listen(0) per-window in the\n * monitor (see #103); this default is still the documented\n * starting port for tooling that needs to probe whether a CodeAgent\n * Mobile session is active locally.\n */\nexport const OBSERVER_BRIDGE_PORT = 47832;\n\n/**\n * Default plugin → backend heartbeat interval. User-configurable\n * via `codeagent-mobile.heartbeatIntervalMs` on VS Code and\n * `heartbeatIntervalMs` in SettingsService.kt's @State on JetBrains.\n * Mirrors the value the apps/api side uses to flip the paired\n * session to offline.\n */\nexport const HEARTBEAT_INTERVAL_MS_DEFAULT = 30_000;\n\n/**\n * SSE + polling reconnect cap. Vercel's serverless functions close\n * SSE connections after ~25 s by default; the client uses 35 s as\n * its overall socket timeout to leave a beat for graceful close.\n */\nexport const SSE_SOCKET_TIMEOUT_MS = 35_000;\n","/**\n * Render raw PTY bytes into an array of screen lines using a simplified\n * virtual terminal. Handles cursor movements (A/B/C/D/G/H), erase (J/K),\n * alternate-screen (?1049h), carriage return, and LF.\n *\n * This is the authoritative implementation used by both codeam-cli (PTY\n * output) and the VS Code extension (shell-integration output) so that\n * the mobile/web client sees identical chunks regardless of surface.\n */\nexport function renderToLines(raw: string): string[] {\n const screen: string[] = [''];\n let row = 0;\n let col = 0;\n\n function ensureRow(): void {\n while (screen.length <= row) screen.push('');\n }\n\n function writeChar(ch: string): void {\n ensureRow();\n if (col < screen[row].length) {\n screen[row] = screen[row].slice(0, col) + ch + screen[row].slice(col + 1);\n } else {\n while (screen[row].length < col) screen[row] += ' ';\n screen[row] += ch;\n }\n col++;\n }\n\n let i = 0;\n while (i < raw.length) {\n const ch = raw[i];\n\n if (ch === '\\x1B') {\n i++;\n if (i >= raw.length) break;\n\n if (raw[i] === '[') {\n i++;\n let param = '';\n while (i < raw.length && !/[@-~]/.test(raw[i])) param += raw[i++];\n const cmd = raw[i] ?? '';\n const n = parseInt(param) || 1;\n\n if (cmd === 'A') { row = Math.max(0, row - n); }\n else if (cmd === 'B') { row += n; ensureRow(); }\n else if (cmd === 'C') { col += n; }\n else if (cmd === 'D') { col = Math.max(0, col - n); }\n else if (cmd === 'G') { col = Math.max(0, n - 1); }\n else if (cmd === 'H' || cmd === 'f') {\n const p = param.split(';');\n row = Math.max(0, (parseInt(p[0] ?? '1') || 1) - 1);\n col = Math.max(0, (parseInt(p[1] ?? '1') || 1) - 1);\n ensureRow();\n } else if (cmd === 'J') {\n if (param === '2' || param === '3') {\n screen.length = 1; screen[0] = ''; row = 0; col = 0;\n } else if (param === '1') {\n for (let r = 0; r < row; r++) screen[r] = '';\n screen[row] = ' '.repeat(col) + screen[row].slice(col);\n } else {\n screen[row] = screen[row].slice(0, col);\n screen.splice(row + 1);\n }\n } else if (cmd === 'K') {\n ensureRow();\n if (param === '' || param === '0') screen[row] = screen[row].slice(0, col);\n else if (param === '1') screen[row] = ' '.repeat(col) + screen[row].slice(col);\n else if (param === '2') screen[row] = '';\n } else if (cmd === 'h' && (param === '?1049' || param === '?47')) {\n screen.length = 1; screen[0] = ''; row = 0; col = 0;\n } else if (cmd === 'l' && (param === '?1049' || param === '?47')) {\n screen.length = 1; screen[0] = ''; row = 0; col = 0;\n }\n } else if (raw[i] === ']') {\n i++;\n while (i < raw.length) {\n if (raw[i] === '\\x07') break;\n if (raw[i] === '\\x1B' && i + 1 < raw.length && raw[i + 1] === '\\\\') { i++; break; }\n i++;\n }\n }\n } else if (ch === '\\r') {\n if (i + 1 < raw.length && raw[i + 1] === '\\n') {\n row++; col = 0; ensureRow(); i++;\n } else {\n col = 0;\n }\n } else if (ch === '\\n') {\n row++; col = 0; ensureRow();\n } else if (ch >= ' ' || ch === '\\t') {\n writeChar(ch);\n }\n\n i++;\n }\n\n return screen;\n}\n","import { z } from 'zod';\n\n/**\n * The command envelope clients receive from the backend relay — both from\n * the `commands` SSE frames on `/api/commands/pending/stream` and from the\n * `GET /api/commands/pending` polling fallback. One schema, shared, so the\n * VS Code extension (and eventually the CLI) stop blind-casting\n * `Record<string, unknown>` into this shape.\n */\nexport interface RemoteCommand {\n id: string;\n sessionId: string;\n pluginId: string;\n type: string;\n payload: Record<string, unknown>;\n status: string;\n createdAt: number;\n}\n\nconst remoteCommandSchema = z.object({\n id: z.string(),\n sessionId: z.string(),\n pluginId: z.string(),\n type: z.string(),\n // The backend may omit `payload` (or send null) for payload-less commands;\n // clients have always normalized that to `{}` — keep that behavior here.\n payload: z.record(z.string(), z.unknown()).nullish(),\n status: z.string(),\n createdAt: z.number(),\n});\n\n/**\n * Validate a raw (already JSON-parsed) value into a `RemoteCommand`.\n * Returns `null` — never throws — on a malformed envelope so callers can\n * log-and-skip the single bad command without dropping the whole batch.\n */\nexport function toRemoteCommand(raw: unknown): RemoteCommand | null {\n const parsed = remoteCommandSchema.safeParse(raw);\n if (!parsed.success) return null;\n const { payload, ...rest } = parsed.data;\n return { ...rest, payload: payload ?? {} };\n}\n","export interface ModelPricing {\n input: number;\n output: number;\n cacheRead: number;\n cacheWrite: number;\n}\n\nexport const MODEL_PRICING: Record<string, ModelPricing> = {\n // ── Anthropic / Claude ────────────────────────────────────\n // The 4.x rows below cover the model ids actually emitted by the CLI\n // (apps/cli/src/agents/claude/runtime.ts listModels) and the JetBrains\n // fallback catalog (RemoteCommandRouter.kt). Prices are copied from the\n // same-family base rows (claude-opus-4 / claude-sonnet-4 /\n // claude-3-5-haiku) until distinct published rates land.\n 'claude-opus-4-7': { input: 15, output: 75, cacheRead: 1.50, cacheWrite: 18.75 },\n 'claude-opus-4-6': { input: 15, output: 75, cacheRead: 1.50, cacheWrite: 18.75 },\n 'claude-sonnet-4-6': { input: 3, output: 15, cacheRead: 0.30, cacheWrite: 3.75 },\n // Haiku-tier prices copied from claude-3-5-haiku (closest same-tier\n // sibling in this table) — previously this id matched NO row and was\n // silently billed at sonnet rates via the unknown-model fallback.\n 'claude-haiku-4-5': { input: 0.80, output: 4, cacheRead: 0.08, cacheWrite: 1 },\n 'claude-sonnet-4': { input: 3, output: 15, cacheRead: 0.30, cacheWrite: 3.75 },\n 'claude-opus-4': { input: 15, output: 75, cacheRead: 1.50, cacheWrite: 18.75 },\n 'claude-3-5-sonnet': { input: 3, output: 15, cacheRead: 0.30, cacheWrite: 3.75 },\n 'claude-3-5-haiku': { input: 0.80, output: 4, cacheRead: 0.08, cacheWrite: 1 },\n 'claude-3-haiku': { input: 0.25, output: 1.25, cacheRead: 0.03, cacheWrite: 0.30 },\n\n // ── Codex / OpenAI ────────────────────────────────────────\n // GPT-5.x rows are derived from OpenAI's published GPT-5 family rates\n // (standard tier: $1.25/1M in, $10/1M out, cached input at ~10% of input;\n // mini tier: $0.25/1M in, $2/1M out). OpenAI has no separate cache-WRITE\n // premium, so cacheWrite mirrors the input rate. Replace with the exact\n // per-version numbers from developers.openai.com/pricing when published —\n // these were the ZERO placeholders that rendered Codex sessions as $0.\n 'gpt-5.5': { input: 1.25, output: 10, cacheRead: 0.125, cacheWrite: 1.25 },\n 'gpt-5.4': { input: 1.25, output: 10, cacheRead: 0.125, cacheWrite: 1.25 },\n 'gpt-5.4-mini': { input: 0.25, output: 2, cacheRead: 0.025, cacheWrite: 0.25 },\n 'gpt-5.3-codex': { input: 1.25, output: 10, cacheRead: 0.125, cacheWrite: 1.25 },\n 'gpt-5.2': { input: 1.25, output: 10, cacheRead: 0.125, cacheWrite: 1.25 },\n 'codex-auto-review': { input: 1.25, output: 10, cacheRead: 0.125, cacheWrite: 1.25 },\n};\n\nexport const MODEL_CONTEXT_WINDOW: Record<string, number> = {\n // ── Anthropic / Claude ────────────────────────────────────\n 'claude-opus-4-7': 1_000_000,\n 'claude-opus-4-6': 1_000_000,\n 'claude-sonnet-4-6': 1_000_000,\n 'claude-haiku-4-5': 200_000,\n 'claude-opus-4': 1_000_000,\n 'claude-sonnet-4': 1_000_000,\n 'claude-3-5-sonnet': 200_000,\n 'claude-3-5-haiku': 200_000,\n 'claude-3-haiku': 200_000,\n\n // ── Codex / OpenAI ────────────────────────────────────────\n 'gpt-5.5': 272_000,\n 'gpt-5.4': 272_000,\n 'gpt-5.4-mini': 272_000,\n 'gpt-5.3-codex': 272_000,\n 'gpt-5.2': 272_000,\n 'codex-auto-review': 272_000,\n};\n\nconst DEFAULT_CONTEXT_WINDOW = 200_000;\n\n/**\n * Longest-prefix lookup. The tables key by model-family prefix; a model id\n * like `claude-opus-4-7` must resolve to its own row, not be shadowed by the\n * shorter `claude-opus-4` — so the match is by prefix LENGTH, never by the\n * table's insertion order.\n */\nfunction longestPrefixMatch<T>(table: Record<string, T>, model: string): T | undefined {\n let best: T | undefined;\n let bestLen = -1;\n for (const [prefix, value] of Object.entries(table)) {\n if (prefix.length > bestLen && model.startsWith(prefix)) {\n best = value;\n bestLen = prefix.length;\n }\n }\n return best;\n}\n\n/** True when the model id resolves to a real MODEL_PRICING row (i.e. getPricing\n * will NOT be guessing via the unknown-model fallback). */\nexport function isKnownModel(model: string): boolean {\n return longestPrefixMatch(MODEL_PRICING, model) !== undefined;\n}\n\n/**\n * Flagged default for an unpriced model id. All-zero so an unknown model is\n * VISIBLY unpriced ($0) rather than silently MISPRICED at some other family's\n * rates (the old sonnet-4 fallback billed unknown ids — including a haiku id\n * that matched no row — at sonnet rates). `getPricing` returns this object for\n * unknown ids so callers that do unconditional arithmetic still work; callers\n * that must distinguish real pricing from the default check `isKnownModel`.\n */\nexport const UNKNOWN_MODEL_PRICING: ModelPricing = {\n input: 0,\n output: 0,\n cacheRead: 0,\n cacheWrite: 0,\n};\n\n/**\n * Resolve pricing by longest matching prefix. Unknown models resolve to the\n * flagged {@link UNKNOWN_MODEL_PRICING} default (all-zero, i.e. visibly\n * unpriced) instead of guessing at another model's rates. Callers that need to\n * distinguish real pricing from the default must check `isKnownModel(model)`.\n */\nexport function getPricing(model: string): ModelPricing {\n return longestPrefixMatch(MODEL_PRICING, model) ?? UNKNOWN_MODEL_PRICING;\n}\n\nexport function getContextWindow(model: string | null): number {\n if (!model) return DEFAULT_CONTEXT_WINDOW;\n return longestPrefixMatch(MODEL_CONTEXT_WINDOW, model) ?? DEFAULT_CONTEXT_WINDOW;\n}\n\n/**\n * Context window ONLY when it's a confident match — `undefined` otherwise (no\n * default). Use where a wrong value is worse than none: the runtime model\n * selector maps native ACP model ids, many of which are opaque aliases\n * (\"default\", \"opus\") or proxied ids (a MiniMax-backed house agent) that aren't\n * in the catalog. Falling back to 200K for those printed a fake \"200K context\"\n * on every row; returning undefined lets the UI omit the sub-label instead.\n */\nexport function tryGetContextWindow(model: string | null): number | undefined {\n if (!model) return undefined;\n return longestPrefixMatch(MODEL_CONTEXT_WINDOW, model);\n}\n","import type { AgentId, AgentMetadata } from './types';\n\nexport const AGENT_REGISTRY: Record<AgentId, AgentMetadata> = {\n claude: {\n id: 'claude',\n displayName: 'Claude Code',\n binaryName: 'claude',\n enabled: true,\n // Mirrors the backend registry (codeagent-mobile\n // apps/api-v2/src/codespaces/agent.ts — authoritative for auth\n // capabilities). `setup_token` is the bare `sk-ant-oat01-…` from\n // `claude setup-token` → delivered via CLAUDE_CODE_OAUTH_TOKEN.\n supportedAuthKinds: ['setup_token', 'oauth_token', 'api_key'],\n preferredAuthKind: 'setup_token',\n headroomWrappable: true,\n headroomKind: 'claude',\n // npm adapter `@agentclientprotocol/claude-agent-acp`.\n acp: true,\n },\n codex: {\n id: 'codex',\n displayName: 'Codex CLI',\n binaryName: 'codex',\n enabled: true,\n supportedAuthKinds: ['oauth_token', 'api_key'],\n preferredAuthKind: 'oauth_token',\n headroomWrappable: true,\n headroomKind: 'codex',\n // npm adapter `@agentclientprotocol/codex-acp`.\n acp: true,\n // OAuth device-code flow; the user_code on the OpenAI page IS a real\n // human-typed code — surfaces render it (with a copy affordance).\n deviceFlow: true,\n showsUserCode: true,\n },\n copilot: {\n id: 'copilot',\n displayName: 'GitHub Copilot CLI',\n binaryName: 'gh',\n enabled: false,\n supportedAuthKinds: ['oauth_token'],\n preferredAuthKind: 'oauth_token',\n // `headroom init --global copilot` exists even though the agent is\n // still disabled here (no runtime builder yet).\n headroomWrappable: true,\n headroomKind: 'copilot',\n acp: false,\n },\n coderabbit: {\n id: 'coderabbit',\n displayName: 'CodeRabbit',\n binaryName: 'coderabbit',\n enabled: true,\n // CodeRabbit links via a CLI-driven LOOPBACK OAuth (`coderabbit auth\n // login --agent`): the CLI captures the token and hands it to the vault\n // through `linkFromCli` (method:'oauth'), same as the terminal handoff.\n // `oauth_token` is preferred; a real API key is still accepted as a\n // fallback. There is no backend PKCE provider — the loopback runs on the\n // user's own machine, so linking is always CLI-mediated.\n supportedAuthKinds: ['oauth_token', 'api_key'],\n preferredAuthKind: 'oauth_token',\n headroomWrappable: false,\n // Legacy PTY runtime — no ACP adapter registered.\n acp: false,\n },\n cursor: {\n id: 'cursor',\n displayName: 'Cursor Agent',\n binaryName: 'cursor-agent',\n enabled: true,\n // Backend registry is authoritative: since the Cursor OAuth\n // device-flow shipped, new links are oauth_token only (the login\n // blob written to ~/.config/cursor/auth.json). Legacy vaulted\n // api_key rows may still exist server-side, but the link surface\n // no longer offers api_key.\n supportedAuthKinds: ['oauth_token'],\n preferredAuthKind: 'oauth_token',\n // `headroom wrap cursor` is \"manual/print-only\" (IDE settings; the\n // headless cursor-agent CLI has no base-URL override) — runs native.\n headroomWrappable: false,\n // Native ACP server: `cursor-agent acp`.\n acp: true,\n // Reverse-engineered device/poll flow; `userCode` is the secret PKCE\n // verifier echoed back on poll — NEVER human-facing.\n deviceFlow: true,\n showsUserCode: false,\n },\n aider: {\n id: 'aider',\n displayName: 'Aider',\n binaryName: 'aider',\n enabled: true,\n // Aider is OAuth-less — auth is via ANTHROPIC_API_KEY / OPENAI_API_KEY\n // / etc. env vars or `~/.aider.conf.yml`. The link flow surfaces\n // this via the existing --api-key escape hatch in commands/link.ts.\n supportedAuthKinds: ['api_key'],\n preferredAuthKind: 'api_key',\n headroomWrappable: false,\n // Legacy PTY runtime — no ACP adapter registered.\n acp: false,\n },\n gemini: {\n id: 'gemini',\n displayName: 'Gemini CLI',\n binaryName: 'gemini',\n enabled: true,\n // OAuth via `gemini auth login` (captured by `codeam link gemini`\n // from ~/.gemini/oauth_creds.json) AND GEMINI_API_KEY are both\n // accepted by the backend's GeminiProvisioningStrategy and propagated\n // into codespace deploys.\n supportedAuthKinds: ['oauth_token', 'api_key'],\n preferredAuthKind: 'oauth_token',\n // Not listed by `headroom wrap --help` — runs native.\n headroomWrappable: false,\n // Native ACP server: `gemini --skip-trust --acp`.\n acp: true,\n },\n kimi: {\n id: 'kimi',\n displayName: 'Kimi Code',\n binaryName: 'kimi',\n enabled: true,\n // API key (KIMI_API_KEY, + optional KIMI_BASE_URL) is the shipping auth —\n // fully documented, no reverse-engineering. OAuth `/login` (login-state at\n // ~/.kimi-code/credentials/<name>.json, base https://api.kimi.com/coding/)\n // is declared so it can land later without a wire change, but capturing\n // that blob server-side is a separate reverse-engineering spike (phase 2).\n supportedAuthKinds: ['api_key', 'oauth_token'],\n preferredAuthKind: 'api_key',\n // Moonshot's `kimi` is not listed by `headroom wrap --help` — runs native.\n headroomWrappable: false,\n // Native ACP server: `kimi acp` (stdio JSON-RPC, answers `initialize`).\n acp: true,\n },\n opencode: {\n id: 'opencode',\n displayName: 'opencode',\n binaryName: 'opencode',\n enabled: true,\n // opencode is model-AGNOSTIC and AUTO-DETECTS provider API keys from env\n // vars (ANTHROPIC_API_KEY / OPENAI_API_KEY / GEMINI_API_KEY / …) — same\n // model as aider. So the credential is the user's OWN provider key; we set\n // the matching env var (detected by prefix) at deploy. api_key only.\n supportedAuthKinds: ['api_key'],\n preferredAuthKind: 'api_key',\n // Not listed by `headroom init --global` (claude/codex/copilot only) — native.\n headroomWrappable: false,\n // Native ACP server: `opencode acp` (stdio JSON-RPC).\n acp: true,\n },\n};\n\nexport function getEnabledAgents(): AgentMetadata[] {\n return Object.values(AGENT_REGISTRY).filter(m => m.enabled);\n}\n\nexport function getAgent(id: AgentId): AgentMetadata {\n const meta = AGENT_REGISTRY[id];\n if (!meta) throw new Error(`Unknown agent id: ${id}`);\n return meta;\n}\n\nexport function isKnownAgentId(id: string): id is AgentId {\n return id in AGENT_REGISTRY;\n}\n","/**\n * Agent identity — the ONE place the public (`LinkedAgentId`) and internal\n * (`AgentId`) id spaces are declared and bridged, plus the ONE alias\n * normalizer every surface funnels through.\n *\n * Canonical values consolidated from (Phase 2, PR-1):\n * - backend `apps/api-v2/src/linked-agents/agent-map.ts`\n * (`PUBLIC_TO_INTERNAL` / `INTERNAL_TO_PUBLIC` / `LinkedAgentId`),\n * - CLI `apps/cli/src/commands/host/agent-provisioning.ts`\n * (`PUBLIC_TO_INTERNAL_AGENT`),\n * - VS Code plugin `apps/vsc-plugin/src/utils/cli-agent-id.ts`\n * (marketplace aliases + `__terminal__:` strip),\n * - CLI `apps/cli/src/commands/start/handlers.ts`\n * (the `claude_code` → `claude` normalization),\n * - mobile `apps/mobile/src/lib/agent-id-map.ts`.\n */\n\nimport type { AgentId, HeadroomKind } from './types';\nimport { AGENT_REGISTRY, isKnownAgentId } from './registry';\n\n// ─── House agent constants ───────────────────────────────────────────────────\n// Byte-identical mirrors of the backend repo's canonical\n// `codeagent-mobile/packages/shared/src/constants/house-agent.ts` (which the\n// api-v2 additionally hand-mirrors in `common/constants/house-agent.ts`).\n// PR-3 replaces those copies with re-exports of THESE.\n\n/** Sentinel id for the synthetic \"CodeAgent Cloud (incluido)\" house agent. */\nexport const HOUSE_AGENT_ID = 'house-codeagent-cloud';\n\n/** Internal provider discriminator for the house agent. */\nexport const HOUSE_AGENT_PROVIDER = 'codeagent_cloud';\n\n/** White-label display strings — never mention the backend model. */\nexport const HOUSE_AGENT_NAME = 'CodeAgent Cloud';\nexport const HOUSE_AGENT_VENDOR = 'CodeAgent';\nexport const HOUSE_AGENT_SUBTITLE = 'Included — no setup';\n\n// ─── Public (LinkedAgent) id space ───────────────────────────────────────────\n\n/**\n * Public-facing linked-agent ids — the id space the `/api/agents/...`\n * endpoints and the mobile/web surfaces speak. The internal `AgentId`\n * (`'claude' | 'codex' | …`) is what the runtimes / provisioning key on.\n */\nexport type LinkedAgentId =\n | 'claude_code'\n | 'codex'\n | 'cursor'\n | 'aider'\n | 'coderabbit'\n | 'gemini'\n | 'kimi'\n | 'openrouter'\n | 'opencode'\n | typeof HOUSE_AGENT_ID;\n\nexport const LINKED_AGENT_IDS: readonly LinkedAgentId[] = [\n 'claude_code',\n 'codex',\n 'cursor',\n 'aider',\n 'coderabbit',\n 'gemini',\n 'kimi',\n 'openrouter',\n 'opencode',\n HOUSE_AGENT_ID,\n];\n\nexport function isLinkedAgentId(value: string): value is LinkedAgentId {\n return (LINKED_AGENT_IDS as readonly string[]).includes(value);\n}\n\n/**\n * Every public id → internal `AgentId`.\n *\n * ⚠️ RECONCILED ASYMMETRY — this map is the UNION of what the two sides\n * historically accepted:\n * - The backend's `agent-map.ts` accepts only the `LinkedAgentId` union\n * (incl. the house agent, whose runtime is Claude Code) — no bare\n * `claude`, no `copilot` (there is no public copilot LinkedAgentId).\n * - The CLI's self-hosted `agent-provisioning.ts` additionally accepts\n * bare `'claude'` and `'copilot'` (deploy payloads have carried\n * already-internal ids), but not the house agent.\n * Consumers that must REJECT ids outside their own historical set keep\n * their own guard on top (e.g. `isLinkedAgentId`).\n */\nexport const PUBLIC_TO_INTERNAL: Readonly<\n Record<LinkedAgentId | 'claude' | 'copilot', AgentId>\n> = {\n claude_code: 'claude',\n // CLI-side extra: self-hosted deploy payloads may carry the internal id.\n claude: 'claude',\n codex: 'codex',\n // CLI-side extra: copilot has no public LinkedAgentId (backend doesn't\n // expose it) but the self-hosted path accepts it.\n copilot: 'copilot',\n cursor: 'cursor',\n aider: 'aider',\n coderabbit: 'coderabbit',\n gemini: 'gemini',\n kimi: 'kimi',\n // opencode is a real, distinct agent (its own binary + ACP server) — it does\n // NOT map onto another runtime. Public id === internal id.\n opencode: 'opencode',\n // OpenRouter runs Claude Code under the hood, pointed at OpenRouter's hosted\n // \"Anthropic Skin\" (https://openrouter.ai/api — native Anthropic Messages\n // protocol) with the user's vaulted `sk-or-…` key as ANTHROPIC_AUTH_TOKEN,\n // instead of the house MiniMax proxy. Same internal runtime as the house\n // agent — `claude` — differing only in WHERE the Anthropic env is pointed (a\n // buildOpenRouterBootstrap variant). The endpoint is FIXED (hosted), so there\n // is no per-user base URL — just the key.\n openrouter: 'claude',\n // The house agent runs Claude Code under the hood (pointed at the\n // MiniMax proxy). Its internal runtime is therefore `claude`.\n [HOUSE_AGENT_ID]: 'claude',\n};\n\n/**\n * Internal → public. Partial: `copilot` has no public LinkedAgentId, and\n * `claude` maps back to `claude_code` (never the house agent — that\n * direction is intentionally lossy).\n */\nexport const INTERNAL_TO_PUBLIC: Readonly<Partial<Record<AgentId, LinkedAgentId>>> = {\n claude: 'claude_code',\n codex: 'codex',\n cursor: 'cursor',\n aider: 'aider',\n coderabbit: 'coderabbit',\n gemini: 'gemini',\n kimi: 'kimi',\n opencode: 'opencode',\n};\n\nfunction isPublicToInternalKey(v: string): v is LinkedAgentId | 'claude' | 'copilot' {\n // Not Object.hasOwn — the VS Code plugin's tsconfig lib predates ES2022.\n return Object.prototype.hasOwnProperty.call(PUBLIC_TO_INTERNAL, v);\n}\n\n/** Resolve a public/linked id to the internal `AgentId`, or null. */\nexport function publicToInternal(publicId: string): AgentId | null {\n return isPublicToInternalKey(publicId) ? PUBLIC_TO_INTERNAL[publicId] : null;\n}\n\n/** Resolve an internal `AgentId` to its public `LinkedAgentId`, or null. */\nexport function internalToPublic(internal: AgentId): LinkedAgentId | null {\n return INTERNAL_TO_PUBLIC[internal] ?? null;\n}\n\n// ─── Alias normalization ─────────────────────────────────────────────────────\n\n/** Prefix IDE plugins use for terminal-hosted agent ids. */\nexport const TERMINAL_AGENT_PREFIX = '__terminal__:';\n\n/**\n * Known aliases → internal `AgentId`. Union of every alias set that used\n * to live scattered across the surfaces: the public `claude_code` id, the\n * VS Code / Open VSX marketplace extension ids, and JetBrains plugin ids.\n */\nconst AGENT_ID_ALIASES: Readonly<Record<string, AgentId>> = {\n claude_code: 'claude',\n 'claude-code': 'claude',\n 'anthropic.claude-code': 'claude',\n 'anthropics.claude': 'claude',\n 'anthropic.claude-ce': 'claude',\n 'anthropic.claude': 'claude',\n 'com.anthropic.claudecode': 'claude',\n 'com.anthropic.claude': 'claude',\n 'openai.chatgpt': 'codex',\n 'coderabbitai.coderabbit-vscode': 'coderabbit',\n};\n\n/**\n * THE agent-id normalizer. Collapses every known spelling of an agent id\n * (registry id, public `claude_code` form, marketplace extension id,\n * `__terminal__:`-prefixed plugin id — case/whitespace tolerant) onto the\n * internal `AgentId`, or `null` when unknown.\n *\n * Deliberately does NOT:\n * - gate on `enabled` (callers that need availability check the\n * registry — see the VS Code wrapper `normalizeCliAgentId`);\n * - map the house agent (that's a runtime substitution, not an alias —\n * use {@link publicToInternal});\n * - fall back to anything. Unknown in → `null` out.\n */\nexport function normalizeAgentId(raw: string): AgentId | null {\n const value = (raw ?? '').trim().toLowerCase();\n if (!value) return null;\n\n if (isKnownAgentId(value)) return value;\n\n const unprefixed = value.startsWith(TERMINAL_AGENT_PREFIX)\n ? value.slice(TERMINAL_AGENT_PREFIX.length)\n : value;\n if (isKnownAgentId(unprefixed)) return unprefixed;\n\n return AGENT_ID_ALIASES[unprefixed] ?? null;\n}\n\n// ─── Headroom kind derivation ────────────────────────────────────────────────\n\n/**\n * The `headroom init --global <kind>` subcommand for an agent id, derived\n * from the registry's `headroomKind` flags — or `null` for unknown or\n * non-wrappable agents (cursor / gemini / aider / anything else).\n *\n * ⚠️ NEVER falls back to `'claude'`. The historical CLI fallback is how\n * the 2026-06 Cursor incident happened: an unsupported agent slipped\n * through, defaulted to `claude`, and `headroom wrap claude` launched\n * Claude Code instead of the user's agent. Callers that genuinely need a\n * default (e.g. picking an init subcommand AFTER the wrappable gate has\n * already passed) apply it themselves — see the CLI's\n * `agentIdToHeadroomKind` wrapper.\n *\n * Matching mirrors the historical predicates on BOTH sides (CLI\n * `isHeadroomSupportedAgent`, api-v2 `isHeadroomWrappableAgent`):\n * case-insensitive, `_`/`-` tolerant, prefix match — so `claude_code`,\n * `Claude-Code`, `codex_cli`, `copilot-cli` all resolve.\n */\nexport function headroomKindFor(agentId: string): HeadroomKind | null {\n const normalized = (agentId ?? '').toLowerCase().replace(/[_-]/g, '');\n if (!normalized) return null;\n for (const meta of Object.values(AGENT_REGISTRY)) {\n if (meta.headroomKind !== undefined && normalized.startsWith(meta.id)) {\n return meta.headroomKind;\n }\n }\n return null;\n}\n\n/**\n * Registry-derived replacement for the two scattered predicates\n * (`isHeadroomSupportedAgent` in the CLI, `isHeadroomWrappableAgent` in\n * api-v2). Accepts both id spaces (`claude_code` and `claude`).\n */\nexport function isHeadroomWrappable(agentId: string): boolean {\n return headroomKindFor(agentId) !== null;\n}\n","import type {\n IntegrationCategory,\n IntegrationDefinition,\n IntegrationId,\n} from './types';\n\n/**\n * The single source of truth for supported integrations. Adding one =\n * 1 entry here + 1 backend OAuth provider + icon. The `delivery` spec is\n * resolved into deploy manifests and executed as data by the CLI, so a new\n * MCP integration with no special logic needs no CLI release.\n */\nexport const INTEGRATION_REGISTRY: Record<IntegrationId, IntegrationDefinition> = {\n jira: {\n // Registry id kept 'jira' for id-stability (existing LinkedIntegration rows\n // stay valid — zero data migration); only the DISPLAY is \"Atlassian\". The\n // one mcp-atlassian server serves BOTH Jira and Confluence, so the same\n // entry now requests Confluence scopes too.\n id: 'jira',\n name: 'Atlassian',\n icon: 'jira',\n category: 'tracker',\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n // Jira + Confluence 3LO granular scopes. The Confluence pair\n // (read:confluence-content.all / write:confluence-content) is the set\n // mcp-atlassian's own Authentication docs recommend for full read+write\n // Confluence (matches its documented env scope string).\n scopes: [\n 'read:jira-work',\n 'write:jira-work',\n 'read:confluence-content.all',\n 'write:confluence-content',\n 'offline_access',\n ],\n },\n delivery: {\n mcp: {\n // mcp-atlassian in BYO-token mode (headless; credentials via env only).\n // Version PINNED to the exact release verified headless by Plan 2's\n // Docker integration test (apps/cli mcp-shim.int.test.ts).\n command: 'uvx',\n args: ['mcp-atlassian==0.22.1'],\n envMapping: {\n ATLASSIAN_OAUTH_ACCESS_TOKEN: 'accessToken',\n ATLASSIAN_OAUTH_CLOUD_ID: 'cloudId',\n },\n // Without ATLASSIAN_OAUTH_ENABLE=true, JiraConfig.from_env() raises\n // \"Missing required JIRA_URL\" (swallowed at server startup) and the\n // server silently registers ZERO Jira tools. The flag activates\n // mcp-atlassian's \"minimal OAuth config for user-provided tokens\"\n // mode — the BYO-token path the broker feeds. Static + non-secret.\n staticEnv: { ATLASSIAN_OAUTH_ENABLE: 'true' },\n },\n },\n },\n sentry: {\n id: 'sentry',\n name: 'Sentry',\n icon: 'sentry',\n category: 'observability',\n // LIVE — the Sentry OAuth Application (Confidential) is registered and\n // SENTRY_OAUTH_CLIENT_ID/SECRET/REDIRECT_URI are in Secret Manager\n // (prod+dev). The backend SentryOAuthProvider is config-gated (503 if\n // env unset) so this is safe even mid-rollout before the secrets mount.\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n // FULL read+write across every Sentry resource — the agent can read\n // issues/events/projects AND act (resolve/assign issues, manage\n // projects/teams/members, releases). `:write` implies `:read`. Admin\n // (destructive org/member management) is deliberately NOT requested.\n // ⚠️ Changing these requires the user to RE-LINK Sentry — the existing\n // token only carries whatever scopes it was granted at link time.\n scopes: [\n 'org:read',\n 'org:write',\n 'project:read',\n 'project:write',\n 'team:read',\n 'team:write',\n 'member:read',\n 'member:write',\n 'event:read',\n 'event:write',\n 'project:releases',\n ],\n },\n delivery: {\n mcp: {\n // Sentry's official stdio MCP server (Node). BYO-token headless: the\n // OAuth access token is fed via SENTRY_ACCESS_TOKEN and the host via\n // SENTRY_HOST (never argv — env only). Version PINNED; bump only\n // after re-verifying headless in the mcp-shim integration test.\n command: 'npx',\n // `--add-scopes` widens the server's default READ-ONLY tool surface to\n // include the write tools our OAuth scopes now grant (resolve/assign\n // issue, update project, etc.), so the agent exposes read AND write.\n args: [\n '-y',\n '@sentry/mcp-server@0.18.0',\n '--add-scopes=org:write,project:write,team:write,member:write,event:write',\n ],\n envMapping: {\n SENTRY_ACCESS_TOKEN: 'accessToken',\n SENTRY_HOST: 'host',\n },\n },\n },\n },\n linear: {\n id: 'linear',\n name: 'Linear',\n icon: 'linear',\n category: 'tracker',\n // LIVE — the Linear OAuth Application (Public + Confidential) is registered\n // and LINEAR_OAUTH_CLIENT_ID/SECRET/REDIRECT_URI are in Secret Manager\n // (prod+dev). The backend LinearOAuthProvider is config-gated (503 if env\n // unset) so this is safe even mid-rollout before the secrets mount.\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n // Linear's coarse scopes: `read` (all issues/projects/comments/cycles)\n // + `write` (create/update issues, comments, state). `write` implies the\n // create/update surface the agent's tools need. `admin` (destructive\n // workspace management) is deliberately NOT requested. ⚠️ Changing these\n // requires the user to RE-LINK Linear — the token only carries the scopes\n // granted at link time. Linear expects a COMMA-separated `scope` param.\n scopes: ['read', 'write'],\n },\n delivery: {\n mcp: {\n // mcp-linear (stdio, @linear/sdk) in BYO-token headless mode: the OAuth\n // access token is fed via LINEAR_API_KEY (env only, never argv) and the\n // Linear GraphQL API accepts it as the Authorization header directly.\n // Verified headless end-to-end (search_issues returned real issues) —\n // the OFFICIAL remote MCP (mcp.linear.app) can't be used here because it\n // forces its own interactive browser OAuth. Version PINNED; bump only\n // after re-verifying headless. Tools: search/get/create/update issue +\n // add comment (read + write).\n command: 'npx',\n args: ['-y', 'mcp-linear@0.1.8'],\n envMapping: {\n LINEAR_API_KEY: 'accessToken',\n },\n },\n },\n },\n github: {\n id: 'github',\n name: 'GitHub',\n icon: 'github',\n category: 'version_control',\n // LIVE. GitHub is the product's code substrate (codespaces + the PR\n // Command Center), and it was historically the ONE connection outside this\n // registry: its credential lives in a `ProviderToken` row rather than the\n // integrations vault, so it was rendered by a hand-written special-case row\n // and could not legally appear in a deploy's `integrationIds` (the manifest\n // resolver rejects unknown ids — a recurring bug class).\n //\n // `kind: 'connection'` closes that gap without re-plumbing OAuth: the entry\n // makes GitHub a first-class, categorised catalog row whose credential the\n // backend resolves from the SAME `ProviderToken` it always used, while the\n // connect/disconnect flow stays owned by the codespaces rail (the clients\n // route those two actions there). ⚠️ It is a REAL connection with a REAL\n // disconnect — do NOT treat it like `github_issues`, which merely derives\n // from it and has no actions of its own.\n enabled: true,\n auth: { kind: 'connection', connection: 'github' },\n // No MCP: a deployed box already has an authenticated `gh` on PATH.\n delivery: {},\n },\n gitlab: {\n id: 'gitlab',\n name: 'GitLab',\n icon: 'gitlab',\n category: 'version_control',\n // LIVE — a user-owned gitlab.com application (Confidential) with BOTH env\n // callbacks registered, so dev/prod share the client and differ only in\n // GITLAB_OAUTH_REDIRECT_URI. The backend GitLabOAuthProvider is\n // config-gated (503 if env unset), so this is safe mid-rollout.\n //\n // ⚠️ Unlike `github`, this is a NORMAL `oauth_redirect` integration: its\n // credential is vaulted here rather than living in a ProviderToken, because\n // nothing else in the product owns a GitLab connection (GitHub's lives on\n // the codespaces rail, which is why it's `kind: 'connection'`).\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n // GitLab has NO per-resource scopes — `api` is the only one that grants\n // merge-request WRITE (comment / approve / merge / close), so a\n // read-only alternative would make the whole MR surface useless.\n // `write_repository` is the git-over-HTTPS rail for the agent's push;\n // `api` already covers it for user tokens, but it costs nothing on a\n // consent screen that already says \"complete read/write access\" and\n // changing scopes later forces EVERY user to re-authorize.\n scopes: ['api', 'write_repository'],\n },\n // No MCP: the box's `git` is authenticated for push, and the MR surface is\n // served backend-side by the VCS provider — same shape as `github`.\n delivery: {},\n },\n github_issues: {\n id: 'github_issues',\n name: 'GitHub Issues',\n icon: 'github_issues',\n category: 'tracker',\n // LIVE, and the ONLY integration with NO link flow of its own: the\n // credential is DERIVED from the GitHub connection the user already made\n // for codespaces/PRs (`ProviderToken` provider='github-codespaces', which\n // carries `repo` scope — enough for the whole Issues surface). So there is\n // no OAuth app, no client id/secret, no Secret Manager entry and nothing\n // to configuration-gate. The backend auto-provisions the catalog row the\n // same way `ensureHouseAgentRow` does for the house agent, and resolves\n // the token live per call rather than vaulting a copy (it can't go stale,\n // and GitHub token refresh stays owned by exactly one place).\n enabled: true,\n auth: { kind: 'derived', derivedFrom: 'github' },\n // NO MCP server on purpose. Every other tracker needs one to give the agent\n // tools, but a deployed box ALREADY has an authenticated `gh` on PATH (the\n // codespace bootstrap exports GH_TOKEN), so `gh issue list/create/comment`\n // works with zero delivery. That also means nothing to pre-warm and no\n // third-party MCP package to pin and keep alive. The Start-from-Work-Item\n // side is served backend-side by the `TrackerProvider`, not by delivery.\n delivery: {},\n },\n slack: {\n id: 'slack',\n name: 'Slack',\n icon: 'slack',\n category: 'comms',\n // LIVE — the Slack app (OAuth v2, USER token — the agent acts AS THE USER)\n // is registered and\n // SLACK_OAUTH_CLIENT_ID/SECRET/REDIRECT_URI are in Secret Manager\n // (prod+dev). The backend SlackOAuthProvider is config-gated (503 if env\n // unset) so this is safe even mid-rollout before the secrets mount.\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n // Slack USER Token Scopes (OAuth v2). Read + write across channels,\n // groups, DMs: list/read history, post messages, react — all AS THE USER.\n // The backend provider requests these under `user_scope` (comma-separated)\n // and stores the authed_user `xoxp-…` token, so the agent sees everything\n // the user sees (no bot needs to be invited to channels). ⚠️ Changing\n // these requires the user to RE-AUTHORIZE the Slack app.\n scopes: [\n 'channels:read',\n 'channels:history',\n 'groups:read',\n 'groups:history',\n 'chat:write',\n 'reactions:read',\n 'reactions:write',\n 'users:read',\n 'im:read',\n 'im:history',\n 'mpim:read',\n 'mpim:history',\n // Message search across the user's channels/DMs (a user-only scope).\n 'search:read',\n ],\n },\n delivery: {\n mcp: {\n // Slack's official reference MCP server (Node). BYO-token headless: the\n // OAuth v2 USER token (xoxp-…) is fed via SLACK_BOT_TOKEN (the server's\n // env var name — it sends whatever token as `Authorization: Bearer`, and\n // Slack's Web API accepts a user token there) and the team id via\n // SLACK_TEAM_ID (env only, never argv). Version PINNED; bump only after\n // re-verifying headless. Tools: list_channels, post_message,\n // reply_to_thread, add_reaction, get_channel_history,\n // get_thread_replies, get_users, get_user_profile (read + write).\n command: 'npx',\n args: ['-y', '@modelcontextprotocol/server-slack@2025.4.25'],\n envMapping: {\n SLACK_BOT_TOKEN: 'accessToken',\n SLACK_TEAM_ID: 'teamId',\n },\n },\n },\n },\n microsoft_teams: {\n id: 'microsoft_teams',\n name: 'Microsoft Teams',\n icon: 'microsoft_teams',\n category: 'comms',\n // LIVE — Microsoft identity platform v2.0 OAuth (multi-tenant, Confidential).\n // The agent reads chats/channels + posts AS THE USER over Microsoft Graph.\n // MS_TEAMS_OAUTH_CLIENT_ID/SECRET/REDIRECT_URI are in Secret Manager (prod+dev);\n // the backend TeamsOAuthProvider is config-gated (503 if env unset).\n // ⚠️ Chat.* + ChannelMessage.Read.All (`.All`) require TENANT-ADMIN consent\n // (MC1163922, Oct-2025) — the connecting user must be an org admin, or their\n // IT admin approves the app for the tenant. Not a self-serve funnel like Slack.\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n // Delegated Microsoft Graph scopes. `offline_access` → rotating refresh\n // (Teams access tokens are ~1h). ⚠️ Changing these requires the user to\n // RE-LINK — the token carries only the scopes granted at link time.\n scopes: [\n 'User.Read',\n 'User.ReadBasic.All',\n 'Team.ReadBasic.All',\n 'Channel.ReadBasic.All',\n 'ChannelMessage.Read.All',\n 'ChannelMessage.Send',\n 'Chat.Read',\n 'Chat.ReadWrite',\n 'offline_access',\n ],\n },\n delivery: {\n mcp: {\n // @floriscornel/teams-mcp (Node): BYO Graph access token via AUTH_TOKEN\n // (env only, never argv). Headless. Version PINNED; bump only after\n // re-verifying headless. Tools: get/send channel + chat messages, replies.\n command: 'npx',\n args: ['-y', '@floriscornel/teams-mcp@0.9.0'],\n envMapping: {\n AUTH_TOKEN: 'accessToken',\n },\n },\n },\n },\n google_chat: {\n id: 'google_chat',\n name: 'Google Chat',\n icon: 'google_chat',\n category: 'comms',\n // COMING SOON — placeholder catalog entry (no OAuth provider / MCP yet). The\n // agent will post review pings + read spaces AS THE USER over the Google Chat\n // API once the provider lands. `enabled:false` → dimmed \"coming soon\" tile.\n enabled: false,\n auth: { kind: 'oauth_redirect' },\n delivery: {},\n },\n discord: {\n id: 'discord',\n name: 'Discord',\n icon: 'discord',\n category: 'comms',\n // LIVE — the DiscordOAuthProvider + DISCORD_* secrets (client id/secret,\n // bot token, per-env redirect) are registered; the backend provider is\n // config-gated (503 if env unset). Follows the SLACK pattern (OAuth redirect, zero friction),\n // EXCEPT Discord OAuth gives no per-install token: the user's `bot`-scope\n // authorize INVITES the app's single bot into their guild, and we store the\n // returned guild id as the per-user credential. The broker injects the app's\n // BOT token as `accessToken` (from config), so `DISCORD_TOKEN` = that bot\n // token and `DISCORD_GUILD_ID` = the user's guild.\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n // `bot` invites the app's bot into the user's guild (Guild Install);\n // `guilds` lets the exchange read the guild name for display. The bot's\n // channel permissions (View Channels + Read Message History + Send\n // Messages + Send Messages in Threads) are set on the app's bot, NOT here.\n // ⚠️ The app's bot MUST have the Message Content privileged intent enabled\n // or read_messages returns empty content.\n scopes: ['bot', 'guilds'],\n },\n delivery: {\n mcp: {\n // mcp-discord (barryyip0625) — Node stdio, BYO bot token headless via the\n // DISCORD_TOKEN env (never argv). Version PINNED; bump only after\n // re-verifying headless. Tools: list/read channels + messages, send\n // message, reply in thread, add reaction. `DISCORD_GUILD_ID` scopes it to\n // the user's invited guild.\n command: 'npx',\n args: ['-y', 'mcp-discord@1.3.4'],\n envMapping: {\n DISCORD_TOKEN: 'accessToken',\n DISCORD_GUILD_ID: 'guildId',\n },\n },\n },\n },\n resend: {\n id: 'resend',\n name: 'Resend',\n icon: 'resend',\n category: 'comms',\n // LIVE — api_key (like Azure DevOps): the user pastes a Resend API key\n // (`re_…`); Resend has NO OAuth, so there's no OAuth app / GSM secret /\n // config-gated 503 — a backend VALIDATOR proves the key against the Resend\n // API and vaults it. ⚠️ SEND-ONLY email → `sendOnly: true` keeps it OUT of\n // From-Conversation (comms conversation sources need readable threads; Resend\n // has none) while still being a linkable comms tool the agent uses via MCP.\n enabled: true,\n sendOnly: true,\n auth: {\n kind: 'api_key',\n fields: [\n {\n key: 'accessToken',\n label: 'API Key',\n placeholder: 're_xxxxxxxxxxxxxxxx',\n secret: true,\n help: 'Create in Resend → API Keys (https://resend.com/api-keys). \"Sending access\" is enough.',\n },\n ],\n },\n delivery: {\n mcp: {\n // The OFFICIAL resend-mcp (Node) in BYO-token mode: the API key is fed\n // via RESEND_API_KEY (env only, never argv). Version PINNED; bump only\n // after re-verifying headless. Tools: send email + contacts/broadcasts/\n // domains.\n command: 'npx',\n args: ['-y', 'resend-mcp@2.6.1'],\n envMapping: {\n RESEND_API_KEY: 'accessToken',\n },\n },\n },\n },\n posthog: {\n id: 'posthog',\n name: 'PostHog',\n icon: 'posthog',\n category: 'observability',\n // LIVE — api_key: the user pastes a PostHog Personal API Key (phx_…, created\n // with the \"MCP Server\" preset). PostHog's MCP is HOSTED-ONLY\n // (mcp.posthog.com) over HTTP, and its stdio bridge (mcp-remote) forces its\n // own browser OAuth — incompatible with our headless broker. So delivery\n // uses the shim's HTTP transport: it relays to the hosted MCP with the key\n // as a Bearer header (exactly Cursor's {url, headers} config). No OAuth, no\n // GSM secret; a backend VALIDATOR proves the key + vaults it.\n enabled: true,\n auth: {\n kind: 'api_key',\n fields: [\n {\n key: 'accessToken',\n label: 'Personal API Key',\n placeholder: 'phx_xxxxxxxxxxxxxxxx',\n secret: true,\n help: 'PostHog → Settings → Personal API keys → create with the \"MCP Server\" preset (scopes it to a project).',\n },\n ],\n },\n delivery: {\n mcp: {\n // HTTP transport (not a spawned stdio server) — the shim relays to\n // PostHog's hosted MCP with the key as a Bearer header. The key stays\n // server-side of the shim, never on argv.\n command: '',\n args: [],\n envMapping: {},\n httpUrl: 'https://mcp.posthog.com/mcp',\n httpHeaders: { Authorization: 'Bearer {accessToken}' },\n },\n },\n },\n datadog: {\n id: 'datadog',\n name: 'Datadog',\n icon: 'datadog',\n category: 'observability',\n // LIVE — api_key: the user pastes a Datadog API key (org-scoped) + an\n // Application key (user-scoped, grants the read scopes) + their regional\n // site. Datadog's OFFICIAL hosted MCP (mcp.<site>) accepts these as headers\n // headless (verified) — no OAuth, no GSM secret. Delivery uses the shim's\n // HTTP transport with the site templated into the URL and both keys sent as\n // DD-API-KEY / DD-APPLICATION-KEY headers.\n enabled: true,\n auth: {\n kind: 'api_key',\n fields: [\n {\n key: 'accessToken',\n label: 'API Key',\n placeholder: 'Datadog API key',\n secret: true,\n help: 'Organization API key — Datadog → Organization Settings → API Keys.',\n },\n {\n key: 'appKey',\n label: 'Application Key',\n placeholder: 'Datadog application key',\n secret: true,\n help: 'User Application key (grants the read scopes) — Datadog → Organization Settings → Application Keys.',\n },\n {\n key: 'host',\n label: 'Site',\n placeholder: 'datadoghq.com',\n secret: false,\n help: 'Your Datadog site — US1: datadoghq.com · EU: datadoghq.eu · US3: us3.datadoghq.com · US5: us5.datadoghq.com · AP1: ap1.datadoghq.com.',\n },\n ],\n },\n delivery: {\n mcp: {\n // HTTP transport — the shim relays to Datadog's hosted MCP for the\n // user's site, with both keys as headers. Keys stay server-side of the\n // shim, never on argv.\n command: '',\n args: [],\n envMapping: {},\n httpUrl: 'https://mcp.{host}/api/unstable/mcp-server/mcp',\n httpHeaders: {\n 'DD-API-KEY': '{accessToken}',\n 'DD-APPLICATION-KEY': '{appKey}',\n },\n },\n },\n },\n notion: {\n id: 'notion',\n name: 'Notion',\n icon: 'notion',\n category: 'docs',\n // LIVE — the Notion public OAuth integration is registered and\n // NOTION_OAUTH_CLIENT_ID/SECRET/REDIRECT_URI are in Secret Manager\n // (prod+dev). The backend NotionOAuthProvider is config-gated (503 if env\n // unset) so this is safe even mid-rollout before the secrets mount.\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n // Notion does NOT use per-request OAuth scopes — access is governed by\n // the integration's configured CAPABILITIES (read/update/insert content\n // + read user info), set once on the Notion integration, not passed in\n // the authorize URL. So there is no `scope` param to request here.\n scopes: [],\n },\n delivery: {\n mcp: {\n // Notion's OFFICIAL stdio MCP server (Node). BYO-token headless: the\n // OAuth access token is fed via NOTION_TOKEN and the server sends it as\n // `Authorization: Bearer` + `Notion-Version: 2022-06-28` (env only,\n // never argv). No discriminator — the token alone authenticates its\n // workspace. Version PINNED; bump only after re-verifying headless.\n command: 'npx',\n args: ['-y', '@notionhq/notion-mcp-server@2.4.1'],\n envMapping: {\n NOTION_TOKEN: 'accessToken',\n },\n },\n },\n },\n azure_devops: {\n id: 'azure_devops',\n name: 'Azure DevOps',\n icon: 'azure_devops',\n category: 'tracker',\n // LIVE — the FIRST api_key (PAT) integration. No OAuth: Azure DevOps OAuth\n // apps are being sunset by Microsoft in favor of Entra ID, and PATs are the\n // native, reliable ADO auth. The user pastes their org URL + a PAT; the\n // backend validates against the ADO REST API and vaults it. No env secrets\n // to configure (config-gated 503 doesn't apply — there's no OAuth app).\n enabled: true,\n auth: {\n kind: 'api_key',\n fields: [\n {\n key: 'orgUrl',\n label: 'Organization URL',\n placeholder: 'https://dev.azure.com/your-org',\n secret: false,\n help: 'Your Azure DevOps organization URL — e.g. https://dev.azure.com/contoso',\n },\n {\n key: 'accessToken',\n label: 'Personal Access Token',\n placeholder: 'Paste your PAT',\n secret: true,\n help: 'Create in Azure DevOps → User settings → Personal access tokens. Recommended scopes: Work Items (Read & Write), Code (Read), Build (Read), Project and Team (Read).',\n },\n ],\n },\n delivery: {\n mcp: {\n // The @tiberriver256 ADO MCP server (Node) in PAT mode: the PAT is fed\n // via AZURE_DEVOPS_PAT (Basic auth) + the org via AZURE_DEVOPS_ORG_URL\n // (env only, never argv). AZURE_DEVOPS_AUTH_METHOD=pat pins the PAT path\n // (the alternative, azure-identity, uses DefaultAzureCredential and\n // would ignore our token). Version PINNED; bump only after re-verifying\n // headless.\n command: 'npx',\n args: ['-y', '@tiberriver256/mcp-server-azure-devops@0.1.46'],\n envMapping: {\n AZURE_DEVOPS_PAT: 'accessToken',\n AZURE_DEVOPS_ORG_URL: 'orgUrl',\n },\n staticEnv: { AZURE_DEVOPS_AUTH_METHOD: 'pat' },\n },\n },\n },\n figma: {\n id: 'figma',\n name: 'Figma',\n icon: 'figma',\n category: 'design',\n // LIVE. OAuth is the PRIMARY flow (app pending Figma's review — until it\n // passes the authorize URL errors \"OAuth app doesn't exist\"), so a PAT\n // fallback (`apiKeyFallback`) ships alongside it and works TODAY: the user\n // pastes a Figma personal access token. Same OAuth-primary + PAT-secondary\n // shape as GitHub. Flip of the OAuth review is transparent (bead codeagent-m4ix).\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n // Granular READ-ONLY scopes (legacy `files:read` is deprecated for\n // OAuth). Asset export (GET /v1/images) rides file_content:read.\n // file_variables:read is Enterprise-only and would break linking for\n // normal accounts — deliberately excluded. ⚠️ Changing these requires\n // the user to RE-LINK Figma.\n scopes: [\n 'current_user:read',\n 'file_content:read',\n 'file_metadata:read',\n 'file_dev_resources:read',\n 'library_content:read',\n ],\n // SECONDARY: paste a Figma personal access token instead of OAuth. The\n // PAT uses the `X-Figma-Token` header, so the delivery env var differs\n // from OAuth (`FIGMA_API_KEY` vs `FIGMA_OAUTH_TOKEN`) — hence its own\n // envMapping, used when the credential was linked via PAT.\n apiKeyFallback: {\n fields: [\n {\n key: 'accessToken',\n label: 'Figma personal access token',\n placeholder: 'figd_…',\n secret: true,\n help: 'Figma → Settings → Security → Personal access tokens (read-only scopes).',\n },\n ],\n envMapping: {\n FIGMA_API_KEY: 'accessToken',\n },\n },\n },\n delivery: {\n mcp: {\n // Framelink figma-developer-mcp (Node, stdio) in BYO-token headless\n // mode — the ONLY known Figma MCP server that accepts an OAuth\n // Bearer token: FIGMA_OAUTH_TOKEN → `Authorization: Bearer` (env\n // only, never argv). Figma's official servers can't be used here\n // (remote = interactive OAuth + client allowlist; Dev Mode =\n // desktop app). Version PINNED; bump only after re-verifying\n // headless. Tools: get_figma_data (condensed layout extraction) +\n // download_figma_images (asset export).\n command: 'npx',\n args: ['-y', 'figma-developer-mcp@0.13.2', '--stdio', '--no-telemetry'],\n envMapping: {\n FIGMA_OAUTH_TOKEN: 'accessToken',\n },\n },\n },\n },\n stitch: {\n id: 'stitch',\n name: 'Stitch',\n icon: 'stitch',\n category: 'design',\n // LIVE — api_key: the user pastes a Stitch API key (Google Stitch →\n // Settings → API Key). Stitch's MCP is hosted-only (stitch.googleapis.com)\n // and accepts the key as the `X-Goog-Api-Key` header headless (verified) —\n // no OAuth, no GSM secret. Delivery uses the shim's HTTP transport.\n // (Stitch ALSO supports Google OAuth 2.0, but that's a follow-up — our model\n // is one auth kind per integration and api_key is the simple, headless path.)\n enabled: true,\n auth: {\n kind: 'api_key',\n fields: [\n {\n key: 'accessToken',\n label: 'API Key',\n placeholder: 'Stitch API key',\n secret: true,\n help: 'Google Stitch → profile menu → Stitch Settings → API Key → Create Key.',\n },\n ],\n },\n delivery: {\n mcp: {\n // HTTP transport — the shim relays to Stitch's hosted MCP with the key\n // as the X-Goog-Api-Key header. Key stays server-side of the shim.\n command: '',\n args: [],\n envMapping: {},\n httpUrl: 'https://stitch.googleapis.com/mcp',\n httpHeaders: { 'X-Goog-Api-Key': '{accessToken}' },\n },\n },\n },\n vercel: {\n id: 'vercel',\n name: 'Vercel',\n icon: 'vercel',\n category: 'infrastructure',\n // DARK — OAuth (PKCE public client). The user authorizes our registered\n // Vercel OAuth app; the access token calls Vercel's OFFICIAL hosted remote\n // MCP (mcp.vercel.com, Streamable HTTP) as a Bearer header — the SAME shim\n // HTTP transport as PostHog/Datadog. Vercel MCP is approved-clients + per-\n // connection OAuth consent, but that consent is the ONE-TIME authorize (not\n // per MCP request), so a backend-brokered access token works headless.\n // Endpoints (from mcp.vercel.com/.well-known/oauth-protected-resource →\n // vercel.com/.well-known/oauth-authorization-server): authorize\n // https://vercel.com/oauth/authorize, token\n // https://vercel.com/api/login/oauth/token, PKCE S256, token-endpoint auth\n // 'none' (public). LIVE — the headless Bearer against mcp.vercel.com was\n // verified (2026-07-27).\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n scopes: ['openid', 'offline_access'],\n },\n delivery: {\n mcp: {\n command: '',\n args: [],\n envMapping: {},\n httpUrl: 'https://mcp.vercel.com',\n httpHeaders: { Authorization: 'Bearer {accessToken}' },\n },\n },\n },\n trello: {\n id: 'trello',\n name: 'Trello',\n icon: 'trello',\n category: 'tracker',\n // LIVE — api_key rail (like Azure DevOps). Trello's auth is a developer API\n // key + a user token, NOT an OAuth2 code exchange: its token returns via a\n // URL fragment / OAuth 1.0a, incompatible with our server-side broker. The\n // user pastes both; the backend validates against the Trello REST API and\n // vaults them. No OAuth app, no GSM secrets, no config-gated 503.\n enabled: true,\n auth: {\n kind: 'api_key',\n fields: [\n {\n key: 'apiKey',\n label: 'API Key',\n placeholder: 'Your Trello API key',\n secret: false,\n help: 'Create a Power-Up at https://trello.com/power-ups/admin and copy its API key.',\n },\n {\n key: 'accessToken',\n label: 'Token',\n placeholder: 'Your Trello token',\n secret: true,\n help: 'On the API-key page, click the \"Token\" link to authorize + generate a token (scope read,write, never-expiring).',\n },\n ],\n },\n delivery: {\n mcp: {\n // mcp-server-trello (Node): TRELLO_API_KEY + TRELLO_TOKEN via env only,\n // never argv. Version PINNED; bump only after re-verifying headless.\n command: 'npx',\n args: ['-y', 'mcp-server-trello@1.0.4'],\n envMapping: {\n TRELLO_API_KEY: 'apiKey',\n TRELLO_TOKEN: 'accessToken',\n },\n },\n },\n },\n clickup: {\n id: 'clickup',\n name: 'ClickUp',\n icon: 'clickup',\n category: 'tracker',\n // DARK — OAuth 2.0 (authorization code). ClickUp's token DOES NOT expire and\n // has NO refresh token, and its OAuth has NO granular scopes (the app gets\n // the workspaces the user authorizes). enabled:false until the OAuth app is\n // registered (client_id/secret) + secrets land + Step-8 live-verify.\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n scopes: [],\n },\n delivery: {\n mcp: {\n // @taazkareem/clickup-mcp-server (Node): CLICKUP_API_KEY (the OAuth\n // access token — ClickUp accepts it in the Authorization header) +\n // CLICKUP_TEAM_ID (the workspace id, captured at exchange) via env only.\n // Version PINNED; bump only after re-verifying headless.\n command: 'npx',\n args: ['-y', '@taazkareem/clickup-mcp-server@0.14.4'],\n envMapping: {\n CLICKUP_API_KEY: 'accessToken',\n CLICKUP_TEAM_ID: 'teamId',\n },\n },\n },\n },\n n8n: {\n id: 'n8n',\n name: 'n8n',\n icon: 'n8n',\n category: 'automation',\n // LIVE — api_key rail (like Trello / Azure DevOps). n8n auth is an API key\n // (Settings → n8n API) + the instance URL (n8n.cloud or self-hosted). The\n // user pastes both; the backend validates against the n8n REST API + vaults\n // them. No OAuth app, no GSM secrets, no config-gated 503.\n // ⚠️ The instance MUST be PUBLICLY reachable (n8n.cloud or a public self-host)\n // — a localhost/private n8n can't be reached from the deploy box.\n enabled: true,\n auth: {\n kind: 'api_key',\n fields: [\n {\n key: 'instanceUrl',\n label: 'Instance URL',\n placeholder: 'https://your-workspace.app.n8n.cloud',\n secret: false,\n help: 'Your n8n base URL — n8n.cloud or your public self-hosted instance.',\n },\n {\n key: 'accessToken',\n label: 'API Key',\n placeholder: 'n8n_api_…',\n secret: true,\n help: 'Create in n8n → Settings → n8n API → Create an API key.',\n },\n ],\n },\n delivery: {\n mcp: {\n // n8n-mcp (Node): N8N_API_URL + N8N_API_KEY via env only, never argv.\n // Version PINNED; bump only after re-verifying headless.\n command: 'npx',\n args: ['-y', 'n8n-mcp@2.66.2'],\n envMapping: {\n N8N_API_URL: 'instanceUrl',\n N8N_API_KEY: 'accessToken',\n },\n // stdio transport (the CLI mcp-shim spawns it over stdio, not HTTP).\n staticEnv: { MCP_MODE: 'stdio' },\n },\n },\n },\n postman: {\n id: 'postman',\n name: 'Postman',\n icon: 'postman',\n category: 'api_tools',\n // LIVE — api_key rail. Postman auth is a personal API key (Settings → API\n // keys). The user pastes it; the backend validates via GET /me + vaults it.\n // No OAuth (Postman's API has no user OAuth), no secrets, no config-503.\n enabled: true,\n auth: {\n kind: 'api_key',\n fields: [\n {\n key: 'accessToken',\n label: 'API Key',\n placeholder: 'PMAK-…',\n secret: true,\n help: 'Postman → Settings → API keys → Generate API Key.',\n },\n ],\n },\n delivery: {\n mcp: {\n // Postman's OFFICIAL stdio MCP server (Node). BYO key via POSTMAN_API_KEY\n // (env only). Version PINNED; bump only after re-verifying headless.\n command: 'npx',\n args: ['-y', '@postman/postman-mcp-server@2.11.2'],\n envMapping: {\n POSTMAN_API_KEY: 'accessToken',\n },\n },\n },\n },\n mixpanel: {\n id: 'mixpanel',\n name: 'Mixpanel',\n icon: 'mixpanel',\n category: 'analytics',\n // LIVE — api_key rail. Mixpanel query/export auth is a SERVICE ACCOUNT\n // (username + password, Basic auth) scoped to a project (project id). The\n // user pastes all three; the backend validates + vaults them. No OAuth\n // redirect (service accounts are machine-to-machine), no GSM secrets.\n enabled: true,\n auth: {\n kind: 'api_key',\n fields: [\n {\n key: 'accessToken',\n label: 'Service Account Username',\n placeholder: 'your-sa.mp-service-account',\n secret: false,\n help: 'Mixpanel → Organization Settings → Service Accounts → create one with project access.',\n },\n {\n key: 'secretKey',\n label: 'Service Account Secret',\n placeholder: 'the service account secret',\n secret: true,\n help: 'Shown once when you create the service account — copy it then.',\n },\n {\n key: 'projectId',\n label: 'Project ID',\n placeholder: '1234567',\n secret: false,\n help: 'Mixpanel → Project Settings → Project ID.',\n },\n ],\n },\n delivery: {\n mcp: {\n // @mendeel/mcp-mixpanel (Node): the service account + project id via env\n // only (MIXPANEL_SERVICE_ACCOUNT_USERNAME/PASSWORD + MIXPANEL_PROJECT_ID).\n // Version PINNED; bump only after re-verifying headless. Default region US.\n command: 'npx',\n args: ['-y', '@mendeel/mcp-mixpanel@1.0.4'],\n envMapping: {\n MIXPANEL_SERVICE_ACCOUNT_USERNAME: 'accessToken',\n MIXPANEL_SERVICE_ACCOUNT_PASSWORD: 'secretKey',\n MIXPANEL_PROJECT_ID: 'projectId',\n },\n },\n },\n },\n supabase: {\n id: 'supabase',\n name: 'Supabase',\n icon: 'supabase',\n category: 'database',\n // LIVE — OAuth 2.0 (authorization code, Confidential). authorize\n // api.supabase.com/v1/oauth/authorize; token /v1/oauth/token\n // (form-urlencoded, client creds via HTTP Basic); rotating refresh tokens.\n // The OAuth app (Organization → OAuth Apps) is registered and\n // SUPABASE_OAUTH_CLIENT_ID/SECRET/REDIRECT_URI are in Secret Manager\n // (prod+dev); the config-gated 503 keeps this safe mid-rollout.\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n // ⚠️ Supabase scopes are configured ON THE OAUTH APP, NOT sent in the\n // authorize URL — this list is informational (what the app was granted).\n scopes: ['all'],\n },\n delivery: {\n mcp: {\n // Supabase's OFFICIAL MCP server (Node). The OAuth access token IS a\n // Management API token → fed via SUPABASE_ACCESS_TOKEN (env only, never\n // argv). Version PINNED; bump only after re-verifying headless.\n command: 'npx',\n args: ['-y', '@supabase/mcp-server-supabase@0.9.0'],\n envMapping: {\n SUPABASE_ACCESS_TOKEN: 'accessToken',\n },\n },\n },\n },\n convex: {\n id: 'convex',\n name: 'Convex',\n icon: 'convex',\n category: 'database',\n // LIVE — api_key rail (the user pastes a Convex DEPLOY KEY).\n // ⚠️ Convex's OAuth application token was tried first and DOES NOT WORK with\n // the MCP: it's a narrow Management-API credential — verified live it fails\n // `convex mcp start` as BOTH CONVEX_DEPLOY_KEY and CONVEX_OVERRIDE_ACCESS_TOKEN,\n // and can't even enumerate the team's deployments (\"requires a Personal\n // Access Token\"). The Convex MCP's DOCUMENTED headless credential is a\n // DEPLOY KEY (CONVEX_DEPLOY_KEY=dev:…|… / prod:… / project:…), so the user\n // pastes one (Convex Dashboard → Project Settings → Deploy Keys), exactly\n // like Azure DevOps / Datadog / Trello. No OAuth app, no GSM secret.\n enabled: true,\n auth: {\n kind: 'api_key',\n fields: [\n {\n key: 'accessToken',\n label: 'Convex deploy key',\n placeholder: 'prod:happy-animal-123|eyJ2…',\n secret: true,\n help: 'Convex Dashboard → Project Settings → Deploy Keys → Generate a deploy key.',\n },\n ],\n },\n delivery: {\n mcp: {\n // Convex's OFFICIAL MCP server ships inside the `convex` npm package\n // (`convex mcp start`). The pasted deploy key is fed via CONVEX_DEPLOY_KEY\n // (env only, never argv) — its documented non-interactive credential.\n // Version PINNED; bump only after re-verifying headless.\n command: 'npx',\n args: ['-y', 'convex@1.42.3', 'mcp', 'start'],\n envMapping: {\n CONVEX_DEPLOY_KEY: 'accessToken',\n },\n },\n },\n },\n confluence: {\n id: 'confluence',\n name: 'Confluence',\n icon: 'confluence',\n category: 'docs',\n // Confluence is served by the SAME Atlassian OAuth + the SAME `mcp-atlassian`\n // server as Jira (the `jira` entry already requests Confluence scopes). This\n // entry is a DISPLAY ALIAS so Confluence is explicit in the Knowledge tab: it\n // BORROWS jira's vaulted credential (`aliasOf: 'jira'`) and has no link flow of\n // its own — connecting Atlassian (the Jira/Atlassian card) lights it up, and\n // the backend resolves its token by delegating to `jira`.\n enabled: true,\n auth: { kind: 'derived', aliasOf: 'jira' },\n delivery: {\n mcp: {\n // Same mcp-atlassian server + BYO-token env as Jira — it exposes the\n // Confluence tools when the deploy selects Confluence without Jira. If\n // BOTH are selected the deploy manifest dedupes identical MCP specs.\n command: 'uvx',\n args: ['mcp-atlassian==0.22.1'],\n envMapping: {\n ATLASSIAN_OAUTH_ACCESS_TOKEN: 'accessToken',\n ATLASSIAN_OAUTH_CLOUD_ID: 'cloudId',\n },\n staticEnv: { ATLASSIAN_OAUTH_ENABLE: 'true' },\n },\n },\n },\n cloudflare: {\n id: 'cloudflare',\n name: 'Cloudflare',\n icon: 'cloudflare',\n category: 'infrastructure',\n // LIVE — Cloudflare self-managed OAuth (dash.cloudflare.com OAuth server,\n // the same one Wrangler uses). Confidential (Client Secret POST). The agent\n // inspects/deploys Workers, Pages, DNS, KV, R2 + reads analytics/logs on the\n // user's account. CLOUDFLARE_OAUTH_CLIENT_ID/SECRET/REDIRECT_URI in Secret\n // Manager (prod+dev); the backend CloudflareOAuthProvider is config-gated (503).\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n // Cloudflare OAuth scope IDs == API-token permission-group names (dot\n // form, e.g. `workers-scripts.write`). ⚠️ MUST exactly match the scopes\n // configured on the OAuth client (b2b4b87e…). `offline_access` → refresh.\n scopes: [\n // Developer Platform (Workers / Pages / KV / R2 / D1 / observability)\n 'd1.read',\n 'page.read',\n 'page.write',\n 'workers-ci.read',\n 'workers-ci.write',\n 'containers.read',\n 'containers.write',\n 'workers-kv-storage.read',\n 'workers-kv-storage.write',\n 'workers-observability.read',\n 'workers-observability-telemetry.write',\n 'workers-observability.write',\n 'r2-catalog.read',\n 'r2-catalog.write',\n 'r2-catalog-sql.read',\n 'workers-r2-bucket-item.read',\n 'workers-r2-bucket-item.write',\n 'workers-r2.read',\n 'workers-r2.write',\n 'workers-routes.read',\n 'workers-routes.write',\n 'workers-scripts.read',\n 'workers-scripts.write',\n 'workers-tail.read',\n // DNS & Zones\n 'account-dns-settings.read',\n 'account-dns-settings.write',\n 'dns-firewall.read',\n 'dns-firewall.write',\n 'dns.read',\n 'dns-view.read',\n 'dns.write',\n 'registrar-domains.admin',\n 'registrar-sandbox-domains.admin',\n 'zone-dns-settings.read',\n 'zone-dns-settings.write',\n 'zone.read',\n 'zone-settings.read',\n 'zone-settings.write',\n // Analytics & Logs\n 'account-analytics.read',\n 'analytics.read',\n 'account-logs.read',\n 'logs.read',\n 'radar.read',\n // Account & Billing\n 'account-api-gateway.read',\n 'account-settings.read',\n 'memberships.read',\n 'notifications.read',\n 'user-details.read',\n 'offline_access',\n ],\n },\n delivery: {\n mcp: {\n // Headless stdio Cloudflare MCP (Cloudflare API v4) — BYO token via env,\n // the OAuth access token works as a Bearer against api.cloudflare.com.\n // The account id is the discriminator for account-level ops. PINNED.\n command: 'npx',\n args: ['-y', '@itunified.io/mcp-cloudflare@2026.4.10-1'],\n envMapping: {\n CLOUDFLARE_API_TOKEN: 'accessToken',\n CLOUDFLARE_ACCOUNT_ID: 'accountId',\n },\n },\n },\n },\n};\n\nexport function getEnabledIntegrations(): IntegrationDefinition[] {\n return Object.values(INTEGRATION_REGISTRY).filter((m) => m.enabled);\n}\n\nexport function getIntegration(id: IntegrationId): IntegrationDefinition {\n const meta = INTEGRATION_REGISTRY[id];\n if (!meta) throw new Error(`Unknown integration id: ${id}`);\n return meta;\n}\n\nexport function isKnownIntegrationId(id: string): id is IntegrationId {\n return id in INTEGRATION_REGISTRY;\n}\n\nexport function getIntegrationsByCategory(\n category: IntegrationCategory,\n): IntegrationDefinition[] {\n return Object.values(INTEGRATION_REGISTRY).filter(\n (m) => m.category === category && m.enabled,\n );\n}\n","/**\n * Agent Toolkits — centralized integration branding catalog.\n * Spec: docs/superpowers/specs/2026-07-10-agent-toolkits-integrations-design.md\n *\n * Shared is pure TS (no React, no platform imports), so this catalog is DATA:\n * raw SVG markup strings + display metadata. Renderers stay per-app (RN\n * `SvgXml` on mobile, inline/`<img>` on web) — this module never renders\n * anything itself.\n *\n * `logoSvg` values are the OFFICIAL brand marks. jira/slack are the\n * multicolor originals (from the vendor). Every other entry (the 6 live\n * integrations' single-path marks + the whole COMING SOON set) is a\n * simple-icons single-path mark that ships with a black fill by default —\n * that fill has been rewritten here to #FFFFFF so the mark reads on the\n * dark surfaces this catalog targets; consumers may re-tint via\n * `brandColor` (e.g. an SVG `<mask>`/currentColor wrapper) if a different\n * treatment is needed. `pendo` + `amplitude` are NOT in simple-icons\n * (brand-guideline restrictions) so they carry faithful hand-authored\n * monochrome marks in the same 24×24 single-path shape.\n */\nexport interface IntegrationBranding {\n /** Stable id — registry ids ('jira') plus upcoming ones not yet in IntegrationId. */\n id: string;\n name: string;\n vendor: string;\n /** One-line value prop shown under the name. */\n tagline: string;\n /** Brand accent for tinted containers/pills on dark surfaces. */\n brandColor: string;\n /** Official logo as raw SVG markup (renderers: SvgXml on RN, inline/img on web). */\n logoSvg: string;\n}\n\nexport const INTEGRATION_BRANDING: Record<string, IntegrationBranding> = {\n // GitLab — the official multicolour Tanuki (from GitLab's own header markup),\n // like jira/slack. Kept verbatim: the four paths are the shape + the two\n // cheeks + the chin, and flattening them to one colour loses the mark.\n // `aria-hidden`/`role`/`class` were stripped — the renderers own a11y.\n gitlab: {\n id: 'gitlab',\n name: 'GitLab',\n vendor: 'GitLab',\n tagline: 'Merge requests, reviews & CI',\n brandColor: '#FC6D26',\n logoSvg:\n '<svg width=\"25\" height=\"24\" viewBox=\"0 0 25 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m24.507 9.5-.034-.09L21.082.562a.896.896 0 0 0-1.694.091l-2.29 7.01H7.825L5.535.653a.898.898 0 0 0-1.694-.09L.451 9.411.416 9.5a6.297 6.297 0 0 0 2.09 7.278l.012.01.03.022 5.16 3.867 2.56 1.935 1.554 1.176a1.051 1.051 0 0 0 1.268 0l1.555-1.176 2.56-1.935 5.197-3.89.014-.01A6.297 6.297 0 0 0 24.507 9.5Z\" fill=\"#E24329\"/><path d=\"m24.507 9.5-.034-.09a11.44 11.44 0 0 0-4.56 2.051l-7.447 5.632 4.742 3.584 5.197-3.89.014-.01A6.297 6.297 0 0 0 24.507 9.5Z\" fill=\"#FC6D26\"/><path d=\"m7.707 20.677 2.56 1.935 1.555 1.176a1.051 1.051 0 0 0 1.268 0l1.555-1.176 2.56-1.935-4.743-3.584-4.755 3.584Z\" fill=\"#FCA326\"/><path d=\"M5.01 11.461a11.43 11.43 0 0 0-4.56-2.05L.416 9.5a6.297 6.297 0 0 0 2.09 7.278l.012.01.03.022 5.16 3.867 4.745-3.584-7.444-5.632Z\" fill=\"#FC6D26\"/></svg>',\n },\n // GitHub — now a REAL `IntegrationId` (`version_control`, `kind: 'connection'`).\n // It started life here as a brand-only entry, back when GitHub was rendered by\n // a hand-written special-case row; the mark is unchanged, it's just also the\n // catalog row's logo now. Still used by the PR/MR Command Center surfaces.\n github: {\n id: 'github',\n name: 'GitHub',\n vendor: 'GitHub',\n tagline: 'Pull requests, reviews & merges',\n brandColor: '#FFFFFF',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>GitHub</title><path fill=\"#FFFFFF\" d=\"M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\"/></svg>',\n },\n // GitHub Issues — the `tracker`-category toolkit integration (a real\n // `IntegrationId`, unlike the `github` entry above). Same official mark, its\n // own name/tagline so the catalog row reads as the issue tracker rather than\n // the code host.\n github_issues: {\n id: 'github_issues',\n name: 'GitHub Issues',\n vendor: 'GitHub',\n tagline: 'Issues & project tracking',\n brandColor: '#FFFFFF',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>GitHub</title><path fill=\"#FFFFFF\" d=\"M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\"/></svg>',\n },\n // npm — a brand-only entry (NOT an `IntegrationId`): the registry the\n // codeam-cli ships to. Present so surfaces like the Wiki can render the\n // official npm mark from the ONE shared catalog instead of a loose asset.\n npm: {\n id: 'npm',\n name: 'npm',\n vendor: 'npm, Inc.',\n tagline: 'The Node package registry',\n brandColor: '#CB3837',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>npm</title><path fill=\"#CB3837\" d=\"M1.763 0C.786 0 0 .786 0 1.763v20.474C0 23.214.786 24 1.763 24h20.474c.977 0 1.763-.786 1.763-1.763V1.763C24 .786 23.214 0 22.237 0zM5.13 5.323l13.837.019-.009 13.836h-3.464l.01-10.382h-3.456L12.08 19.17H5.113z\"/></svg>',\n },\n jira: {\n // Branding key kept 'jira' for id-stability; DISPLAY rebranded to Atlassian\n // (the one integration fronts both Jira + Confluence via mcp-atlassian).\n id: 'jira',\n name: 'Atlassian',\n vendor: 'Atlassian',\n tagline: 'Jira · Confluence',\n brandColor: '#357DE8',\n logoSvg:\n '<svg viewBox=\"0 0 32 32\" height=\"32\" xmlns=\"http://www.w3.org/2000/svg\" focusable=\"false\" aria-hidden=\"true\"><defs><linearGradient id=\"uid18\" x1=\"14.8402\" y1=\"15.8324\" x2=\"8.6599\" y2=\"26.5369\" gradientUnits=\"userSpaceOnUse\"><stop stop-color=\"#2684FF\" stop-opacity=\"0.4\" offset=\"0%\"></stop><stop stop-color=\"#2684FF\" offset=\"0.9228\"></stop></linearGradient></defs><path fill=\"url(#uid18)\" d=\"M11.6397 14.0398C11.2789 13.643 10.7378 13.679 10.4852 14.148L4.64091 25.8728C4.42446 26.3418 4.74912 26.8829 5.25419 26.8829H13.4074C13.6599 26.8829 13.9125 26.7386 14.0207 26.4861C15.7885 22.8424 14.7061 17.3227 11.6397 14.0398Z\"></path><path fill=\"#357DE8\" d=\"M15.9343 3.36124C12.6513 8.55622 12.8678 14.2923 15.0324 18.6215C17.1969 22.9506 18.8565 26.2336 18.9647 26.4861C19.0729 26.7386 19.3254 26.8829 19.578 26.8829H27.7312C28.2363 26.8829 28.597 26.3418 28.3445 25.8728C28.3445 25.8728 17.3774 3.93846 17.0887 3.39732C16.8723 2.89225 16.259 2.85618 15.9343 3.36124Z\"></path></svg>',\n },\n confluence: {\n id: 'confluence',\n name: 'Confluence',\n vendor: 'Atlassian',\n tagline: 'Part of Atlassian · docs & wiki',\n brandColor: '#1868DB',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Confluence</title><path fill=\"#FFFFFF\" d=\"M.87 18.257c-.248.382-.53.875-.763 1.245a.764.764 0 0 0 .255 1.04l4.965 3.054a.764.764 0 0 0 1.058-.26c.199-.332.454-.763.733-1.221 1.967-3.247 3.945-2.853 7.508-1.146l4.957 2.337a.764.764 0 0 0 1.028-.382l2.364-5.346a.764.764 0 0 0-.382-1c-1.048-.494-3.124-1.478-4.965-2.361C10.911 10.97 5.224 11.185.87 18.257zM23.131 5.743c.249-.405.531-.875.764-1.25a.764.764 0 0 0-.256-1.034L18.675.404a.764.764 0 0 0-1.058.26c-.195.335-.451.763-.734 1.225-1.966 3.246-3.945 2.85-7.508 1.146L4.437.694a.764.764 0 0 0-1.027.382L1.046 6.422a.764.764 0 0 0 .382 1c1.039.49 3.105 1.467 4.965 2.361 6.698 3.246 12.392 3.029 16.738-4.04z\"/></svg>',\n },\n slack: {\n id: 'slack',\n name: 'Slack',\n vendor: 'Salesforce',\n tagline: 'Team messaging & alerts',\n brandColor: '#E01E5A',\n logoSvg:\n '<svg width=\"54\" height=\"54\" viewBox=\"0 0 54 54\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><g clip-path=\"url(#clip0_4127_70105)\"><path d=\"M11.379 33.9993C11.379 37.1358 8.84512 39.6507 5.7276 39.6507C2.61008 39.6507 0.0572205 37.1168 0.0572205 33.9993C0.0572205 30.8817 2.5911 28.3479 5.70862 28.3479H11.36V33.9993H11.379Z\" fill=\"#E01E5A\"/><path d=\"M14.1962 33.9997C14.1962 30.8632 16.7301 28.3483 19.8476 28.3483C22.9651 28.3483 25.499 30.8822 25.499 33.9997V48.1353C25.499 51.2718 22.9651 53.7867 19.8476 53.7867C16.7301 53.7867 14.1962 51.2718 14.1962 48.1353V33.9997Z\" fill=\"#E01E5A\"/><path d=\"M19.8662 11.2673C16.7296 11.2673 14.2148 8.73347 14.2148 5.61594C14.2148 2.49842 16.7486 -0.0354538 19.8662 -0.0354538C22.9837 -0.0354538 25.5175 2.49842 25.5175 5.61594V11.2673H19.8662Z\" fill=\"#36C5F0\"/><path d=\"M19.8682 14.1334C23.0047 14.1334 25.5196 16.6673 25.5196 19.7848C25.5196 22.9023 22.9857 25.4362 19.8682 25.4362H5.67566C2.53916 25.4362 0.0242615 22.9023 0.0242615 19.7848C0.0242615 16.6673 2.55814 14.1334 5.67566 14.1334H19.8682Z\" fill=\"#36C5F0\"/><path d=\"M42.5323 19.7853C42.5323 16.6488 45.0662 14.1339 48.1837 14.1339C51.3012 14.1339 53.8351 16.6678 53.8351 19.7853C53.8351 22.9028 51.3012 25.4367 48.1837 25.4367H42.5323V19.7853Z\" fill=\"#2EB67D\"/><path d=\"M39.7126 19.7934C39.7126 22.9299 37.1787 25.4448 34.0612 25.4448C30.9436 25.4448 28.4098 22.911 28.4098 19.7934V5.61986C28.4098 2.48336 30.9436 -0.0315399 34.0612 -0.0315399C37.1787 -0.0315399 39.7126 2.48336 39.7126 5.61986V19.7934Z\" fill=\"#2EB67D\"/><path d=\"M34.0376 42.482C37.1741 42.482 39.689 45.0158 39.689 48.1334C39.689 51.2509 37.1552 53.7848 34.0376 53.7848C30.9201 53.7848 28.3862 51.2509 28.3862 48.1334V42.482H34.0376Z\" fill=\"#ECB22E\"/><path d=\"M34.0381 39.6507C30.9016 39.6507 28.3867 37.1168 28.3867 33.9993C28.3867 30.8818 30.9206 28.3479 34.0381 28.3479H48.2306C51.3671 28.3479 53.882 30.8818 53.882 33.9993C53.882 37.1168 51.3482 39.6507 48.2306 39.6507H34.0381Z\" fill=\"#ECB22E\"/></g><defs><clipPath id=\"clip0_4127_70105\"><rect width=\"54\" height=\"54\" fill=\"white\"/></clipPath></defs></svg>',\n },\n microsoft_teams: {\n id: 'microsoft_teams',\n name: 'Microsoft Teams',\n vendor: 'Microsoft',\n tagline: 'Team chat & collaboration',\n brandColor: '#6264A7',\n logoSvg:\n '<svg width=\"54\" height=\"54\" viewBox=\"0 0 54 54\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M36.6 22h12.3c1 0 1.8.8 1.8 1.8v10.4a7.2 7.2 0 0 1-7.2 7.2 7.2 7.2 0 0 1-7.2-7.2V22z\" fill=\"#5059C9\"/><circle cx=\"44\" cy=\"14.4\" r=\"4.6\" fill=\"#5059C9\"/><circle cx=\"27.2\" cy=\"12\" r=\"6.6\" fill=\"#7B83EB\"/><path d=\"M35.4 22H16.9c-1 .02-1.8.86-1.78 1.86v11.9A12 12 0 0 0 26.9 47.6a12 12 0 0 0 10.28-11.84V23.86c.02-1-.78-1.84-1.78-1.86z\" fill=\"#7B83EB\"/><path opacity=\".12\" d=\"M28 22v18.4a1.86 1.86 0 0 1-1.72 1.84H15.72A12.7 12.7 0 0 1 15.12 38V23.86c-.02-1 .78-1.84 1.78-1.86H28z\" fill=\"#000\"/><rect x=\"2.5\" y=\"15\" width=\"23.5\" height=\"23.5\" rx=\"2.2\" fill=\"#4B53BC\"/><path d=\"M19.8 21.4H8.7v3.05h4v11.1h3.1v-11.1h4V21.4z\" fill=\"#fff\"/></svg>',\n },\n google_chat: {\n id: 'google_chat',\n name: 'Google Chat',\n vendor: 'Google',\n tagline: 'Team messaging & spaces',\n brandColor: '#00AC47',\n logoSvg:\n '<svg width=\"54\" height=\"54\" viewBox=\"0 0 54 54\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M46 6H8a3.5 3.5 0 0 0-3.5 3.5v26A3.5 3.5 0 0 0 8 39h4.5v8.2a1.3 1.3 0 0 0 2.15 1L26 39h20a3.5 3.5 0 0 0 3.5-3.5v-26A3.5 3.5 0 0 0 46 6z\" fill=\"#00AC47\"/><circle cx=\"19.5\" cy=\"22.5\" r=\"3.1\" fill=\"#fff\"/><circle cx=\"34.5\" cy=\"22.5\" r=\"3.1\" fill=\"#fff\"/></svg>',\n },\n discord: {\n id: 'discord',\n name: 'Discord',\n vendor: 'Discord',\n tagline: 'Voice, video & text chat',\n brandColor: '#5865F2',\n logoSvg:\n '<svg width=\"54\" height=\"54\" viewBox=\"0 0 54 54\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M43.6 12.2A38 38 0 0 0 34.1 9.3a26 26 0 0 0-1.2 2.5 35.3 35.3 0 0 0-10.6 0 26 26 0 0 0-1.2-2.5 38 38 0 0 0-9.5 2.9C4.6 21.2 3 30 3.8 38.6a38.4 38.4 0 0 0 11.6 5.9 28 28 0 0 0 2.5-4 24.8 24.8 0 0 1-3.9-1.9c.33-.24.65-.5.95-.75a27.5 27.5 0 0 0 23.5 0c.3.27.62.52.95.75a24.8 24.8 0 0 1-3.9 1.9 28 28 0 0 0 2.5 4 38.3 38.3 0 0 0 11.6-5.9c.94-9.9-1.6-18.6-6.6-26.4zM19.4 33.3c-2.3 0-4.2-2.1-4.2-4.7s1.85-4.7 4.2-4.7 4.24 2.13 4.2 4.7c0 2.6-1.87 4.7-4.2 4.7zm15.3 0c-2.3 0-4.2-2.1-4.2-4.7s1.85-4.7 4.2-4.7 4.24 2.13 4.2 4.7c0 2.6-1.85 4.7-4.2 4.7z\" fill=\"#5865F2\"/></svg>',\n },\n linear: {\n id: 'linear',\n name: 'Linear',\n vendor: 'Linear',\n tagline: 'Issue tracking for product teams',\n brandColor: '#5E6AD2',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Linear</title><path fill=\"#FFFFFF\" d=\"M2.886 4.18A11.982 11.982 0 0 1 11.99 0C18.624 0 24 5.376 24 12.009c0 3.64-1.62 6.903-4.18 9.105L2.887 4.18ZM1.817 5.626l16.556 16.556c-.524.33-1.075.62-1.65.866L.951 7.277c.247-.575.537-1.126.866-1.65ZM.322 9.163l14.515 14.515c-.71.172-1.443.282-2.195.322L0 11.358a12 12 0 0 1 .322-2.195Zm-.17 4.862 9.823 9.824a12.02 12.02 0 0 1-9.824-9.824Z\"/></svg>',\n },\n sentry: {\n id: 'sentry',\n name: 'Sentry',\n vendor: 'Sentry',\n tagline: 'Error & performance monitoring',\n brandColor: '#7B68C7',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 50 44\" xmlns=\"http://www.w3.org/2000/svg\"><title>Sentry</title><path fill=\"#FFFFFF\" d=\"M29,2.26a4.67,4.67,0,0,0-8,0L14.42,13.53A32.21,32.21,0,0,1,32.17,40.19H27.55A27.68,27.68,0,0,0,12.09,17.47L6,28a15.92,15.92,0,0,1,9.23,12.17H4.62A.76.76,0,0,1,4,39.06l2.94-5a10.74,10.74,0,0,0-3.36-1.9l-2.91,5a4.54,4.54,0,0,0,1.69,6.24A4.66,4.66,0,0,0,4.62,44H19.15a19.4,19.4,0,0,0-8-17.31l2.31-4A23.87,23.87,0,0,1,23.76,44H36.07a35.88,35.88,0,0,0-16.41-31.8l4.67-8a.77.77,0,0,1,1.05-.27c.53.29,20.29,34.77,20.66,35.17a.76.76,0,0,1-.68,1.13H40.6q.09,1.91,0,3.81h4.78A4.59,4.59,0,0,0,50,39.43a4.49,4.49,0,0,0-.62-2.28Z\"></path></svg>',\n },\n notion: {\n id: 'notion',\n name: 'Notion',\n vendor: 'Notion Labs',\n tagline: 'Docs, wikis & knowledge',\n brandColor: '#E8E7E4',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Notion</title><path fill=\"#FFFFFF\" d=\"M4.459 4.208c.746.606 1.026.56 2.428.466l13.215-.793c.28 0 .047-.28-.046-.326L17.86 1.968c-.42-.326-.981-.7-2.055-.607L3.01 2.295c-.466.046-.56.28-.374.466zm.793 3.08v13.904c0 .747.373 1.027 1.214.98l14.523-.84c.841-.046.935-.56.935-1.167V6.354c0-.606-.233-.933-.748-.887l-15.177.887c-.56.047-.747.327-.747.933zm14.337.745c.093.42 0 .84-.42.888l-.7.14v10.264c-.608.327-1.168.514-1.635.514-.748 0-.935-.234-1.495-.933l-4.577-7.186v6.952L12.21 19s0 .84-1.168.84l-3.222.186c-.093-.186 0-.653.327-.746l.84-.233V9.854L7.822 9.76c-.094-.42.14-1.026.793-1.073l3.456-.233 4.764 7.279v-6.44l-1.215-.139c-.093-.514.28-.887.747-.933zM1.936 1.035l13.31-.98c1.634-.14 2.055-.047 3.082.7l4.249 2.986c.7.513.934.653.934 1.213v16.378c0 1.026-.373 1.634-1.68 1.726l-15.458.934c-.98.047-1.448-.093-1.962-.747l-3.129-4.06c-.56-.747-.793-1.306-.793-1.96V2.667c0-.839.374-1.54 1.447-1.632z\"/></svg>',\n },\n azure_devops: {\n id: 'azure_devops',\n name: 'Azure DevOps',\n vendor: 'Microsoft',\n tagline: 'Boards, Repos & Pipelines',\n brandColor: '#0078D7',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Azure DevOps</title><path fill=\"#FFFFFF\" d=\"M0 8.877L2.247 5.91l8.405-3.416V.022l7.37 5.393L2.966 8.338v8.225L0 15.707zm24-4.45v14.651l-5.753 4.9-9.303-3.057v3.056l-5.978-7.416 15.057 1.798V5.415z\"/></svg>',\n },\n gmail: {\n id: 'gmail',\n name: 'Gmail',\n vendor: 'Google',\n tagline: 'Read, search & send email',\n brandColor: '#EA4335',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Gmail</title><path fill=\"#FFFFFF\" d=\"M24 5.457v13.909c0 .904-.732 1.636-1.636 1.636h-3.819V11.73L12 16.64l-6.545-4.91v9.273H1.636A1.636 1.636 0 0 1 0 19.366V5.457c0-2.023 2.309-3.178 3.927-1.964L5.455 4.64 12 9.548l6.545-4.91 1.528-1.145C21.69 2.28 24 3.434 24 5.457z\"/></svg>',\n },\n posthog: {\n id: 'posthog',\n name: 'PostHog',\n vendor: 'PostHog',\n tagline: 'Product analytics & feature flags',\n brandColor: '#1D4AFF',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>PostHog</title><path fill=\"#FFFFFF\" d=\"M9.854 14.5 5 9.647.854 5.5A.5.5 0 0 0 0 5.854V8.44a.5.5 0 0 0 .146.353L5 13.647l.147.146L9.854 18.5l.146.147v-.049c.065.03.134.049.207.049h2.586a.5.5 0 0 0 .353-.854L9.854 14.5zm0-5-4-4a.487.487 0 0 0-.409-.144.515.515 0 0 0-.356.21.493.493 0 0 0-.089.288V8.44a.5.5 0 0 0 .147.353l9 9a.5.5 0 0 0 .853-.354v-2.585a.5.5 0 0 0-.146-.354l-5-5zm1-4a.5.5 0 0 0-.854.354V8.44a.5.5 0 0 0 .147.353l4 4a.5.5 0 0 0 .853-.354V9.854a.5.5 0 0 0-.146-.354l-4-4zm12.647 11.515a3.863 3.863 0 0 1-2.232-1.1l-4.708-4.707a.5.5 0 0 0-.854.354v6.585a.5.5 0 0 0 .5.5H23.5a.5.5 0 0 0 .5-.5v-.6c0-.276-.225-.497-.499-.532zm-5.394.032a.8.8 0 1 1 0-1.6.8.8 0 0 1 0 1.6zM.854 15.5a.5.5 0 0 0-.854.354v2.293a.5.5 0 0 0 .5.5h2.293c.222 0 .39-.135.462-.309a.493.493 0 0 0-.109-.545L.854 15.501zM5 14.647.854 10.5a.5.5 0 0 0-.854.353v2.586a.5.5 0 0 0 .146.353L4.854 18.5l.146.147h2.793a.5.5 0 0 0 .353-.854L5 14.647z\"/></svg>',\n },\n clickup: {\n id: 'clickup',\n name: 'ClickUp',\n vendor: 'ClickUp',\n tagline: 'Tasks, docs & project management',\n brandColor: '#7B68EE',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>ClickUp</title><path fill=\"#FFFFFF\" d=\"M2 18.439l3.69-2.828c1.961 2.56 4.044 3.739 6.363 3.739 2.307 0 4.33-1.166 6.203-3.704L22 18.405C19.298 22.065 15.941 24 12.053 24 8.178 24 4.788 22.078 2 18.439zM12.04 6.15l-6.568 5.66-3.036-3.52L12.055 0l9.543 8.296-3.05 3.509z\"/></svg>',\n },\n figma: {\n id: 'figma',\n name: 'Figma',\n vendor: 'Figma',\n tagline: 'Designs, files & comments',\n brandColor: '#F24E1E',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Figma</title><path fill=\"#FFFFFF\" d=\"M15.852 8.981h-4.588V0h4.588c2.476 0 4.49 2.014 4.49 4.49s-2.014 4.491-4.49 4.491zM12.735 7.51h3.117c1.665 0 3.019-1.355 3.019-3.019s-1.355-3.019-3.019-3.019h-3.117V7.51zm0 1.471H8.148c-2.476 0-4.49-2.014-4.49-4.49S5.672 0 8.148 0h4.588v8.981zm-4.587-7.51c-1.665 0-3.019 1.355-3.019 3.019s1.354 3.02 3.019 3.02h3.117V1.471H8.148zm4.587 15.019H8.148c-2.476 0-4.49-2.014-4.49-4.49s2.014-4.49 4.49-4.49h4.588v8.98zM8.148 8.981c-1.665 0-3.019 1.355-3.019 3.019s1.355 3.019 3.019 3.019h3.117V8.981H8.148zM8.172 24c-2.489 0-4.515-2.014-4.515-4.49s2.014-4.49 4.49-4.49h4.588v4.441c0 2.503-2.047 4.539-4.563 4.539zm-.024-7.51a3.023 3.023 0 0 0-3.019 3.019c0 1.665 1.365 3.019 3.044 3.019 1.705 0 3.093-1.376 3.093-3.068v-2.97H8.148zm7.704 0h-.098c-2.476 0-4.49-2.014-4.49-4.49s2.014-4.49 4.49-4.49h.098c2.476 0 4.49 2.014 4.49 4.49s-2.014 4.49-4.49 4.49zm-.097-7.509c-1.665 0-3.019 1.355-3.019 3.019s1.355 3.019 3.019 3.019h.098c1.665 0 3.019-1.355 3.019-3.019s-1.355-3.019-3.019-3.019h-.098z\"/></svg>',\n },\n stitch: {\n id: 'stitch',\n name: 'Stitch',\n vendor: 'Google',\n tagline: 'AI UI design → code',\n brandColor: '#A855F7',\n // A simple 4-point design \"sparkle\" mark (Google Stitch has no simple-icons\n // entry) — white on dark, re-tintable via brandColor like the other marks.\n logoSvg:\n '<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path fill=\"#FFFFFF\" d=\"M12 1.5l1.9 6.3a4 4 0 0 0 2.3 2.3l6.3 1.9-6.3 1.9a4 4 0 0 0-2.3 2.3L12 22.5l-1.9-6.3a4 4 0 0 0-2.3-2.3L1.5 12l6.3-1.9a4 4 0 0 0 2.3-2.3L12 1.5z\"/></svg>',\n },\n trello: {\n id: 'trello',\n name: 'Trello',\n vendor: 'Atlassian',\n tagline: 'Boards, lists & cards',\n brandColor: '#0052CC',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Trello</title><path fill=\"#FFFFFF\" d=\"M21.147 0H2.853A2.86 2.86 0 000 2.853v18.294A2.86 2.86 0 002.853 24h18.294A2.86 2.86 0 0024 21.147V2.853A2.86 2.86 0 0021.147 0zM10.34 17.287a.953.953 0 01-.953.953h-4a.954.954 0 01-.954-.953V5.38a.953.953 0 01.954-.953h4a.954.954 0 01.953.953zm9.233-5.467a.944.944 0 01-.953.947h-4a.947.947 0 01-.953-.947V5.38a.953.953 0 01.953-.953h4a.954.954 0 01.953.953z\"/></svg>',\n },\n resend: {\n id: 'resend',\n name: 'Resend',\n vendor: 'Resend',\n tagline: 'Transactional email delivery',\n brandColor: '#FFFFFF',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Resend</title><path fill=\"#FFFFFF\" d=\"M14.679 0c4.648 0 7.413 2.765 7.413 6.434s-2.765 6.434-7.413 6.434H12.33L24 24h-8.245l-8.88-8.44c-.636-.588-.93-1.273-.93-1.86 0-.831.587-1.565 1.713-1.883l4.574-1.224c1.737-.465 2.936-1.81 2.936-3.572 0-2.153-1.761-3.4-3.939-3.4H0V0z\"/></svg>',\n },\n vercel: {\n id: 'vercel',\n name: 'Vercel',\n vendor: 'Vercel',\n tagline: 'Deployments, logs & projects',\n brandColor: '#FFFFFF',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Vercel</title><path fill=\"#FFFFFF\" d=\"m12 1.608 12 20.784H0Z\"/></svg>',\n },\n supabase: {\n id: 'supabase',\n name: 'Supabase',\n vendor: 'Supabase',\n tagline: 'Postgres, auth & storage',\n brandColor: '#3FCF8E',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Supabase</title><path fill=\"#FFFFFF\" d=\"M11.9 1.036c-.015-.986-1.26-1.41-1.874-.637L.764 12.05C-.33 13.427.65 15.455 2.409 15.455h9.579l.113 7.51c.014.985 1.259 1.408 1.873.636l9.262-11.653c1.093-1.375.113-3.403-1.645-3.403h-9.642z\"/></svg>',\n },\n convex: {\n id: 'convex',\n name: 'Convex',\n vendor: 'Convex',\n tagline: 'Reactive database & backend',\n brandColor: '#EE342F',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Convex</title><path fill=\"#FFFFFF\" d=\"M15.09 18.916c3.488-.387 6.776-2.246 8.586-5.348-.857 7.673-9.247 12.522-16.095 9.545a3.47 3.47 0 0 1-1.547-1.314c-1.539-2.417-2.044-5.492-1.318-8.282 2.077 3.584 6.3 5.78 10.374 5.399m-10.501-7.65c-1.414 3.266-1.475 7.092.258 10.24-6.1-4.59-6.033-14.41-.074-18.953a3.44 3.44 0 0 1 1.893-.707c2.825-.15 5.695.942 7.708 2.977-4.09.04-8.073 2.66-9.785 6.442m11.757-5.437C14.283 2.951 11.053.992 7.515.933c6.84-3.105 15.253 1.929 16.17 9.37a3.6 3.6 0 0 1-.334 2.02c-1.278 2.594-3.647 4.607-6.416 5.352 2.029-3.763 1.778-8.36-.589-11.847\"/></svg>',\n },\n cloudflare: {\n id: 'cloudflare',\n name: 'Cloudflare',\n vendor: 'Cloudflare',\n tagline: 'Workers, Pages, DNS & R2',\n brandColor: '#F6821F',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 460 271.2\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><title>Cloudflare</title><path fill=\"#FBAD41\" d=\"M328.6,125.6c-0.8,0-1.5,0.6-1.8,1.4l-4.8,16.7c-2.1,7.2-1.3,13.8,2.2,18.7c3.2,4.5,8.6,7.1,15.1,7.4l26.2,1.6c0.8,0,1.5,0.4,1.9,1c0.4,0.6,0.5,1.5,0.3,2.2c-0.4,1.2-1.6,2.1-2.9,2.2l-27.3,1.6c-14.8,0.7-30.7,12.6-36.3,27.2l-2,5.1c-0.4,1,0.3,2,1.4,2h93.8c1.1,0,2.1-0.7,2.4-1.8c1.6-5.8,2.5-11.9,2.5-18.2c0-37-30.2-67.2-67.3-67.2C330.9,125.5,329.7,125.5,328.6,125.6z\"/><path fill=\"#F6821F\" d=\"M292.8,204.4c2.1-7.2,1.3-13.8-2.2-18.7c-3.2-4.5-8.6-7.1-15.1-7.4l-123.1-1.6c-0.8,0-1.5-0.4-1.9-1s-0.5-1.4-0.3-2.2c0.4-1.2,1.6-2.1,2.9-2.2l124.2-1.6c14.7-0.7,30.7-12.6,36.3-27.2l7.1-18.5c0.3-0.8,0.4-1.6,0.2-2.4c-8-36.2-40.3-63.2-78.9-63.2c-35.6,0-65.8,23-76.6,54.9c-7-5.2-15.9-8-25.5-7.1c-17.1,1.7-30.8,15.4-32.5,32.5c-0.4,4.4-0.1,8.7,0.9,12.7c-27.9,0.8-50.2,23.6-50.2,51.7c0,2.5,0.2,5,0.5,7.5c0.2,1.2,1.2,2.1,2.4,2.1h227.2c1.3,0,2.5-0.9,2.9-2.2L292.8,204.4z\"/></svg>',\n },\n asana: {\n id: 'asana',\n name: 'Asana',\n vendor: 'Asana',\n tagline: 'Tasks, projects & workflows',\n brandColor: '#F06A6A',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Asana</title><path fill=\"#FFFFFF\" d=\"M18.78 12.653c-2.882 0-5.22 2.336-5.22 5.22s2.338 5.22 5.22 5.22 5.22-2.34 5.22-5.22-2.336-5.22-5.22-5.22zm-13.56 0c-2.88 0-5.22 2.337-5.22 5.22s2.338 5.22 5.22 5.22 5.22-2.338 5.22-5.22-2.336-5.22-5.22-5.22zm12-6.525c0 2.883-2.337 5.22-5.22 5.22-2.882 0-5.22-2.337-5.22-5.22 0-2.88 2.338-5.22 5.22-5.22 2.883 0 5.22 2.34 5.22 5.22z\"/></svg>',\n },\n postman: {\n id: 'postman',\n name: 'Postman',\n vendor: 'Postman',\n tagline: 'APIs, collections & environments',\n brandColor: '#FF6C37',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Postman</title><path fill=\"#FFFFFF\" d=\"M13.527.099C6.955-.744.942 3.9.099 10.473c-.843 6.572 3.8 12.584 10.373 13.428 6.573.843 12.587-3.801 13.428-10.374C24.744 6.955 20.101.943 13.527.099zm2.471 7.485a.855.855 0 0 0-.593.25l-4.453 4.453-.307-.307-.643-.643c4.389-4.376 5.18-4.418 5.996-3.753zm-4.863 4.861l4.44-4.44a.62.62 0 1 1 .847.903l-4.699 4.125-.588-.588zm.33.694l-1.1.238a.06.06 0 0 1-.067-.032.06.06 0 0 1 .01-.073l.645-.645.512.512zm-2.803-.459l1.172-1.172.879.878-1.979.426a.074.074 0 0 1-.085-.039.072.072 0 0 1 .013-.093zm-3.646 6.058a.076.076 0 0 1-.069-.083.077.077 0 0 1 .022-.046h.002l.946-.946 1.222 1.222-2.123-.147zm2.425-1.256a.228.228 0 0 0-.117.256l.203.865a.125.125 0 0 1-.211.117h-.003l-.934-.934-.294-.295 3.762-3.758 1.82-.393.874.874c-1.255 1.102-2.971 2.201-5.1 3.268zm5.279-3.428h-.002l-.839-.839 4.699-4.125a.952.952 0 0 0 .119-.127c-.148 1.345-2.029 3.245-3.977 5.091zm3.657-6.46l-.003-.002a1.822 1.822 0 0 1 2.459-2.684l-1.61 1.613a.119.119 0 0 0 0 .169l1.247 1.247a1.817 1.817 0 0 1-2.093-.343zm2.578 0a1.714 1.714 0 0 1-.271.218h-.001l-1.207-1.207 1.533-1.533c.661.72.637 1.832-.054 2.522zM18.855 6.05a.143.143 0 0 0-.053.157.416.416 0 0 1-.053.45.14.14 0 0 0 .023.197.141.141 0 0 0 .084.03.14.14 0 0 0 .106-.05.691.691 0 0 0 .087-.751.138.138 0 0 0-.194-.033z\"/></svg>',\n },\n n8n: {\n id: 'n8n',\n name: 'n8n',\n vendor: 'n8n',\n tagline: 'Workflow automation & webhooks',\n brandColor: '#EA4B71',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>n8n</title><path fill=\"#FFFFFF\" d=\"M21.4737 5.6842c-1.1772 0-2.1663.8051-2.4468 1.8947h-2.8955c-1.235 0-2.289.893-2.492 2.111l-.1038.623a1.263 1.263 0 0 1-1.246 1.0555H11.289c-.2805-1.0896-1.2696-1.8947-2.4468-1.8947s-2.1663.8051-2.4467 1.8947H4.973c-.2805-1.0896-1.2696-1.8947-2.4468-1.8947C1.1311 9.4737 0 10.6047 0 12s1.131 2.5263 2.5263 2.5263c1.1772 0 2.1663-.8051 2.4468-1.8947h1.4223c.2804 1.0896 1.2696 1.8947 2.4467 1.8947 1.1772 0 2.1663-.8051 2.4468-1.8947h1.0008a1.263 1.263 0 0 1 1.2459 1.0555l.1038.623c.203 1.218 1.257 2.111 2.492 2.111h.3692c.2804 1.0895 1.2696 1.8947 2.4468 1.8947 1.3952 0 2.5263-1.131 2.5263-2.5263s-1.131-2.5263-2.5263-2.5263c-1.1772 0-2.1664.805-2.4468 1.8947h-.3692a1.263 1.263 0 0 1-1.246-1.0555l-.1037-.623A2.52 2.52 0 0 0 13.9607 12a2.52 2.52 0 0 0 .821-1.4794l.1038-.623a1.263 1.263 0 0 1 1.2459-1.0555h2.8955c.2805 1.0896 1.2696 1.8947 2.4468 1.8947 1.3952 0 2.5263-1.131 2.5263-2.5263s-1.131-2.5263-2.5263-2.5263m0 1.2632a1.263 1.263 0 0 1 1.2631 1.2631 1.263 1.263 0 0 1-1.2631 1.2632 1.263 1.263 0 0 1-1.2632-1.2632 1.263 1.263 0 0 1 1.2632-1.2631M2.5263 10.7368A1.263 1.263 0 0 1 3.7895 12a1.263 1.263 0 0 1-1.2632 1.2632A1.263 1.263 0 0 1 1.2632 12a1.263 1.263 0 0 1 1.2631-1.2632m6.3158 0A1.263 1.263 0 0 1 10.1053 12a1.263 1.263 0 0 1-1.2632 1.2632A1.263 1.263 0 0 1 7.579 12a1.263 1.263 0 0 1 1.2632-1.2632m10.1053 3.7895a1.263 1.263 0 0 1 1.2631 1.2632 1.263 1.263 0 0 1-1.2631 1.2631 1.263 1.263 0 0 1-1.2632-1.2631 1.263 1.263 0 0 1 1.2632-1.2632\"/></svg>',\n },\n stripe: {\n id: 'stripe',\n name: 'Stripe',\n vendor: 'Stripe',\n tagline: 'Payments, customers & invoices',\n brandColor: '#635BFF',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Stripe</title><path fill=\"#FFFFFF\" d=\"M13.976 9.15c-2.172-.806-3.356-1.426-3.356-2.409 0-.831.683-1.305 1.901-1.305 2.227 0 4.515.858 6.09 1.631l.89-5.494C18.252.975 15.697 0 12.165 0 9.667 0 7.589.654 6.104 1.872 4.56 3.147 3.757 4.992 3.757 7.218c0 4.039 2.467 5.76 6.476 7.219 2.585.92 3.445 1.574 3.445 2.583 0 .98-.84 1.545-2.354 1.545-1.875 0-4.965-.921-6.99-2.109l-.9 5.555C5.175 22.99 8.385 24 11.714 24c2.641 0 4.843-.624 6.328-1.813 1.664-1.305 2.525-3.236 2.525-5.732 0-4.128-2.524-5.851-6.594-7.305h.003z\"/></svg>',\n },\n mixpanel: {\n id: 'mixpanel',\n name: 'Mixpanel',\n vendor: 'Mixpanel',\n tagline: 'Product & user analytics',\n brandColor: '#7856FF',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Mixpanel</title><path fill=\"#FFFFFF\" d=\"M6.967 9.996h3.053c-.763-.477-1.048-1.145-1.431-2.384L7.443 3.366C6.919 1.458 6.49.551 4.39.551H.004v1.145h.621c1.286 0 1.431.477 1.814 1.908L3.44 7.326c.524 1.814 1.337 2.67 3.53 2.67h-.003Zm7.06 0h3.053c2.194 0 2.956-.86 3.484-2.67l1.001-3.722c.382-1.431.57-1.908 1.814-1.908H24V.551h-4.34c-2.146 0-2.576.86-3.053 2.815l-1.145 4.246c-.384 1.286-.673 1.907-1.435 2.384Zm-4.007 4.008h4.007V9.996H10.02v4.008ZM0 23.449h4.39c2.1 0 2.529-.907 3.053-2.815l1.146-4.246c.383-1.239.668-1.907 1.431-2.384H6.967c-2.194 0-3.007.86-3.531 2.67l-1.001 3.722c-.383 1.431-.524 1.907-1.814 1.907H0v1.146Zm19.65 0h4.343v-1.146h-.622c-1.239 0-1.431-.476-1.814-1.907l-1.001-3.722c-.524-1.814-1.286-2.67-3.483-2.67h-3.046c.762.477 1.041 1.098 1.424 2.384l1.145 4.246c.477 1.955.907 2.815 3.054 2.815Z\"/></svg>',\n },\n pendo: {\n id: 'pendo',\n name: 'Pendo',\n vendor: 'Pendo',\n tagline: 'Product analytics & user guides',\n brandColor: '#EC2588',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Pendo</title><path fill=\"#FFFFFF\" d=\"M3 3h13.5A4.5 4.5 0 0 1 21 7.5v9A4.5 4.5 0 0 1 16.5 21H3V3Zm5 4v10h3v-3h2.2a3.5 3.5 0 0 0 0-7H8Zm3 2h1.9a1.5 1.5 0 0 1 0 3H11V9Z\"/></svg>',\n },\n pagerduty: {\n id: 'pagerduty',\n name: 'PagerDuty',\n vendor: 'PagerDuty',\n tagline: 'Incidents, alerts & on-call',\n brandColor: '#06AC38',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>PagerDuty</title><path fill=\"#FFFFFF\" d=\"M16.965 1.18C15.085.164 13.769 0 10.683 0H3.73v14.55h6.926c2.743 0 4.8-.164 6.61-1.37 1.975-1.303 3.004-3.484 3.004-6.007 0-2.716-1.262-4.896-3.305-5.994zm-5.5 10.326h-4.21V3.113l3.977-.027c3.62-.028 5.43 1.234 5.43 4.128 0 3.113-2.248 4.292-5.197 4.292zM3.73 17.61h3.525V24H3.73Z\"/></svg>',\n },\n amplitude: {\n id: 'amplitude',\n name: 'Amplitude',\n vendor: 'Amplitude',\n tagline: 'Digital analytics & experiments',\n brandColor: '#1F6FFF',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Amplitude</title><path fill=\"#FFFFFF\" d=\"M1 21h2V11H1v10Zm4 0h2V6H5v15Zm4 0h2V3H9v18Zm4 0h2V6h-2v15Zm4 0h2v-8h-2v8Zm4 0h2v-5h-2v5Z\"/></svg>',\n },\n datadog: {\n id: 'datadog',\n name: 'Datadog',\n vendor: 'Datadog',\n tagline: 'Metrics, logs & monitoring',\n brandColor: '#632CA6',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Datadog</title><path fill=\"#FFFFFF\" d=\"M19.57 17.04l-1.997-1.316-1.665 2.782-1.937-.567-1.706 2.604.087.82 9.274-1.71-.538-5.794zm-8.649-2.498l1.488-.204c.241.108.409.15.697.223.45.117.97.23 1.741-.16.18-.088.553-.43.704-.625l6.096-1.106.622 7.527-10.444 1.882zm11.325-2.712l-.602.115L20.488 0 .789 2.285l2.427 19.693 2.306-.334c-.184-.263-.471-.581-.96-.989-.68-.564-.44-1.522-.039-2.127.53-1.022 3.26-2.322 3.106-3.956-.056-.594-.15-1.368-.702-1.898-.02.22.017.432.017.432s-.227-.289-.34-.683c-.112-.15-.2-.199-.319-.4-.085.233-.073.503-.073.503s-.186-.437-.216-.807c-.11.166-.137.48-.137.48s-.241-.69-.186-1.062c-.11-.323-.436-.965-.343-2.424.6.421 1.924.321 2.44-.439.171-.251.288-.939-.086-2.293-.24-.868-.835-2.16-1.066-2.651l-.028.02c.122.395.374 1.223.47 1.625.293 1.218.372 1.642.234 2.204-.116.488-.397.808-1.107 1.165-.71.358-1.653-.514-1.713-.562-.69-.55-1.224-1.447-1.284-1.883-.062-.477.275-.763.445-1.153-.243.07-.514.192-.514.192s.323-.334.722-.624c.165-.109.262-.178.436-.323a9.762 9.762 0 0 0-.456.003s.42-.227.855-.392c-.318-.014-.623-.003-.623-.003s.937-.419 1.678-.727c.509-.208 1.006-.147 1.286.257.367.53.752.817 1.569.996.501-.223.653-.337 1.284-.509.554-.61.99-.688.99-.688s-.216.198-.274.51c.314-.249.66-.455.66-.455s-.134.164-.259.426l.03.043c.366-.22.797-.394.797-.394s-.123.156-.268.358c.277-.002.838.012 1.056.037 1.285.028 1.552-1.374 2.045-1.55.618-.22.894-.353 1.947.68.903.888 1.609 2.477 1.259 2.833-.294.295-.874-.115-1.516-.916a3.466 3.466 0 0 1-.716-1.562 1.533 1.533 0 0 0-.497-.85s.23.51.23.96c0 .246.03 1.165.424 1.68-.039.076-.057.374-.1.43-.458-.554-1.443-.95-1.604-1.067.544.445 1.793 1.468 2.273 2.449.453.927.186 1.777.416 1.997.065.063.976 1.197 1.15 1.767.306.994.019 2.038-.381 2.685l-1.117.174c-.163-.045-.273-.068-.42-.153.08-.143.241-.5.243-.572l-.063-.111c-.348.492-.93.97-1.414 1.245-.633.359-1.363.304-1.838.156-1.348-.415-2.623-1.327-2.93-1.566 0 0-.01.191.048.234.34.383 1.119 1.077 1.872 1.56l-1.605.177.759 5.908c-.337.048-.39.071-.757.124-.325-1.147-.946-1.895-1.624-2.332-.599-.384-1.424-.47-2.214-.314l-.05.059a2.851 2.851 0 0 1 1.863.444c.654.413 1.181 1.481 1.375 2.124.248.822.42 1.7-.248 2.632-.476.662-1.864 1.028-2.986.237.3.481.705.876 1.25.95.809.11 1.577-.03 2.106-.574.452-.464.69-1.434.628-2.456l.714-.104.258 1.834 11.827-1.424zM15.05 6.848c-.034.075-.085.125-.007.37l.004.014.013.032.032.073c.14.287.295.558.552.696.067-.011.136-.019.207-.023.242-.01.395.028.492.08.009-.048.01-.119.005-.222-.018-.364.072-.982-.626-1.308-.264-.122-.634-.084-.757.068a.302.302 0 0 1 .058.013c.186.066.06.13.027.207m1.958 3.392c-.092-.05-.52-.03-.821.005-.574.068-1.193.267-1.328.372-.247.191-.135.523.047.66.511.382.96.638 1.432.575.29-.038.546-.497.728-.914.124-.288.124-.598-.058-.698m-5.077-2.942c.162-.154-.805-.355-1.556.156-.554.378-.571 1.187-.041 1.646.053.046.096.078.137.104a4.77 4.77 0 0 1 1.396-.412c.113-.125.243-.345.21-.745-.044-.542-.455-.456-.146-.749\"/></svg>',\n },\n};\n\nexport const UPCOMING_INTEGRATION_IDS = [\n 'gmail',\n 'asana',\n 'stripe',\n 'pendo',\n 'pagerduty',\n 'amplitude',\n] as const;\n\nexport function getIntegrationBranding(id: string): IntegrationBranding | null {\n return INTEGRATION_BRANDING[id] ?? null;\n}\n","// Repo stack → recommended integrations (Session Tools \"Recommended\").\n//\n// PURE logic only — no filesystem. The CLI reads the repo's dependency\n// manifests (package.json, requirements.txt, go.mod, …), collects the raw\n// dependency NAMES, and calls `recommendForDeps(names)`. Keeping the mapping\n// here (single source) lets the CLI and any UI agree on what a dep implies.\nimport type { IntegrationId } from './types';\n\nexport type RepoStack = 'frontend' | 'backend' | 'fullstack' | 'mobile' | 'unknown';\n\n/** The Session Tools stack-detection result (wire type, carried by the\n * SESSION_STACK_DETECTED event). `source` distinguishes the deterministic\n * dependency scan from the agent one-shot fallback. */\nexport interface RepoStackDetection {\n stack: RepoStack;\n /** Integrations the repo's dependencies directly evidence (high confidence). */\n detected: IntegrationId[];\n /** Integrations commonly paired with the classified stack, minus `detected`. */\n recommended: IntegrationId[];\n source: 'scan' | 'agent';\n}\n\n/** Exact dependency name → integration (across ecosystems: npm, pip, go, gem…). */\nexport const DEP_TO_INTEGRATION: Record<string, IntegrationId> = {\n // errors / observability\n '@sentry/node': 'sentry',\n '@sentry/react': 'sentry',\n '@sentry/nextjs': 'sentry',\n '@sentry/browser': 'sentry',\n 'sentry-sdk': 'sentry',\n 'dd-trace': 'datadog',\n 'datadog-api-client': 'datadog',\n 'datadog-metrics': 'datadog',\n // analytics\n 'posthog-js': 'posthog',\n 'posthog-node': 'posthog',\n posthog: 'posthog',\n mixpanel: 'mixpanel',\n 'mixpanel-browser': 'mixpanel',\n // database / backend platforms\n convex: 'convex',\n '@supabase/supabase-js': 'supabase',\n 'supabase': 'supabase',\n // infra / deploy\n vercel: 'vercel',\n '@vercel/node': 'vercel',\n wrangler: 'cloudflare',\n // comms\n '@slack/web-api': 'slack',\n '@slack/bolt': 'slack',\n 'discord.js': 'discord',\n 'discord-py': 'discord',\n 'discord': 'discord',\n // trackers / docs / design\n '@linear/sdk': 'linear',\n '@notionhq/client': 'notion',\n 'jira.js': 'jira',\n jira: 'jira',\n newman: 'postman',\n 'figma-api': 'figma',\n 'figma-js': 'figma',\n // email / automation\n resend: 'resend',\n n8n: 'n8n',\n};\n\n/** Dependency name PREFIX → integration (scoped-package families). */\nconst DEP_PREFIX_TO_INTEGRATION: Array<[string, IntegrationId]> = [\n ['@sentry/', 'sentry'],\n ['@vercel/', 'vercel'],\n ['@cloudflare/', 'cloudflare'],\n ['@supabase/', 'supabase'],\n ['@slack/', 'slack'],\n ['@datadog/', 'datadog'],\n ['@linear/', 'linear'],\n ['@notionhq/', 'notion'],\n];\n\n/** Stack classification markers (dependency name → stack signal). */\nconst FRONTEND_MARKERS = [\n 'react',\n 'react-dom',\n 'next',\n 'vue',\n 'nuxt',\n '@angular/core',\n 'svelte',\n '@sveltejs/kit',\n 'solid-js',\n 'astro',\n 'gatsby',\n 'remix',\n '@remix-run/react',\n];\nconst MOBILE_MARKERS = ['react-native', 'expo', '@react-native/core', '@ionic/core', 'flutter'];\nconst BACKEND_MARKERS = [\n 'express',\n 'fastify',\n '@nestjs/core',\n 'koa',\n '@hapi/hapi',\n 'django',\n 'flask',\n 'fastapi',\n 'rails',\n 'sinatra',\n 'gin-gonic',\n 'laravel/framework',\n 'actix-web',\n 'spring-boot',\n];\n\nfunction hasAny(deps: Set<string>, markers: string[]): boolean {\n for (const m of markers) if (deps.has(m)) return true;\n return false;\n}\n\n/** Classify the repo stack from its dependency NAMES. */\nexport function classifyStack(depNames: string[]): RepoStack {\n const deps = new Set(depNames);\n const mobile = hasAny(deps, MOBILE_MARKERS);\n if (mobile) return 'mobile';\n const frontend = hasAny(deps, FRONTEND_MARKERS);\n const backend = hasAny(deps, BACKEND_MARKERS);\n if (frontend && backend) return 'fullstack';\n if (frontend) return 'frontend';\n if (backend) return 'backend';\n return 'unknown';\n}\n\n/** Integrations directly evidenced by the repo's dependencies (deduped, stable order). */\nexport function detectedIntegrationsFromDeps(depNames: string[]): IntegrationId[] {\n const out: IntegrationId[] = [];\n const seen = new Set<IntegrationId>();\n const push = (id: IntegrationId) => {\n if (!seen.has(id)) {\n seen.add(id);\n out.push(id);\n }\n };\n for (const name of depNames) {\n const exact = DEP_TO_INTEGRATION[name];\n if (exact) {\n push(exact);\n continue;\n }\n for (const [prefix, id] of DEP_PREFIX_TO_INTEGRATION) {\n if (name.startsWith(prefix)) {\n push(id);\n break;\n }\n }\n }\n return out;\n}\n\n/** Commonly-paired integrations per classified stack (the curated inference layer). */\nexport const STACK_TO_RECOMMENDED: Record<RepoStack, IntegrationId[]> = {\n frontend: ['figma', 'sentry', 'posthog', 'vercel'],\n backend: ['sentry', 'datadog', 'supabase', 'convex', 'cloudflare'],\n fullstack: ['sentry', 'posthog', 'vercel', 'supabase', 'convex'],\n mobile: ['sentry', 'posthog'],\n unknown: [],\n};\n\n/**\n * The deterministic core of Session Tools \"Recommended\": classify the stack and\n * combine direct-evidence detections with stack inference. `recommended`\n * excludes anything already `detected`. Returns `source:'scan'`. When both\n * `detected` and `recommended` are empty (unrecognized stack), the CLI falls\n * back to the agent one-shot (B).\n */\nexport function recommendForDeps(depNames: string[]): RepoStackDetection {\n const stack = classifyStack(depNames);\n const detected = detectedIntegrationsFromDeps(depNames);\n const detectedSet = new Set(detected);\n const recommended = STACK_TO_RECOMMENDED[stack].filter((id) => !detectedSet.has(id));\n return { stack, detected, recommended, source: 'scan' };\n}\n","import type { SkillDefinition } from './types';\n\n// QUALITY playbook; the mechanical gh steps stay in buildAgentReviewPrompt —\n// this skill is complementary reviewing guidance.\nconst CODE_REVIEW_BODY = `Use this skill when reviewing a pull request. It defines what a high-signal\nreview looks like so your inline comments are worth the author's time.\n\n## Review priorities (in order)\n1. **Correctness** — does the change do what the PR says, and only that? Trace the\n changed paths for logic errors, off-by-one, null/undefined, wrong branch, and\n inverted conditions. State a concrete failure scenario (inputs → wrong output)\n for anything you flag as a bug.\n2. **Security** — untrusted input reaching a sink (SQL, shell, path, HTML), secrets\n in code/logs, authz gaps, credentials passed via argv instead of env.\n3. **Tests** — does the change carry tests that would fail without it? Missing\n coverage on a bug-prone path is a finding.\n4. **Clarity / reuse** — duplicated logic, a simpler existing helper, a name that\n misleads. Only raise these when they materially affect maintainability.\n\n## Comment discipline\n- One finding per comment, anchored to the exact line.\n- Lead with severity: **blocker**, **should-fix**, or **nit**.\n- Say WHY (the failure or risk), not just WHAT. Propose the fix when it is short.\n- Do NOT restate the diff, praise trivially, or nitpick style a formatter owns.\n- If the PR is correct and well-tested, say so plainly and approve — a clean review\n is a valid outcome, not a failure to find something.\n\n## Scope\nReview only what the diff changes and its direct blast radius. Do not demand\nunrelated refactors.`;\n\nconst CODE_REVIEW_INSTRUCTION = `When reviewing this PR, prioritize correctness first, then security, then test\ncoverage, then clarity/reuse. One finding per inline comment, anchored to the exact\nline, each led by a severity tag (blocker/should-fix/nit) and a concrete reason\n(the failure scenario or risk), not a restatement of the diff. If the change is\ncorrect and well-tested, approve and say so — finding nothing is a valid outcome.\nReview only the diff and its direct blast radius; do not demand unrelated refactors.`;\n\nexport const codeReviewSkill: SkillDefinition = {\n id: 'code-review',\n name: 'Code Review',\n description: `High-signal PR review: prioritize correctness → security → tests → clarity, one anchored finding per comment.`,\n source: 'curated',\n delivery: {\n skillFile: { body: CODE_REVIEW_BODY },\n instruction: { body: CODE_REVIEW_INSTRUCTION },\n },\n};\n","import type { SkillDefinition } from './types';\n\nconst RESOLVE_CONFLICTS_BODY = `Use this skill when resolving merge conflicts on a pull request. The goal is a\nmerge that preserves BOTH sides' intent, not one that just makes the file compile.\n\n## Method\n1. Understand each conflict hunk before editing: what did HEAD change, what did the\n base branch change, and WHY. Read the surrounding function, not just the markers.\n2. Prefer a union of intents. Drop a side only when the two changes are genuinely\n mutually exclusive — and when you do, keep the side that matches the PR's purpose.\n3. Never leave a conflict marker (\\`<<<<<<<\\`, \\`=======\\`, \\`>>>>>>>\\`) behind. Grep for\n them before committing.\n4. After resolving, the code must build and its tests must pass. Run them. A merge\n that resolves markers but breaks the build is not done.\n5. For lockfiles/generated files, regenerate rather than hand-merge.\n\n## Commit\nOne commit that explains what was reconciled and any intent you had to choose\nbetween. Then push the branch.`;\n\nconst RESOLVE_CONFLICTS_INSTRUCTION = `When resolving these merge conflicts, preserve both sides' intent — read each hunk's\nsurrounding code to understand what HEAD and the base branch each changed and why,\nand prefer a union of intents; drop a side only when the two are mutually exclusive,\nkeeping the side that matches the PR's purpose. Leave no conflict markers behind\n(grep for them). Regenerate lockfiles rather than hand-merging them. The result must\nbuild and pass tests — run them — before you commit and push.`;\n\nexport const resolveConflictsSkill: SkillDefinition = {\n id: 'resolve-conflicts',\n name: 'Resolve Conflicts',\n description: `Merge-conflict resolution that preserves both sides' intent, leaves no markers, and keeps the build green.`,\n source: 'curated',\n delivery: {\n skillFile: { body: RESOLVE_CONFLICTS_BODY },\n instruction: { body: RESOLVE_CONFLICTS_INSTRUCTION },\n },\n};\n","import type { SkillDefinition } from './types';\n\n// An improved, adaptive take on spec-driven development: right-size the ceremony,\n// ground in the real codebase, clarify without stalling, gate against\n// over-engineering, and — the part most spec workflows lack — verify each\n// acceptance criterion + adversarially self-review before calling it done. Tracks\n// real work in beads, never scaffolds spec/plan/tasks files into the user's repo.\nconst SPEC_DRIVEN_BODY = `Use this skill for any coding task that isn't a trivial one-liner. Build the right\nthing, provably, with the least ceremony the task warrants — specification before\ncode, but its depth scales to the work. Skipping it yields code that looks right yet\nsolves the wrong problem or breaks something you never checked.\n\n## Step 0 — Right-size the work (always first)\n- **Quick** — a typo, copy tweak, one-line fix, a single file with no unknowns.\n No ceremony: make the change, run the relevant test/build, confirm it. Do NOT\n write a spec for a typo.\n- **Standard** — a feature or fix across a few files, some unknowns, a testable\n outcome. A light inline pass (a 2-3 sentence spec + a short plan), then build\n test-first, then verify. No scaffolding files.\n- **Deep** — large, ambiguous, risky, or touching many files / shared contracts /\n data / auth. The full flow below, tracking the work in beads (\\`bd\\`), not scratch\n files.\nWhen unsure, start one level lighter and escalate the moment real ambiguity or risk\nappears. State which level you picked in one line.\n\n## Step 1 — Ground in the real codebase (before specifying anything non-trivial)\nYou are almost never in a greenfield. Before you spec or plan, survey the real code:\nthe existing patterns for this kind of change, the files you'll touch, the test\nsetup, prior art, and the constraints (auth, data, shared types, CLAUDE.md\nconventions). A spec written in a vacuum produces a plan that fights the codebase.\nRead first; never assume.\n\n## Step 2 — Specify: the WHAT and WHY (not the HOW)\nState the user-visible outcome and why it matters, then the acceptance criteria —\neach concrete and testable (\"tapping X shows Y\", \"the endpoint returns 409 when Z\"),\nnever vague (\"works well\"). List what is out of scope. Put NO implementation detail\nhere (no file names, no libraries). If a requirement is ambiguous, mark it rather\nthan guess.\n\n## Step 3 — Clarify: resolve ambiguity, but don't stall\nGather the ambiguities that would actually change what you build. Ask the\nhighest-leverage ones — batched, at most ~3, phrased as concrete choices. For\nlow-stakes unknowns, pick a sensible default and SAY so (\"assuming X unless you tell\nme otherwise\") instead of asking. On a conversational/mobile client every round-trip\nis expensive — don't pester; decide what you safely can.\n\n## Step 4 — Plan: the HOW, grounded and simple\nDesign against the real code. Apply the simplicity gates before committing:\n- **Fewest moving parts** that satisfy the criteria. If you add a layer or\n abstraction, justify it or drop it.\n- **Use the framework/library directly** — don't wrap it for flexibility you don't\n need yet.\n- **Minimal blast radius** — touch what the change needs, nothing more.\nState the test strategy (what proves each criterion) and name the real risks. Keep\nthe plan short.\n\n## Step 5 — Tasks: small, verifiable, ordered\nSplit the plan into tasks that each end in something you can run and check. Mark\nindependent ones as parallelizable. Each task is the smallest unit worth its own\ncheck. Sequence by dependency.\n\n## Step 6 — Implement: test-first by default\nFor each task: write the test that would fail without the change, watch it fail, make\nit pass, keep it green. Follow the patterns you found in Step 1. Commit in logical\nunits. Escape hatch: for a genuine spike or exploratory UI where test-first is\nimpractical, say so explicitly and add the test right after — never skip it silently.\n\n## Step 7 — Verify + self-review (what separates \"done\" from \"looks done\")\nReturn to the acceptance criteria and prove EACH one is met — run the tests, diff the\nbehavior, look at the real output. Then review your own work adversarially:\n- What did I NOT test?\n- What did I change that I didn't need to?\n- What could this have broken (the blast radius)?\n- Does anything contradict the spec?\nFix what you find before declaring done. \"The tests I wrote pass\" is not \"it works\".\n\n## Step 8 — Done + handoff\nDone means acceptance criteria met, tests green, no known regressions. Summarize what\nchanged in plain terms. File any deferred work or follow-ups to beads (\\`bd\\`) so\nnothing is lost. Never claim done on unverified work.\n\n## Principles (the constitution)\n- Clarify before you build; verify before you call it done.\n- Testable beats descriptive — a criterion you can't check isn't one.\n- Grounded beats greenfield — fit the codebase that exists.\n- Simple beats clever — the least structure that works.\n- Scale the process to the task — ceremony on a typo is a bug.\n- Real work goes to beads, not throwaway files in someone's repo.`;\n\nconst SPEC_DRIVEN_INSTRUCTION = `Follow spec-driven development, scaled to the task:\n1. Right-size first. A trivial change (typo, one file, no unknowns) → just make it\n well and verify, no ceremony. A feature/ambiguous/risky change → spec → plan →\n build → verify.\n2. Ground in the real codebase before planning — read the existing patterns, tests,\n and constraints. Never assume; read first.\n3. Specify the WHAT and WHY as testable acceptance criteria, not the HOW. Mark\n ambiguities instead of guessing.\n4. Clarify only the highest-leverage unknowns (batch <=3, concrete choices); default\n the low-stakes ones and say so. Don't stall on round-trips.\n5. Plan the simplest approach that fits the code: fewest moving parts, use libraries\n directly, minimal blast radius. State how each criterion will be tested.\n6. Implement test-first by default; follow existing patterns; commit in logical units.\n7. Verify EACH acceptance criterion (run tests, diff behavior), then self-review\n adversarially: what's untested? what did I change needlessly? what could I have\n broken? what contradicts the spec? Fix before declaring done.\n8. Track deferred work in beads (bd), not scratch files. Simple beats clever; verify\n before done.`;\n\nexport const specDrivenDevelopmentSkill: SkillDefinition = {\n id: 'spec-driven-development',\n name: 'Spec-Driven Development',\n description: `Spec-driven development scaled to the task: right-size, ground in the code, write testable acceptance criteria, plan simply, build test-first, and verify every criterion before done.`,\n source: 'curated',\n delivery: {\n skillFile: { body: SPEC_DRIVEN_BODY },\n instruction: { body: SPEC_DRIVEN_INSTRUCTION },\n },\n};\n","import type { SkillDefinition } from './types';\n\n// Naming guidance distilled from CodeAesthetic — \"Naming Things in Code\"\n// (https://www.youtube.com/watch?v=-J3wNP6u5YU). The video's five rules are the\n// spine (no abbreviations, no type-in-variable, units when they matter, no\n// type-in-type, refactor the \"Utils\" bucket); the rest are consistent, widely\n// held extensions. Deliberately framed as a REVIEW/REFACTOR guide with strong\n// \"don't mass-rename\" guardrails — the skill makes better names available, it\n// does NOT license renaming code that was already clear.\n\nconst CODE_NAMING_BODY = `Use this skill when NAMING or RENAMING code — variables, functions, classes,\ninterfaces, types, files, modules, components, hooks, constants — while writing\nnew code, refactoring, or reviewing a diff. A name should reveal intent and\ndomain meaning so a reader understands the code without translating it in their\nhead, and so it needs fewer comments.\n\n## The five core rules (name the concept, not the implementation)\n\n1. **Don't abbreviate, and don't use single letters for meaningful values.**\n \\`usr\\`/\\`cfg\\`/\\`authReq\\` → \\`user\\`/\\`config\\`/\\`authRequest\\`; \\`u\\`/\\`p\\` → \\`user\\`/\\`project\\`.\n Abbreviations rely on context the next reader may not have. Allowed: standard\n acronyms the domain already uses (API, URL, HTTP, ID, UUID, CLI, SDK, PR, OAuth),\n loop indices \\`i\\`/\\`j\\`, coordinates \\`x\\`/\\`y\\`, and generic type params \\`T\\`/\\`K\\`/\\`V\\`.\n\n2. **Don't put the data TYPE in a variable name.** \\`userList\\` → \\`users\\`,\n \\`nameString\\` → \\`displayName\\`, \\`isActiveBool\\` → \\`isActive\\`, \\`invoiceMap\\` →\n \\`invoicesById\\`. The type system and editor already show the type; the name\n should carry the meaning.\n\n3. **Include the UNIT when a number has one** (unless the type makes it\n unmistakable). \\`timeout\\` → \\`timeoutMs\\`, \\`delay\\` → \\`retryDelaySeconds\\`,\n \\`size\\` → \\`fileSizeBytes\\`, \\`price\\` → \\`priceUsd\\`. Especially time, distance,\n money, bytes, rates, percentages, token/rate limits.\n\n4. **Don't put the type CONSTRUCT in a type's name.** \\`UserClass\\` → \\`User\\`,\n \\`PaymentInterface\\`/\\`IPayment\\` → \\`Payment\\` (or a role like \\`PaymentGateway\\`),\n \\`ConfigType\\` → \\`Config\\`, \\`StatusEnum\\` → \\`Status\\`. Name the concept, not\n \"what kind of programming thing it is.\"\n\n5. **Refactor when you're reaching for \\`Utils\\`/\\`Helpers\\`/\\`Common\\`.** A generic\n bucket usually hides a missing concept. Group by domain instead:\n \\`utils/parseCookie\\` → a \\`cookies/\\` module or a \\`CookieStore\\`/\\`CookieJar\\` class;\n \\`utils/formatDate\\` → \\`dates/\\` or a \\`DateRangeFormatter\\`. Same for vague\n \\`Base\\`/\\`Abstract\\`/\\`Manager\\`/\\`Helper\\` class names — prefer the role\n (\\`Repository\\`, \\`BillingService\\`, \\`UserProvisioner\\`) unless the project\n convention explicitly requires \\`Base\\`/\\`Abstract\\`.\n\n## Shape rules\n\n- **Functions are verbs / verb phrases:** \\`createInvoice()\\`, \\`sendPasswordResetEmail()\\`,\n \\`findRepositoryById()\\`. Booleans read like a question in an \\`if\\`:\n \\`isActive\\`, \\`hasAccess\\`, \\`canDeploy\\`, \\`shouldRetry\\` (prefix \\`is/has/can/should/needs\\`).\n- **Classes / interfaces / types are nouns or roles:** \\`Invoice\\`, \\`PaymentGateway\\`,\n \\`AgentSession\\` — not \\`InvoiceData\\`, \\`SubscriptionThing\\`, \\`AgentSessionType\\`.\n- **Collections are plural, or \\`…ById\\`/\\`…ByX\\` when indexed:** \\`users\\`,\n \\`activeSessions\\`, \\`invoicesByCustomerId\\`.\n- **Don't encode a temporary implementation in a name that may change:**\n \\`postgresUserRepository\\` → \\`userRepository\\`, \\`redisCache\\` → \\`cache\\` — unless the\n implementation IS the meaningful distinction. Name the role, not today's backend.\n- **Follow the project's existing conventions** (casing, file naming, \\`useX\\`\n hooks, \\`handleX\\` handlers). Match the surrounding code; don't introduce a new\n style without a strong reason.\n\n## Words to distrust (infer the real role, then name it)\n\n\\`data\\`, \\`info\\`, \\`item\\`, \\`obj\\`, \\`temp\\`, \\`result\\`, \\`value\\`, \\`thing\\`, \\`stuff\\`,\n\\`manager\\`, \\`helper\\`, \\`utils\\`, \\`common\\`, \\`base\\`, \\`abstract\\`, \\`processor\\`,\n\\`handler\\`, \\`payload\\`. They're acceptable only when the surrounding context makes\nthem precise (e.g. \\`config\\` inside one \\`DatabaseConnection\\`); vague when passed\nacross many layers.\n\n## Guardrails — this is a review/refactor guide, NOT a mass-rename mandate\n\n- Names that are already clear and idiomatic are DONE — leave them.\n- Before renaming: read how the identifier is used and infer the real domain\n concept. Prefer the **smallest** clear rename.\n- Do NOT do large unrelated renames, and do NOT rename purely for personal\n preference. Stay inside the change you were asked to make.\n- Preserve public API / exported names unless the task explicitly allows a break;\n when you do rename, update every reference, tests, and docs in the same change.\n- No clever names, jokes, or metaphors in production code; don't make names\n needlessly long either.\n\nWhen reviewing, raise a naming issue only when a clearer name would materially\nhelp a reader — one suggestion per finding: \\`current → suggested — why\\`.`;\n\nconst CODE_NAMING_INSTRUCTION = `When naming or renaming code, name the domain concept, not the implementation:\nno abbreviations or single-letter names for meaningful values (standard acronyms\nlike API/URL/ID/OAuth are fine); don't put the data type in a variable name\n(userList → users); include the unit when a number has one (timeout → timeoutMs);\ndon't put the type construct in a type's name (IPayment → Payment); and refactor\ngeneric Utils/Helper/Manager/Base buckets into a domain module or a role-named\nclass. Functions are verbs (createInvoice), booleans read like questions\n(isActive/hasAccess), classes/types are nouns/roles, collections are plural or\n…ById. This is a review/refactor guide, not a mandate to mass-rename: leave\nalready-clear names alone, prefer the smallest clear rename, don't rename\nunrelated code, and preserve public/exported names unless the task allows a break.`;\n\nexport const codeNamingSkill: SkillDefinition = {\n id: 'code-naming',\n name: 'Code Naming',\n description: `Naming review & refactor: name the domain concept, not the implementation — no abbreviations, no type-in-name, units when they matter, no Utils/Manager/Base buckets; rename minimally, never mass-rename.`,\n source: 'curated',\n delivery: {\n skillFile: { body: CODE_NAMING_BODY },\n instruction: { body: CODE_NAMING_INSTRUCTION },\n },\n};\n","// The registry assembles every curated Agent Skill from its own file into one\n// lookup. Adding a skill = a new `<id>.ts` file (definition + content) + one import\n// line here + widening `SkillId` in `types.ts`. Because content is bundled and\n// delivered as data, a new curated skill needs a client release but no backend logic.\nimport type { SkillDefinition, SkillId, SkillRail } from './types';\nimport { codeReviewSkill } from './code-review';\nimport { resolveConflictsSkill } from './resolve-conflicts';\nimport { specDrivenDevelopmentSkill } from './spec-driven-development';\nimport { codeNamingSkill } from './code-naming';\n\nexport const SKILL_REGISTRY: Record<SkillId, SkillDefinition> = {\n 'code-review': codeReviewSkill,\n 'resolve-conflicts': resolveConflictsSkill,\n 'spec-driven-development': specDrivenDevelopmentSkill,\n 'code-naming': codeNamingSkill,\n};\n\nexport function isSkillId(id: string): id is SkillId {\n return Object.prototype.hasOwnProperty.call(SKILL_REGISTRY, id);\n}\n\nexport function getSkillDefinition(id: string): SkillDefinition | null {\n return isSkillId(id) ? SKILL_REGISTRY[id] : null;\n}\n\nexport function skillHasRail(id: SkillId, rail: SkillRail): boolean {\n return Boolean(SKILL_REGISTRY[id].delivery[rail]);\n}\n","/**\n * Production API base URL for all CodeAgent Mobile clients.\n *\n * History note: prod migrated from Vercel (`https://api.codeagent-mobile.com`)\n * to Cloud Run / api-v2 (`https://api.codeagent-mobile.com`) in 2026-05. The\n * Vercel deployment is now gated by Vercel deployment protection and returns\n * 403 for unauthed traffic — DO NOT fall back to it.\n *\n * Override at runtime with `CODEAM_API_URL` (full URL override) OR set\n * `CODEAM_TEST_MODE=1` to point every client request at the dev\n * preview without having to know its host.\n */\nexport const DEFAULT_API_BASE_URL = 'https://api.codeagent-mobile.com' as const;\n\n/**\n * Dev-preview API base URL. Same Cloud Run service as prod but routed\n * to the `dev` revision (auto-deploys from the `dev` branch in the\n * backend repo). Manual smoke tests + load runs land here.\n */\nexport const DEV_API_BASE_URL = 'https://dev-api.codeagent-mobile.com' as const;\n\n/**\n * Resolve the active API base URL, honoring in priority order:\n *\n * 1. Explicit `CODEAM_API_URL` env var — full URL, takes precedence.\n * 2. `CODEAM_TEST_MODE=1` shortcut — flips to [DEV_API_BASE_URL]\n * without the user having to know the dev host.\n * 3. The `DEFAULT_API_BASE_URL` constant (prod).\n *\n * Used by every CLI service that talks to the backend so one env var\n * flips heartbeats, command relay, chunk uploads, and the pairing\n * flow in lockstep — eliminates the cross-environment misroute where\n * pairing succeeds in dev (shared Redis) but the CLI keeps\n * heartbeating to prod.\n */\nexport function resolveApiBaseUrl(): string {\n // Guard against non-Node runtimes (browser bundles import this\n // module). `process` is undefined there; treat as prod default.\n // `@codeam/shared` deliberately avoids depending on `@types/node`\n // so its types stay consumable from the mobile RN bundle too, so we\n // reach for the env via a structural cast rather than NodeJS.ProcessEnv.\n const env = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env;\n const explicit = env?.CODEAM_API_URL?.trim();\n if (explicit) return explicit;\n const testFlag = env?.CODEAM_TEST_MODE?.trim();\n if (testFlag === '1' || testFlag?.toLowerCase() === 'true') return DEV_API_BASE_URL;\n return DEFAULT_API_BASE_URL;\n}\n","/**\n * Headroom provisioning manifest — the SINGLE source of truth for what a\n * Headroom install consists of, rendered by every provisioning surface:\n *\n * - codespace bootstrap (bash composer in the backend repo,\n * `apps/api-v2/src/codespaces/github-ssh.service.ts` — adopts in PR-2),\n * - self-hosted deploy (TS installer, CLI `commands/host-agent.ts`\n * `setupHeadroomForSelfHosted`),\n * - on-demand local sessions (\"Session add-ons → Cost-saving\", CLI\n * `services/headroom/configure.ts`).\n *\n * Values are DATA-first (arrays/records, plus tiny pure renderers) so both\n * the TS installer and a bash composer can interpolate from them. Renderers\n * are byte-exact with the literals they replaced — guarded by\n * `packages/shared/__tests__/headroom-manifest.test.ts`.\n *\n * ⚠️ The extras matter: `[proxy,code]` pulls the ONNX compression engines\n * (Kompress + tree-sitter CodeCompressor). NEVER add `[ml]` — that's\n * multi-GB PyTorch, and a broken/cold torch wedges every prompt at\n * \"Thinking…\". The models are pre-downloaded at provision time because the\n * proxy eager-loads with `allow_download=False` and a cold cache defers the\n * ~840 MB download to the first prompt (blowing the agent's ~90 s idle\n * timeout).\n */\n\n/** Local proxy port the agent's config is routed to. */\nexport const HEADROOM_PROXY_PORT = 8787;\n\n/**\n * Env that pins the ONNX backend on the proxy process — never imports\n * torch. Spread into the proxy launch env on every surface.\n */\nexport const HEADROOM_BACKEND_ENV = {\n HEADROOM_KOMPRESS_BACKEND: 'onnx_cpu',\n} as const;\n\n/**\n * The proxy's HTTP/server companion packages, installed alongside the\n * `headroom-ai[...]` package. The COMPRESSION ENGINES come from the\n * headroom-ai extras — NOT this list.\n */\nexport const HEADROOM_PIP_COMPANIONS: readonly string[] = [\n 'fastapi',\n 'uvicorn',\n 'httpx[http2]',\n 'websockets',\n 'zstandard',\n];\n\n/** The three provisioning surfaces (see module doc). */\nexport type HeadroomSurface = 'codespace' | 'selfHosted' | 'onDemand';\n\n/**\n * pip extras per surface. `onDemand` additionally ships `image`\n * (image-compression support, added with the Session add-ons path in\n * codeam-cli@2.49.0); the older codespace/self-hosted install strings\n * remain `[proxy,code]` byte-for-byte.\n */\nexport const HEADROOM_EXTRAS_BY_SURFACE: Readonly<Record<HeadroomSurface, readonly string[]>> = {\n codespace: ['proxy', 'code'],\n selfHosted: ['proxy', 'code'],\n onDemand: ['proxy', 'code', 'image'],\n};\n\n/** `headroom-ai[<extras>]` — the pip requirement string. */\nexport function headroomPipPackage(extras: readonly string[]): string {\n return `headroom-ai[${extras.join(',')}]`;\n}\n\n/** One HuggingFace repo to pre-warm into the HF cache at provision time. */\nexport interface HeadroomModelSpec {\n repo: string;\n /** `snapshot_download(..., allow_patterns=[…])` filter. */\n allowPatterns: readonly string[];\n}\n\n/**\n * The two HF repos Kompress needs. kompress-v2-base is the ONNX model\n * (skip its .pt/.safetensors torch artifacts); ModernBERT-base is the\n * TOKENIZER ONLY (skip its model weights).\n */\nexport const HEADROOM_MODELS: readonly HeadroomModelSpec[] = [\n {\n repo: 'chopratejas/kompress-v2-base',\n allowPatterns: ['*.json', 'onnx/*.onnx', 'kompress-int8-wo.onnx'],\n },\n {\n repo: 'answerdotai/ModernBERT-base',\n allowPatterns: ['*.json', 'tokenizer*', '*.txt', 'vocab*', 'merges*'],\n },\n];\n\n/** Formatting knob so each surface can stay byte-identical to its\n * historical literal (the CLI joins patterns with `,`, the codespace\n * bash composer with `, `). */\nexport interface HeadroomPythonRenderOpts {\n /** Put a space after the commas between allow_patterns entries. */\n spaceAfterComma?: boolean;\n}\n\n/** Render one `snapshot_download(...)` python line for a model. */\nexport function headroomSnapshotDownloadLine(\n model: HeadroomModelSpec,\n opts: HeadroomPythonRenderOpts = {},\n): string {\n const sep = opts.spaceAfterComma ? ', ' : ',';\n const patterns = model.allowPatterns.map((p) => `\"${p}\"`).join(sep);\n return `snapshot_download(\"${model.repo}\", allow_patterns=[${patterns}])`;\n}\n\n/**\n * The full model pre-download python snippet (import + one\n * `snapshot_download` per model), newline-joined — what the surfaces pass\n * to `python -c` / a heredoc.\n */\nexport function headroomModelPredownloadScript(opts: HeadroomPythonRenderOpts = {}): string {\n return [\n 'from huggingface_hub import snapshot_download',\n ...HEADROOM_MODELS.map((m) => headroomSnapshotDownloadLine(m, opts)),\n ].join('\\n');\n}\n","/**\n * Canonical names of the per-user SSE bus events (`/api/users/me/stream`).\n *\n * The authoritative list is the `UserEvent` discriminated union in the\n * backend repo: codeagent-mobile/apps/api-v2/src/user-events/user-events.types.ts.\n * Every `type:` literal of that union appears here exactly once — when a new\n * variant lands on the union, add its name here (and in the backend mirror of\n * this file at codeagent-mobile/packages/shared/src/types/events.ts).\n *\n * Producers (CLI event posts, backend `userEvents.publish` calls) and\n * consumers (the `useUserEventsSSE` hooks' switch cases) should reference\n * `USER_EVENTS.*` instead of re-typing the string, so a typo becomes a\n * compile error instead of a silently dropped event.\n */\nexport const USER_EVENTS = {\n PAIRED_SESSION_STATUS: 'paired_session_status',\n PAIRED_SESSION_ADDED: 'paired_session_added',\n PAIRED_SESSION_REMOVED: 'paired_session_removed',\n PAIRED_SESSION_BRANCH_CHANGED: 'paired_session_branch_changed',\n SHARED_WITH_ME_ADDED: 'shared_with_me_added',\n SHARED_WITH_ME_REVOKED: 'shared_with_me_revoked',\n USAGE_CHANGED: 'usage_changed',\n TASK_DONE: 'task_done',\n HUNK_PENDING_REVIEW_ADDED: 'hunk_pending_review_added',\n HUNK_REVIEW_RESOLVED: 'hunk_review_resolved',\n FILE_CHANGED: 'file_changed',\n FILES_BATCH_CHANGED: 'files_batch_changed',\n AGENT_STREAMING_CHUNK: 'agent_streaming_chunk',\n AGENT_AWAITING_ANSWER: 'agent_awaiting_answer',\n AWAITING_INPUT_ADDED: 'awaiting_input_added',\n AGENT_ANSWER_RESOLVED: 'agent_answer_resolved',\n TEMPLATE_ADDED: 'template_added',\n TEMPLATE_REMOVED: 'template_removed',\n TEMPLATE_UPDATED: 'template_updated',\n AGENT_TASK_DISPATCHED: 'agent_task_dispatched',\n AGENT_TASK_COMPLETED: 'agent_task_completed',\n LINKED_AGENT_ADDED: 'linked_agent_added',\n QUOTA_REACHED: 'quota_reached',\n LINKED_AGENT_LINK_FAILED: 'linked_agent_link_failed',\n CODESPACE_AGENT_INSTALLED: 'codespace_agent_installed',\n AGENT_CREDENTIALS_REFRESHED: 'agent_credentials_refreshed',\n CREDENTIAL_INVALID: 'credential_invalid',\n CODESPACE_WAKING: 'codespace_waking',\n CODESPACE_BILLING_BLOCKED: 'codespace_billing_blocked',\n COST_SAVING_UPDATED: 'cost_saving_updated',\n COMMAND_COMPLETED: 'command_completed',\n AI_SUMMARY_PENDING: 'ai_summary_pending',\n AI_SUMMARY_READY: 'ai_summary_ready',\n AI_INSIGHT_PENDING: 'ai_insight_pending',\n AI_INSIGHT_READY: 'ai_insight_ready',\n PUSH_TOKEN_INVALIDATED: 'push_token_invalidated',\n PREVIEW_DETECTION_PENDING: 'preview_detection_pending',\n PREVIEW_DETECTION_READY: 'preview_detection_ready',\n PREVIEW_STARTING: 'preview_starting',\n PREVIEW_READY: 'preview_ready',\n PREVIEW_STOPPED: 'preview_stopped',\n PREVIEW_ERROR: 'preview_error',\n PREVIEW_PROGRESS: 'preview_progress',\n BEADS_STATE_CHANGED: 'beads_state_changed',\n BEADS_PROVISIONING: 'beads_provisioning',\n BEADS_TEAM_MEMORY_CHANGED: 'beads_team_memory_changed',\n AUDIT_EVENT_ADDED: 'audit_event_added',\n SELF_HOSTED_HOST_ADDED: 'self_hosted_host_added',\n SELF_HOSTED_HOST_STATUS: 'self_hosted_host_status',\n SELF_HOSTED_HOST_REMOVED: 'self_hosted_host_removed',\n SELF_HOSTED_HOST_TELEMETRY: 'self_hosted_host_telemetry',\n SELF_HOSTED_HOST_METRICS: 'self_hosted_host_metrics',\n SELF_HOSTED_HOST_SESSIONS: 'self_hosted_host_sessions',\n SELF_HOSTED_DEPLOY_PROGRESS: 'self_hosted_deploy_progress',\n /** Fleet rescue: the user's CodeAgent Box reached RUNNING (host enrolled\n * + online). Drives the mobile \"Use a free CodeAgent Box\" flow to\n * auto-deploy the user's presets instead of hanging on a paired session\n * a box never creates. */\n FLEET_BOX_READY: 'fleet_box_ready',\n REFERRAL_REWARD_EARNED: 'referral_reward_earned',\n HEADROOM_PROGRESS: 'headroom_progress',\n HEADROOM_STATUS: 'headroom_status',\n BEADS_STATUS: 'beads_status',\n LINKED_AGENT_HEADROOM_BUDGET_UPDATED: 'linked_agent_headroom_budget_updated',\n CLI_UPDATE_AVAILABLE: 'cli_update_available',\n AGENT_INSTALL_PROGRESS: 'agent_install_progress',\n AGENT_INSTALL_FAILED: 'agent_install_failed',\n CLI_UPDATE_PROGRESS: 'cli_update_progress',\n CLI_UPDATE_FAILED: 'cli_update_failed',\n BATON_STATE: 'baton_state',\n INTEGRATION_LINKED: 'integration_linked',\n INTEGRATION_UNLINKED: 'integration_unlinked',\n INTEGRATION_CREDENTIAL_INVALID: 'integration_credential_invalid',\n // Session Tools — add/remove Agent Toolkit integrations on an ALREADY-ACTIVE\n // session. The backend publishes SESSION_INTEGRATIONS_CHANGED after it persists\n // the new attached set + relays `integrations_sync` to the box (the CLI rewrites\n // the manifest + reprovisions the live agent, answered synchronously via the\n // command result). Drives live UI on the sending device AND any other paired\n // device on the same session. Mirrored in repo A. (`integrations_detect` is a\n // pure request/response over the relay — no event.)\n SESSION_INTEGRATIONS_CHANGED: 'session_integrations_changed',\n // CodeRabbit reviewer — the CLI posts these to /api/coderabbit/events; the\n // backend re-publishes them on the per-user SSE bus (mirrored in repo A).\n CODERABBIT_PROGRESS: 'coderabbit_progress',\n CODERABBIT_STATUS: 'coderabbit_status',\n CODERABBIT_REVIEW: 'coderabbit_review',\n\n // VCS / PR Command Center — the backend publishes this after an agent finishes\n // reviewing a PR (verdict + comment count + findings), driving the mobile\n // completion screen + push. Mirrored in repo A's app-shared events.ts.\n VCS_AGENT_REVIEW_COMPLETE: 'vcs_agent_review_complete',\n /** PR-review launch progress toast — the \"Review with an agent\" flow shows a\n * toast when the review runs server-side (Inngest). Mobile-only surface,\n * produced by api-v2 (the CLI neither produces nor consumes it). Mirrored in\n * repo A. */\n PR_REVIEW_LAUNCH: 'pr_review_launch',\n} as const;\n\nexport type UserEventName = (typeof USER_EVENTS)[keyof typeof USER_EVENTS];\n","/**\n * Prompt the CLI sends to the user's linked agent (Claude, Codex, …)\n * in a headless one-shot to detect how to start the project's dev\n * server. Same pattern as the AI Insights \"summary\" prompt — the\n * agent runs locally with the user's auth, has read access to the\n * project, and returns a tiny JSON blob the CLI parses.\n *\n * Kept here (in `@codeam/shared`) so the CLI build inlines the\n * exact string at compile time without runtime fetch from the backend.\n */\nexport const PREVIEW_DETECT_PROMPT = `\nAnalyze the project in the current working directory and return how to start\nits development server for in-app preview.\n\nRead package.json, Procfile, Dockerfile, docker-compose.yml, manage.py, app.json,\nmix.exs, Cargo.toml, go.mod, requirements.txt, Gemfile, and any other framework\nmarkers you find at depth <= 2.\n\nReturn ONLY a JSON object on stdout (no prose, no markdown fences):\n\n{\n \"framework\": \"<name, or 'unsupported'>\",\n \"command\": \"<executable>\",\n \"args\": [\"...\"],\n \"port\": <number>,\n \"ready_pattern\": \"<regex matching the server-ready stdout line>\",\n \"env\": { \"HOST\": \"0.0.0.0\" },\n \"setup_commands\": [{ \"cmd\": \"<executable>\", \"args\": [\"...\"] }],\n \"notes\": \"<one-line caveat or null>\"\n}\n\nRules:\n- Pick the script the developer would run locally to see the app (typically \"dev\", \"start\", \"serve\").\n- Prefer binding to 0.0.0.0 — most frameworks default to localhost which the tunnel cannot reach.\n- For Expo: framework=\"Expo\", command=\"npx\", args=[\"expo\",\"start\",\"--tunnel\"], port=8081, notes=\"Scan QR with Expo Go\".\n- If no dev server applies (CLI library, lambda, batch script): {\"framework\":\"unsupported\",\"notes\":\"<reason>\"}.\n\nCRITICAL — setup_commands:\n- DO NOT include an install command (npm install, pnpm install, yarn install,\n yarn, bun install) in setup_commands. A lockfile-aware pre-flight installer\n runs BEFORE setup_commands and picks the correct package manager from the\n lockfile present (pnpm-lock.yaml -> pnpm, yarn.lock -> yarn, bun.lockb -> bun,\n else npm). Emitting an install here either duplicates that work or, worse,\n uses the WRONG package manager on top of node_modules just populated by the\n pre-flight, which crashes (e.g. npm errors with \"Cannot read properties of\n null (reading 'matches')\" when run over pnpm's .pnpm/ layout).\n- ONLY include setup_commands for genuinely non-install work the project needs\n before its dev server can boot: prisma generate, codegen, prebuild scripts,\n database migrations against a local SQLite, etc.\n- Each setup_commands entry MUST be an object {\"cmd\": \"...\", \"args\": [\"...\"]} —\n e.g. {\"cmd\": \"npx\", \"args\": [\"prisma\", \"generate\"]}. NOT a bare string.\n- For most projects, setup_commands should be an empty array [].\n\nOUTPUT JSON ONLY. NO MARKDOWN. NO COMMENTARY.\n`.trim();\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/protocol/constants.ts","../src/protocol/renderToLines.ts","../src/protocol/remote-command.ts","../src/models/pricing.ts","../src/agents/registry.ts","../src/agents/identity.ts","../src/integrations/registry.ts","../src/integrations/branding.ts","../src/integrations/stack-detect.ts","../src/skills/code-review.ts","../src/skills/resolve-conflicts.ts","../src/skills/spec-driven-development.ts","../src/skills/code-naming.ts","../src/skills/registry.ts","../src/api-url.ts","../src/headroom/manifest.ts","../src/types/events.ts","../src/preview-prompts.ts"],"sourcesContent":["/**\n * Shared wire / lifecycle constants. The values here are bundled\n * into the CLI + VS Code extension at build time via tsup / esbuild\n * and mirrored in `apps/jetbrains-plugin/.../protocol/Constants.kt`\n * since Kotlin can't import an npm package.\n *\n * If you change one of these values, also update the Kotlin mirror.\n */\n\n/**\n * Discriminated chunk-protocol version sent as the\n * `X-Codeam-Protocol-Version` header on every authed request. The\n * backend uses this to opt into legacy translations or to reject\n * with 426 when the client is too far behind. Bumped in lockstep\n * with chunk-shape changes (e.g. when the `chrome_steps` chunk\n * type is added).\n */\nexport const PROTOCOL_VERSION = '2.0.0' as const;\n\n/**\n * The VS Code AgentOutputMonitor's loopback HTTP server bound to\n * 127.0.0.1 on this port — the observer JS in the IDE renderer\n * uses it to round-trip captured chat content back into the\n * extension host. The port is intentionally fixed (rather than\n * `listen(0)`) so the observer script can be a static constant\n * rather than dynamically rewriting itself per session.\n *\n * Multi-window collision is solved by listen(0) per-window in the\n * monitor (see #103); this default is still the documented\n * starting port for tooling that needs to probe whether a CodeAgent\n * Mobile session is active locally.\n */\nexport const OBSERVER_BRIDGE_PORT = 47832;\n\n/**\n * Default plugin → backend heartbeat interval. User-configurable\n * via `codeagent-mobile.heartbeatIntervalMs` on VS Code and\n * `heartbeatIntervalMs` in SettingsService.kt's @State on JetBrains.\n * Mirrors the value the apps/api side uses to flip the paired\n * session to offline.\n */\nexport const HEARTBEAT_INTERVAL_MS_DEFAULT = 30_000;\n\n/**\n * SSE + polling reconnect cap. Vercel's serverless functions close\n * SSE connections after ~25 s by default; the client uses 35 s as\n * its overall socket timeout to leave a beat for graceful close.\n */\nexport const SSE_SOCKET_TIMEOUT_MS = 35_000;\n","/**\n * Render raw PTY bytes into an array of screen lines using a simplified\n * virtual terminal. Handles cursor movements (A/B/C/D/G/H), erase (J/K),\n * alternate-screen (?1049h), carriage return, and LF.\n *\n * This is the authoritative implementation used by both codeam-cli (PTY\n * output) and the VS Code extension (shell-integration output) so that\n * the mobile/web client sees identical chunks regardless of surface.\n */\nexport function renderToLines(raw: string): string[] {\n const screen: string[] = [''];\n let row = 0;\n let col = 0;\n\n function ensureRow(): void {\n while (screen.length <= row) screen.push('');\n }\n\n function writeChar(ch: string): void {\n ensureRow();\n if (col < screen[row].length) {\n screen[row] = screen[row].slice(0, col) + ch + screen[row].slice(col + 1);\n } else {\n while (screen[row].length < col) screen[row] += ' ';\n screen[row] += ch;\n }\n col++;\n }\n\n let i = 0;\n while (i < raw.length) {\n const ch = raw[i];\n\n if (ch === '\\x1B') {\n i++;\n if (i >= raw.length) break;\n\n if (raw[i] === '[') {\n i++;\n let param = '';\n while (i < raw.length && !/[@-~]/.test(raw[i])) param += raw[i++];\n const cmd = raw[i] ?? '';\n const n = parseInt(param) || 1;\n\n if (cmd === 'A') { row = Math.max(0, row - n); }\n else if (cmd === 'B') { row += n; ensureRow(); }\n else if (cmd === 'C') { col += n; }\n else if (cmd === 'D') { col = Math.max(0, col - n); }\n else if (cmd === 'G') { col = Math.max(0, n - 1); }\n else if (cmd === 'H' || cmd === 'f') {\n const p = param.split(';');\n row = Math.max(0, (parseInt(p[0] ?? '1') || 1) - 1);\n col = Math.max(0, (parseInt(p[1] ?? '1') || 1) - 1);\n ensureRow();\n } else if (cmd === 'J') {\n if (param === '2' || param === '3') {\n screen.length = 1; screen[0] = ''; row = 0; col = 0;\n } else if (param === '1') {\n for (let r = 0; r < row; r++) screen[r] = '';\n screen[row] = ' '.repeat(col) + screen[row].slice(col);\n } else {\n screen[row] = screen[row].slice(0, col);\n screen.splice(row + 1);\n }\n } else if (cmd === 'K') {\n ensureRow();\n if (param === '' || param === '0') screen[row] = screen[row].slice(0, col);\n else if (param === '1') screen[row] = ' '.repeat(col) + screen[row].slice(col);\n else if (param === '2') screen[row] = '';\n } else if (cmd === 'h' && (param === '?1049' || param === '?47')) {\n screen.length = 1; screen[0] = ''; row = 0; col = 0;\n } else if (cmd === 'l' && (param === '?1049' || param === '?47')) {\n screen.length = 1; screen[0] = ''; row = 0; col = 0;\n }\n } else if (raw[i] === ']') {\n i++;\n while (i < raw.length) {\n if (raw[i] === '\\x07') break;\n if (raw[i] === '\\x1B' && i + 1 < raw.length && raw[i + 1] === '\\\\') { i++; break; }\n i++;\n }\n }\n } else if (ch === '\\r') {\n if (i + 1 < raw.length && raw[i + 1] === '\\n') {\n row++; col = 0; ensureRow(); i++;\n } else {\n col = 0;\n }\n } else if (ch === '\\n') {\n row++; col = 0; ensureRow();\n } else if (ch >= ' ' || ch === '\\t') {\n writeChar(ch);\n }\n\n i++;\n }\n\n return screen;\n}\n","import { z } from 'zod';\n\n/**\n * The command envelope clients receive from the backend relay — both from\n * the `commands` SSE frames on `/api/commands/pending/stream` and from the\n * `GET /api/commands/pending` polling fallback. One schema, shared, so the\n * VS Code extension (and eventually the CLI) stop blind-casting\n * `Record<string, unknown>` into this shape.\n */\nexport interface RemoteCommand {\n id: string;\n sessionId: string;\n pluginId: string;\n type: string;\n payload: Record<string, unknown>;\n status: string;\n createdAt: number;\n}\n\nconst remoteCommandSchema = z.object({\n id: z.string(),\n sessionId: z.string(),\n pluginId: z.string(),\n type: z.string(),\n // The backend may omit `payload` (or send null) for payload-less commands;\n // clients have always normalized that to `{}` — keep that behavior here.\n payload: z.record(z.string(), z.unknown()).nullish(),\n status: z.string(),\n createdAt: z.number(),\n});\n\n/**\n * Validate a raw (already JSON-parsed) value into a `RemoteCommand`.\n * Returns `null` — never throws — on a malformed envelope so callers can\n * log-and-skip the single bad command without dropping the whole batch.\n */\nexport function toRemoteCommand(raw: unknown): RemoteCommand | null {\n const parsed = remoteCommandSchema.safeParse(raw);\n if (!parsed.success) return null;\n const { payload, ...rest } = parsed.data;\n return { ...rest, payload: payload ?? {} };\n}\n","export interface ModelPricing {\n input: number;\n output: number;\n cacheRead: number;\n cacheWrite: number;\n}\n\nexport const MODEL_PRICING: Record<string, ModelPricing> = {\n // ── Anthropic / Claude ────────────────────────────────────\n // The 4.x rows below cover the model ids actually emitted by the CLI\n // (apps/cli/src/agents/claude/runtime.ts listModels) and the JetBrains\n // fallback catalog (RemoteCommandRouter.kt). Prices are copied from the\n // same-family base rows (claude-opus-4 / claude-sonnet-4 /\n // claude-3-5-haiku) until distinct published rates land.\n 'claude-opus-4-7': { input: 15, output: 75, cacheRead: 1.50, cacheWrite: 18.75 },\n 'claude-opus-4-6': { input: 15, output: 75, cacheRead: 1.50, cacheWrite: 18.75 },\n 'claude-sonnet-4-6': { input: 3, output: 15, cacheRead: 0.30, cacheWrite: 3.75 },\n // Haiku-tier prices copied from claude-3-5-haiku (closest same-tier\n // sibling in this table) — previously this id matched NO row and was\n // silently billed at sonnet rates via the unknown-model fallback.\n 'claude-haiku-4-5': { input: 0.80, output: 4, cacheRead: 0.08, cacheWrite: 1 },\n 'claude-sonnet-4': { input: 3, output: 15, cacheRead: 0.30, cacheWrite: 3.75 },\n 'claude-opus-4': { input: 15, output: 75, cacheRead: 1.50, cacheWrite: 18.75 },\n 'claude-3-5-sonnet': { input: 3, output: 15, cacheRead: 0.30, cacheWrite: 3.75 },\n 'claude-3-5-haiku': { input: 0.80, output: 4, cacheRead: 0.08, cacheWrite: 1 },\n 'claude-3-haiku': { input: 0.25, output: 1.25, cacheRead: 0.03, cacheWrite: 0.30 },\n\n // ── Codex / OpenAI ────────────────────────────────────────\n // GPT-5.x rows are derived from OpenAI's published GPT-5 family rates\n // (standard tier: $1.25/1M in, $10/1M out, cached input at ~10% of input;\n // mini tier: $0.25/1M in, $2/1M out). OpenAI has no separate cache-WRITE\n // premium, so cacheWrite mirrors the input rate. Replace with the exact\n // per-version numbers from developers.openai.com/pricing when published —\n // these were the ZERO placeholders that rendered Codex sessions as $0.\n 'gpt-5.5': { input: 1.25, output: 10, cacheRead: 0.125, cacheWrite: 1.25 },\n 'gpt-5.4': { input: 1.25, output: 10, cacheRead: 0.125, cacheWrite: 1.25 },\n 'gpt-5.4-mini': { input: 0.25, output: 2, cacheRead: 0.025, cacheWrite: 0.25 },\n 'gpt-5.3-codex': { input: 1.25, output: 10, cacheRead: 0.125, cacheWrite: 1.25 },\n 'gpt-5.2': { input: 1.25, output: 10, cacheRead: 0.125, cacheWrite: 1.25 },\n 'codex-auto-review': { input: 1.25, output: 10, cacheRead: 0.125, cacheWrite: 1.25 },\n};\n\nexport const MODEL_CONTEXT_WINDOW: Record<string, number> = {\n // ── Anthropic / Claude ────────────────────────────────────\n 'claude-opus-4-7': 1_000_000,\n 'claude-opus-4-6': 1_000_000,\n 'claude-sonnet-4-6': 1_000_000,\n 'claude-haiku-4-5': 200_000,\n 'claude-opus-4': 1_000_000,\n 'claude-sonnet-4': 1_000_000,\n 'claude-3-5-sonnet': 200_000,\n 'claude-3-5-haiku': 200_000,\n 'claude-3-haiku': 200_000,\n\n // ── Codex / OpenAI ────────────────────────────────────────\n 'gpt-5.5': 272_000,\n 'gpt-5.4': 272_000,\n 'gpt-5.4-mini': 272_000,\n 'gpt-5.3-codex': 272_000,\n 'gpt-5.2': 272_000,\n 'codex-auto-review': 272_000,\n};\n\nconst DEFAULT_CONTEXT_WINDOW = 200_000;\n\n/**\n * Longest-prefix lookup. The tables key by model-family prefix; a model id\n * like `claude-opus-4-7` must resolve to its own row, not be shadowed by the\n * shorter `claude-opus-4` — so the match is by prefix LENGTH, never by the\n * table's insertion order.\n */\nfunction longestPrefixMatch<T>(table: Record<string, T>, model: string): T | undefined {\n let best: T | undefined;\n let bestLen = -1;\n for (const [prefix, value] of Object.entries(table)) {\n if (prefix.length > bestLen && model.startsWith(prefix)) {\n best = value;\n bestLen = prefix.length;\n }\n }\n return best;\n}\n\n/** True when the model id resolves to a real MODEL_PRICING row (i.e. getPricing\n * will NOT be guessing via the unknown-model fallback). */\nexport function isKnownModel(model: string): boolean {\n return longestPrefixMatch(MODEL_PRICING, model) !== undefined;\n}\n\n/**\n * Flagged default for an unpriced model id. All-zero so an unknown model is\n * VISIBLY unpriced ($0) rather than silently MISPRICED at some other family's\n * rates (the old sonnet-4 fallback billed unknown ids — including a haiku id\n * that matched no row — at sonnet rates). `getPricing` returns this object for\n * unknown ids so callers that do unconditional arithmetic still work; callers\n * that must distinguish real pricing from the default check `isKnownModel`.\n */\nexport const UNKNOWN_MODEL_PRICING: ModelPricing = {\n input: 0,\n output: 0,\n cacheRead: 0,\n cacheWrite: 0,\n};\n\n/**\n * Resolve pricing by longest matching prefix. Unknown models resolve to the\n * flagged {@link UNKNOWN_MODEL_PRICING} default (all-zero, i.e. visibly\n * unpriced) instead of guessing at another model's rates. Callers that need to\n * distinguish real pricing from the default must check `isKnownModel(model)`.\n */\nexport function getPricing(model: string): ModelPricing {\n return longestPrefixMatch(MODEL_PRICING, model) ?? UNKNOWN_MODEL_PRICING;\n}\n\nexport function getContextWindow(model: string | null): number {\n if (!model) return DEFAULT_CONTEXT_WINDOW;\n return longestPrefixMatch(MODEL_CONTEXT_WINDOW, model) ?? DEFAULT_CONTEXT_WINDOW;\n}\n\n/**\n * Context window ONLY when it's a confident match — `undefined` otherwise (no\n * default). Use where a wrong value is worse than none: the runtime model\n * selector maps native ACP model ids, many of which are opaque aliases\n * (\"default\", \"opus\") or proxied ids (a MiniMax-backed house agent) that aren't\n * in the catalog. Falling back to 200K for those printed a fake \"200K context\"\n * on every row; returning undefined lets the UI omit the sub-label instead.\n */\nexport function tryGetContextWindow(model: string | null): number | undefined {\n if (!model) return undefined;\n return longestPrefixMatch(MODEL_CONTEXT_WINDOW, model);\n}\n","import type { AgentId, AgentMetadata } from './types';\n\nexport const AGENT_REGISTRY: Record<AgentId, AgentMetadata> = {\n claude: {\n id: 'claude',\n displayName: 'Claude Code',\n binaryName: 'claude',\n enabled: true,\n // Mirrors the backend registry (codeagent-mobile\n // apps/api-v2/src/codespaces/agent.ts — authoritative for auth\n // capabilities). `setup_token` is the bare `sk-ant-oat01-…` from\n // `claude setup-token` → delivered via CLAUDE_CODE_OAUTH_TOKEN.\n supportedAuthKinds: ['setup_token', 'oauth_token', 'api_key'],\n preferredAuthKind: 'setup_token',\n headroomWrappable: true,\n headroomKind: 'claude',\n // npm adapter `@agentclientprotocol/claude-agent-acp`.\n acp: true,\n },\n codex: {\n id: 'codex',\n displayName: 'Codex CLI',\n binaryName: 'codex',\n enabled: true,\n supportedAuthKinds: ['oauth_token', 'api_key'],\n preferredAuthKind: 'oauth_token',\n headroomWrappable: true,\n headroomKind: 'codex',\n // npm adapter `@agentclientprotocol/codex-acp`.\n acp: true,\n // OAuth device-code flow; the user_code on the OpenAI page IS a real\n // human-typed code — surfaces render it (with a copy affordance).\n deviceFlow: true,\n showsUserCode: true,\n },\n copilot: {\n id: 'copilot',\n displayName: 'GitHub Copilot CLI',\n binaryName: 'gh',\n enabled: false,\n supportedAuthKinds: ['oauth_token'],\n preferredAuthKind: 'oauth_token',\n // `headroom init --global copilot` exists even though the agent is\n // still disabled here (no runtime builder yet).\n headroomWrappable: true,\n headroomKind: 'copilot',\n acp: false,\n },\n coderabbit: {\n id: 'coderabbit',\n displayName: 'CodeRabbit',\n binaryName: 'coderabbit',\n enabled: true,\n // CodeRabbit links via a CLI-driven LOOPBACK OAuth (`coderabbit auth\n // login --agent`): the CLI captures the token and hands it to the vault\n // through `linkFromCli` (method:'oauth'), same as the terminal handoff.\n // `oauth_token` is preferred; a real API key is still accepted as a\n // fallback. There is no backend PKCE provider — the loopback runs on the\n // user's own machine, so linking is always CLI-mediated.\n supportedAuthKinds: ['oauth_token', 'api_key'],\n preferredAuthKind: 'oauth_token',\n headroomWrappable: false,\n // Legacy PTY runtime — no ACP adapter registered.\n acp: false,\n },\n cursor: {\n id: 'cursor',\n displayName: 'Cursor Agent',\n binaryName: 'cursor-agent',\n enabled: true,\n // Backend registry is authoritative: since the Cursor OAuth\n // device-flow shipped, new links are oauth_token only (the login\n // blob written to ~/.config/cursor/auth.json). Legacy vaulted\n // api_key rows may still exist server-side, but the link surface\n // no longer offers api_key.\n supportedAuthKinds: ['oauth_token'],\n preferredAuthKind: 'oauth_token',\n // `headroom wrap cursor` is \"manual/print-only\" (IDE settings; the\n // headless cursor-agent CLI has no base-URL override) — runs native.\n headroomWrappable: false,\n // Native ACP server: `cursor-agent acp`.\n acp: true,\n // Reverse-engineered device/poll flow; `userCode` is the secret PKCE\n // verifier echoed back on poll — NEVER human-facing.\n deviceFlow: true,\n showsUserCode: false,\n },\n aider: {\n id: 'aider',\n displayName: 'Aider',\n binaryName: 'aider',\n enabled: true,\n // Aider is OAuth-less — auth is via ANTHROPIC_API_KEY / OPENAI_API_KEY\n // / etc. env vars or `~/.aider.conf.yml`. The link flow surfaces\n // this via the existing --api-key escape hatch in commands/link.ts.\n supportedAuthKinds: ['api_key'],\n preferredAuthKind: 'api_key',\n headroomWrappable: false,\n // Legacy PTY runtime — no ACP adapter registered.\n acp: false,\n },\n gemini: {\n id: 'gemini',\n displayName: 'Gemini CLI',\n binaryName: 'gemini',\n enabled: true,\n // OAuth via `gemini auth login` (captured by `codeam link gemini`\n // from ~/.gemini/oauth_creds.json) AND GEMINI_API_KEY are both\n // accepted by the backend's GeminiProvisioningStrategy and propagated\n // into codespace deploys.\n supportedAuthKinds: ['oauth_token', 'api_key'],\n preferredAuthKind: 'oauth_token',\n // Not listed by `headroom wrap --help` — runs native.\n headroomWrappable: false,\n // Native ACP server: `gemini --skip-trust --acp`.\n acp: true,\n },\n kimi: {\n id: 'kimi',\n displayName: 'Kimi Code',\n binaryName: 'kimi',\n enabled: true,\n // API key (KIMI_API_KEY, + optional KIMI_BASE_URL) is the shipping auth —\n // fully documented, no reverse-engineering. OAuth `/login` (login-state at\n // ~/.kimi-code/credentials/<name>.json, base https://api.kimi.com/coding/)\n // is declared so it can land later without a wire change, but capturing\n // that blob server-side is a separate reverse-engineering spike (phase 2).\n supportedAuthKinds: ['api_key', 'oauth_token'],\n preferredAuthKind: 'api_key',\n // Moonshot's `kimi` is not listed by `headroom wrap --help` — runs native.\n headroomWrappable: false,\n // Native ACP server: `kimi acp` (stdio JSON-RPC, answers `initialize`).\n acp: true,\n },\n opencode: {\n id: 'opencode',\n displayName: 'opencode',\n binaryName: 'opencode',\n enabled: true,\n // opencode is model-AGNOSTIC and AUTO-DETECTS provider API keys from env\n // vars (ANTHROPIC_API_KEY / OPENAI_API_KEY / GEMINI_API_KEY / …) — same\n // model as aider. So the credential is the user's OWN provider key; we set\n // the matching env var (detected by prefix) at deploy. api_key only.\n supportedAuthKinds: ['api_key'],\n preferredAuthKind: 'api_key',\n // Not listed by `headroom init --global` (claude/codex/copilot only) — native.\n headroomWrappable: false,\n // Native ACP server: `opencode acp` (stdio JSON-RPC).\n acp: true,\n },\n};\n\nexport function getEnabledAgents(): AgentMetadata[] {\n return Object.values(AGENT_REGISTRY).filter(m => m.enabled);\n}\n\nexport function getAgent(id: AgentId): AgentMetadata {\n const meta = AGENT_REGISTRY[id];\n if (!meta) throw new Error(`Unknown agent id: ${id}`);\n return meta;\n}\n\nexport function isKnownAgentId(id: string): id is AgentId {\n return id in AGENT_REGISTRY;\n}\n","/**\n * Agent identity — the ONE place the public (`LinkedAgentId`) and internal\n * (`AgentId`) id spaces are declared and bridged, plus the ONE alias\n * normalizer every surface funnels through.\n *\n * Canonical values consolidated from (Phase 2, PR-1):\n * - backend `apps/api-v2/src/linked-agents/agent-map.ts`\n * (`PUBLIC_TO_INTERNAL` / `INTERNAL_TO_PUBLIC` / `LinkedAgentId`),\n * - CLI `apps/cli/src/commands/host/agent-provisioning.ts`\n * (`PUBLIC_TO_INTERNAL_AGENT`),\n * - VS Code plugin `apps/vsc-plugin/src/utils/cli-agent-id.ts`\n * (marketplace aliases + `__terminal__:` strip),\n * - CLI `apps/cli/src/commands/start/handlers.ts`\n * (the `claude_code` → `claude` normalization),\n * - mobile `apps/mobile/src/lib/agent-id-map.ts`.\n */\n\nimport type { AgentId, HeadroomKind } from './types';\nimport { AGENT_REGISTRY, isKnownAgentId } from './registry';\n\n// ─── House agent constants ───────────────────────────────────────────────────\n// Byte-identical mirrors of the backend repo's canonical\n// `codeagent-mobile/packages/shared/src/constants/house-agent.ts` (which the\n// api-v2 additionally hand-mirrors in `common/constants/house-agent.ts`).\n// PR-3 replaces those copies with re-exports of THESE.\n\n/** Sentinel id for the synthetic \"CodeAgent Cloud (incluido)\" house agent. */\nexport const HOUSE_AGENT_ID = 'house-codeagent-cloud';\n\n/** Internal provider discriminator for the house agent. */\nexport const HOUSE_AGENT_PROVIDER = 'codeagent_cloud';\n\n/** White-label display strings — never mention the backend model. */\nexport const HOUSE_AGENT_NAME = 'CodeAgent Cloud';\nexport const HOUSE_AGENT_VENDOR = 'CodeAgent';\nexport const HOUSE_AGENT_SUBTITLE = 'Included — no setup';\n\n// ─── Public (LinkedAgent) id space ───────────────────────────────────────────\n\n/**\n * Public-facing linked-agent ids — the id space the `/api/agents/...`\n * endpoints and the mobile/web surfaces speak. The internal `AgentId`\n * (`'claude' | 'codex' | …`) is what the runtimes / provisioning key on.\n */\nexport type LinkedAgentId =\n | 'claude_code'\n | 'codex'\n | 'cursor'\n | 'aider'\n | 'coderabbit'\n | 'gemini'\n | 'kimi'\n | 'openrouter'\n | 'opencode'\n | typeof HOUSE_AGENT_ID;\n\nexport const LINKED_AGENT_IDS: readonly LinkedAgentId[] = [\n 'claude_code',\n 'codex',\n 'cursor',\n 'aider',\n 'coderabbit',\n 'gemini',\n 'kimi',\n 'openrouter',\n 'opencode',\n HOUSE_AGENT_ID,\n];\n\nexport function isLinkedAgentId(value: string): value is LinkedAgentId {\n return (LINKED_AGENT_IDS as readonly string[]).includes(value);\n}\n\n/**\n * Every public id → internal `AgentId`.\n *\n * ⚠️ RECONCILED ASYMMETRY — this map is the UNION of what the two sides\n * historically accepted:\n * - The backend's `agent-map.ts` accepts only the `LinkedAgentId` union\n * (incl. the house agent, whose runtime is Claude Code) — no bare\n * `claude`, no `copilot` (there is no public copilot LinkedAgentId).\n * - The CLI's self-hosted `agent-provisioning.ts` additionally accepts\n * bare `'claude'` and `'copilot'` (deploy payloads have carried\n * already-internal ids), but not the house agent.\n * Consumers that must REJECT ids outside their own historical set keep\n * their own guard on top (e.g. `isLinkedAgentId`).\n */\nexport const PUBLIC_TO_INTERNAL: Readonly<\n Record<LinkedAgentId | 'claude' | 'copilot', AgentId>\n> = {\n claude_code: 'claude',\n // CLI-side extra: self-hosted deploy payloads may carry the internal id.\n claude: 'claude',\n codex: 'codex',\n // CLI-side extra: copilot has no public LinkedAgentId (backend doesn't\n // expose it) but the self-hosted path accepts it.\n copilot: 'copilot',\n cursor: 'cursor',\n aider: 'aider',\n coderabbit: 'coderabbit',\n gemini: 'gemini',\n kimi: 'kimi',\n // opencode is a real, distinct agent (its own binary + ACP server) — it does\n // NOT map onto another runtime. Public id === internal id.\n opencode: 'opencode',\n // OpenRouter runs Claude Code under the hood, pointed at OpenRouter's hosted\n // \"Anthropic Skin\" (https://openrouter.ai/api — native Anthropic Messages\n // protocol) with the user's vaulted `sk-or-…` key as ANTHROPIC_AUTH_TOKEN,\n // instead of the house MiniMax proxy. Same internal runtime as the house\n // agent — `claude` — differing only in WHERE the Anthropic env is pointed (a\n // buildOpenRouterBootstrap variant). The endpoint is FIXED (hosted), so there\n // is no per-user base URL — just the key.\n openrouter: 'claude',\n // The house agent runs Claude Code under the hood (pointed at the\n // MiniMax proxy). Its internal runtime is therefore `claude`.\n [HOUSE_AGENT_ID]: 'claude',\n};\n\n/**\n * Internal → public. Partial: `copilot` has no public LinkedAgentId, and\n * `claude` maps back to `claude_code` (never the house agent — that\n * direction is intentionally lossy).\n */\nexport const INTERNAL_TO_PUBLIC: Readonly<Partial<Record<AgentId, LinkedAgentId>>> = {\n claude: 'claude_code',\n codex: 'codex',\n cursor: 'cursor',\n aider: 'aider',\n coderabbit: 'coderabbit',\n gemini: 'gemini',\n kimi: 'kimi',\n opencode: 'opencode',\n};\n\nfunction isPublicToInternalKey(v: string): v is LinkedAgentId | 'claude' | 'copilot' {\n // Not Object.hasOwn — the VS Code plugin's tsconfig lib predates ES2022.\n return Object.prototype.hasOwnProperty.call(PUBLIC_TO_INTERNAL, v);\n}\n\n/** Resolve a public/linked id to the internal `AgentId`, or null. */\nexport function publicToInternal(publicId: string): AgentId | null {\n return isPublicToInternalKey(publicId) ? PUBLIC_TO_INTERNAL[publicId] : null;\n}\n\n/** Resolve an internal `AgentId` to its public `LinkedAgentId`, or null. */\nexport function internalToPublic(internal: AgentId): LinkedAgentId | null {\n return INTERNAL_TO_PUBLIC[internal] ?? null;\n}\n\n// ─── Alias normalization ─────────────────────────────────────────────────────\n\n/** Prefix IDE plugins use for terminal-hosted agent ids. */\nexport const TERMINAL_AGENT_PREFIX = '__terminal__:';\n\n/**\n * Known aliases → internal `AgentId`. Union of every alias set that used\n * to live scattered across the surfaces: the public `claude_code` id, the\n * VS Code / Open VSX marketplace extension ids, and JetBrains plugin ids.\n */\nconst AGENT_ID_ALIASES: Readonly<Record<string, AgentId>> = {\n claude_code: 'claude',\n 'claude-code': 'claude',\n 'anthropic.claude-code': 'claude',\n 'anthropics.claude': 'claude',\n 'anthropic.claude-ce': 'claude',\n 'anthropic.claude': 'claude',\n 'com.anthropic.claudecode': 'claude',\n 'com.anthropic.claude': 'claude',\n 'openai.chatgpt': 'codex',\n 'coderabbitai.coderabbit-vscode': 'coderabbit',\n};\n\n/**\n * THE agent-id normalizer. Collapses every known spelling of an agent id\n * (registry id, public `claude_code` form, marketplace extension id,\n * `__terminal__:`-prefixed plugin id — case/whitespace tolerant) onto the\n * internal `AgentId`, or `null` when unknown.\n *\n * Deliberately does NOT:\n * - gate on `enabled` (callers that need availability check the\n * registry — see the VS Code wrapper `normalizeCliAgentId`);\n * - map the house agent (that's a runtime substitution, not an alias —\n * use {@link publicToInternal});\n * - fall back to anything. Unknown in → `null` out.\n */\nexport function normalizeAgentId(raw: string): AgentId | null {\n const value = (raw ?? '').trim().toLowerCase();\n if (!value) return null;\n\n if (isKnownAgentId(value)) return value;\n\n const unprefixed = value.startsWith(TERMINAL_AGENT_PREFIX)\n ? value.slice(TERMINAL_AGENT_PREFIX.length)\n : value;\n if (isKnownAgentId(unprefixed)) return unprefixed;\n\n return AGENT_ID_ALIASES[unprefixed] ?? null;\n}\n\n// ─── Headroom kind derivation ────────────────────────────────────────────────\n\n/**\n * The `headroom init --global <kind>` subcommand for an agent id, derived\n * from the registry's `headroomKind` flags — or `null` for unknown or\n * non-wrappable agents (cursor / gemini / aider / anything else).\n *\n * ⚠️ NEVER falls back to `'claude'`. The historical CLI fallback is how\n * the 2026-06 Cursor incident happened: an unsupported agent slipped\n * through, defaulted to `claude`, and `headroom wrap claude` launched\n * Claude Code instead of the user's agent. Callers that genuinely need a\n * default (e.g. picking an init subcommand AFTER the wrappable gate has\n * already passed) apply it themselves — see the CLI's\n * `agentIdToHeadroomKind` wrapper.\n *\n * Matching mirrors the historical predicates on BOTH sides (CLI\n * `isHeadroomSupportedAgent`, api-v2 `isHeadroomWrappableAgent`):\n * case-insensitive, `_`/`-` tolerant, prefix match — so `claude_code`,\n * `Claude-Code`, `codex_cli`, `copilot-cli` all resolve.\n */\nexport function headroomKindFor(agentId: string): HeadroomKind | null {\n const normalized = (agentId ?? '').toLowerCase().replace(/[_-]/g, '');\n if (!normalized) return null;\n for (const meta of Object.values(AGENT_REGISTRY)) {\n if (meta.headroomKind !== undefined && normalized.startsWith(meta.id)) {\n return meta.headroomKind;\n }\n }\n return null;\n}\n\n/**\n * Registry-derived replacement for the two scattered predicates\n * (`isHeadroomSupportedAgent` in the CLI, `isHeadroomWrappableAgent` in\n * api-v2). Accepts both id spaces (`claude_code` and `claude`).\n */\nexport function isHeadroomWrappable(agentId: string): boolean {\n return headroomKindFor(agentId) !== null;\n}\n","import type {\n IntegrationCategory,\n IntegrationDefinition,\n IntegrationId,\n} from './types';\n\n/**\n * The single source of truth for supported integrations. Adding one =\n * 1 entry here + 1 backend OAuth provider + icon. The `delivery` spec is\n * resolved into deploy manifests and executed as data by the CLI, so a new\n * MCP integration with no special logic needs no CLI release.\n */\nexport const INTEGRATION_REGISTRY: Record<IntegrationId, IntegrationDefinition> = {\n jira: {\n // Registry id kept 'jira' for id-stability (existing LinkedIntegration rows\n // stay valid — zero data migration); only the DISPLAY is \"Atlassian\". The\n // one mcp-atlassian server serves BOTH Jira and Confluence, so the same\n // entry now requests Confluence scopes too.\n id: 'jira',\n name: 'Atlassian',\n icon: 'jira',\n category: 'tracker',\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n // Jira + Confluence 3LO granular scopes. The Confluence pair\n // (read:confluence-content.all / write:confluence-content) is the set\n // mcp-atlassian's own Authentication docs recommend for full read+write\n // Confluence (matches its documented env scope string).\n scopes: [\n 'read:jira-work',\n 'write:jira-work',\n 'read:confluence-content.all',\n 'write:confluence-content',\n 'offline_access',\n ],\n },\n delivery: {\n mcp: {\n // mcp-atlassian in BYO-token mode (headless; credentials via env only).\n // Version PINNED to the exact release verified headless by Plan 2's\n // Docker integration test (apps/cli mcp-shim.int.test.ts).\n command: 'uvx',\n args: ['mcp-atlassian==0.22.1'],\n envMapping: {\n ATLASSIAN_OAUTH_ACCESS_TOKEN: 'accessToken',\n ATLASSIAN_OAUTH_CLOUD_ID: 'cloudId',\n },\n // Without ATLASSIAN_OAUTH_ENABLE=true, JiraConfig.from_env() raises\n // \"Missing required JIRA_URL\" (swallowed at server startup) and the\n // server silently registers ZERO Jira tools. The flag activates\n // mcp-atlassian's \"minimal OAuth config for user-provided tokens\"\n // mode — the BYO-token path the broker feeds. Static + non-secret.\n staticEnv: { ATLASSIAN_OAUTH_ENABLE: 'true' },\n },\n },\n },\n sentry: {\n id: 'sentry',\n name: 'Sentry',\n icon: 'sentry',\n category: 'observability',\n // LIVE — the Sentry OAuth Application (Confidential) is registered and\n // SENTRY_OAUTH_CLIENT_ID/SECRET/REDIRECT_URI are in Secret Manager\n // (prod+dev). The backend SentryOAuthProvider is config-gated (503 if\n // env unset) so this is safe even mid-rollout before the secrets mount.\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n // FULL read+write across every Sentry resource — the agent can read\n // issues/events/projects AND act (resolve/assign issues, manage\n // projects/teams/members, releases). `:write` implies `:read`. Admin\n // (destructive org/member management) is deliberately NOT requested.\n // ⚠️ Changing these requires the user to RE-LINK Sentry — the existing\n // token only carries whatever scopes it was granted at link time.\n scopes: [\n 'org:read',\n 'org:write',\n 'project:read',\n 'project:write',\n 'team:read',\n 'team:write',\n 'member:read',\n 'member:write',\n 'event:read',\n 'event:write',\n 'project:releases',\n ],\n },\n delivery: {\n mcp: {\n // Sentry's official stdio MCP server (Node). BYO-token headless: the\n // OAuth access token is fed via SENTRY_ACCESS_TOKEN and the host via\n // SENTRY_HOST (never argv — env only). Version PINNED; bump only\n // after re-verifying headless in the mcp-shim integration test.\n command: 'npx',\n // `--add-scopes` widens the server's default READ-ONLY tool surface to\n // include the write tools our OAuth scopes now grant (resolve/assign\n // issue, update project, etc.), so the agent exposes read AND write.\n args: [\n '-y',\n '@sentry/mcp-server@0.18.0',\n '--add-scopes=org:write,project:write,team:write,member:write,event:write',\n ],\n envMapping: {\n SENTRY_ACCESS_TOKEN: 'accessToken',\n SENTRY_HOST: 'host',\n },\n },\n },\n },\n linear: {\n id: 'linear',\n name: 'Linear',\n icon: 'linear',\n category: 'tracker',\n // LIVE — the Linear OAuth Application (Public + Confidential) is registered\n // and LINEAR_OAUTH_CLIENT_ID/SECRET/REDIRECT_URI are in Secret Manager\n // (prod+dev). The backend LinearOAuthProvider is config-gated (503 if env\n // unset) so this is safe even mid-rollout before the secrets mount.\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n // Linear's coarse scopes: `read` (all issues/projects/comments/cycles)\n // + `write` (create/update issues, comments, state). `write` implies the\n // create/update surface the agent's tools need. `admin` (destructive\n // workspace management) is deliberately NOT requested. ⚠️ Changing these\n // requires the user to RE-LINK Linear — the token only carries the scopes\n // granted at link time. Linear expects a COMMA-separated `scope` param.\n scopes: ['read', 'write'],\n },\n delivery: {\n mcp: {\n // mcp-linear (stdio, @linear/sdk) in BYO-token headless mode: the OAuth\n // access token is fed via LINEAR_API_KEY (env only, never argv) and the\n // Linear GraphQL API accepts it as the Authorization header directly.\n // Verified headless end-to-end (search_issues returned real issues) —\n // the OFFICIAL remote MCP (mcp.linear.app) can't be used here because it\n // forces its own interactive browser OAuth. Version PINNED; bump only\n // after re-verifying headless. Tools: search/get/create/update issue +\n // add comment (read + write).\n command: 'npx',\n args: ['-y', 'mcp-linear@0.1.8'],\n envMapping: {\n LINEAR_API_KEY: 'accessToken',\n },\n },\n },\n },\n github: {\n id: 'github',\n name: 'GitHub',\n icon: 'github',\n category: 'version_control',\n // LIVE. GitHub is the product's code substrate (codespaces + the PR\n // Command Center), and it was historically the ONE connection outside this\n // registry: its credential lives in a `ProviderToken` row rather than the\n // integrations vault, so it was rendered by a hand-written special-case row\n // and could not legally appear in a deploy's `integrationIds` (the manifest\n // resolver rejects unknown ids — a recurring bug class).\n //\n // `kind: 'connection'` closes that gap without re-plumbing OAuth: the entry\n // makes GitHub a first-class, categorised catalog row whose credential the\n // backend resolves from the SAME `ProviderToken` it always used, while the\n // connect/disconnect flow stays owned by the codespaces rail (the clients\n // route those two actions there). ⚠️ It is a REAL connection with a REAL\n // disconnect — do NOT treat it like `github_issues`, which merely derives\n // from it and has no actions of its own.\n enabled: true,\n auth: { kind: 'connection', connection: 'github' },\n // No MCP: a deployed box already has an authenticated `gh` on PATH.\n delivery: {},\n },\n gitlab: {\n id: 'gitlab',\n name: 'GitLab',\n icon: 'gitlab',\n category: 'version_control',\n // LIVE — a user-owned gitlab.com application (Confidential) with BOTH env\n // callbacks registered, so dev/prod share the client and differ only in\n // GITLAB_OAUTH_REDIRECT_URI. The backend GitLabOAuthProvider is\n // config-gated (503 if env unset), so this is safe mid-rollout.\n //\n // ⚠️ Unlike `github`, this is a NORMAL `oauth_redirect` integration: its\n // credential is vaulted here rather than living in a ProviderToken, because\n // nothing else in the product owns a GitLab connection (GitHub's lives on\n // the codespaces rail, which is why it's `kind: 'connection'`).\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n // GitLab has NO per-resource scopes — `api` is the only one that grants\n // merge-request WRITE (comment / approve / merge / close), so a\n // read-only alternative would make the whole MR surface useless.\n // `write_repository` is the git-over-HTTPS rail for the agent's push;\n // `api` already covers it for user tokens, but it costs nothing on a\n // consent screen that already says \"complete read/write access\" and\n // changing scopes later forces EVERY user to re-authorize.\n scopes: ['api', 'write_repository'],\n },\n // No MCP: the box's `git` is authenticated for push, and the MR surface is\n // served backend-side by the VCS provider — same shape as `github`.\n delivery: {},\n },\n github_issues: {\n id: 'github_issues',\n name: 'GitHub Issues',\n icon: 'github_issues',\n category: 'tracker',\n // LIVE, and the ONLY integration with NO link flow of its own: the\n // credential is DERIVED from the GitHub connection the user already made\n // for codespaces/PRs (`ProviderToken` provider='github-codespaces', which\n // carries `repo` scope — enough for the whole Issues surface). So there is\n // no OAuth app, no client id/secret, no Secret Manager entry and nothing\n // to configuration-gate. The backend auto-provisions the catalog row the\n // same way `ensureHouseAgentRow` does for the house agent, and resolves\n // the token live per call rather than vaulting a copy (it can't go stale,\n // and GitHub token refresh stays owned by exactly one place).\n enabled: true,\n auth: { kind: 'derived', derivedFrom: 'github' },\n // NO MCP server on purpose. Every other tracker needs one to give the agent\n // tools, but a deployed box ALREADY has an authenticated `gh` on PATH (the\n // codespace bootstrap exports GH_TOKEN), so `gh issue list/create/comment`\n // works with zero delivery. That also means nothing to pre-warm and no\n // third-party MCP package to pin and keep alive. The Start-from-Work-Item\n // side is served backend-side by the `TrackerProvider`, not by delivery.\n delivery: {},\n },\n slack: {\n id: 'slack',\n name: 'Slack',\n icon: 'slack',\n category: 'comms',\n // LIVE — the Slack app (OAuth v2, USER token — the agent acts AS THE USER)\n // is registered and\n // SLACK_OAUTH_CLIENT_ID/SECRET/REDIRECT_URI are in Secret Manager\n // (prod+dev). The backend SlackOAuthProvider is config-gated (503 if env\n // unset) so this is safe even mid-rollout before the secrets mount.\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n // Slack USER Token Scopes (OAuth v2). Read + write across channels,\n // groups, DMs: list/read history, post messages, react — all AS THE USER.\n // The backend provider requests these under `user_scope` (comma-separated)\n // and stores the authed_user `xoxp-…` token, so the agent sees everything\n // the user sees (no bot needs to be invited to channels). ⚠️ Changing\n // these requires the user to RE-AUTHORIZE the Slack app.\n scopes: [\n 'channels:read',\n 'channels:history',\n 'groups:read',\n 'groups:history',\n 'chat:write',\n 'reactions:read',\n 'reactions:write',\n 'users:read',\n 'im:read',\n 'im:history',\n 'mpim:read',\n 'mpim:history',\n // Message search across the user's channels/DMs (a user-only scope).\n 'search:read',\n ],\n },\n delivery: {\n mcp: {\n // Slack's official reference MCP server (Node). BYO-token headless: the\n // OAuth v2 USER token (xoxp-…) is fed via SLACK_BOT_TOKEN (the server's\n // env var name — it sends whatever token as `Authorization: Bearer`, and\n // Slack's Web API accepts a user token there) and the team id via\n // SLACK_TEAM_ID (env only, never argv). Version PINNED; bump only after\n // re-verifying headless. Tools: list_channels, post_message,\n // reply_to_thread, add_reaction, get_channel_history,\n // get_thread_replies, get_users, get_user_profile (read + write).\n command: 'npx',\n args: ['-y', '@modelcontextprotocol/server-slack@2025.4.25'],\n envMapping: {\n SLACK_BOT_TOKEN: 'accessToken',\n SLACK_TEAM_ID: 'teamId',\n },\n },\n },\n },\n microsoft_teams: {\n id: 'microsoft_teams',\n name: 'Microsoft Teams',\n icon: 'microsoft_teams',\n category: 'comms',\n // LIVE — Microsoft identity platform v2.0 OAuth (multi-tenant, Confidential).\n // The agent reads chats/channels + posts AS THE USER over Microsoft Graph.\n // MS_TEAMS_OAUTH_CLIENT_ID/SECRET/REDIRECT_URI are in Secret Manager (prod+dev);\n // the backend TeamsOAuthProvider is config-gated (503 if env unset).\n // ⚠️ Chat.* + ChannelMessage.Read.All (`.All`) require TENANT-ADMIN consent\n // (MC1163922, Oct-2025) — the connecting user must be an org admin, or their\n // IT admin approves the app for the tenant. Not a self-serve funnel like Slack.\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n // Delegated Microsoft Graph scopes. `offline_access` → rotating refresh\n // (Teams access tokens are ~1h). ⚠️ Changing these requires the user to\n // RE-LINK — the token carries only the scopes granted at link time.\n scopes: [\n 'User.Read',\n 'User.ReadBasic.All',\n 'Team.ReadBasic.All',\n 'Channel.ReadBasic.All',\n 'ChannelMessage.Read.All',\n 'ChannelMessage.Send',\n 'Chat.Read',\n 'Chat.ReadWrite',\n 'offline_access',\n ],\n },\n delivery: {\n mcp: {\n // @floriscornel/teams-mcp (Node): BYO Graph access token via AUTH_TOKEN\n // (env only, never argv). Headless. Version PINNED; bump only after\n // re-verifying headless. Tools: get/send channel + chat messages, replies.\n command: 'npx',\n args: ['-y', '@floriscornel/teams-mcp@0.9.0'],\n envMapping: {\n AUTH_TOKEN: 'accessToken',\n },\n },\n },\n },\n google_chat: {\n id: 'google_chat',\n name: 'Google Chat',\n icon: 'google_chat',\n category: 'comms',\n // COMING SOON — placeholder catalog entry (no OAuth provider / MCP yet). The\n // agent will post review pings + read spaces AS THE USER over the Google Chat\n // API once the provider lands. `enabled:false` → dimmed \"coming soon\" tile.\n enabled: false,\n auth: { kind: 'oauth_redirect' },\n delivery: {},\n },\n discord: {\n id: 'discord',\n name: 'Discord',\n icon: 'discord',\n category: 'comms',\n // LIVE — the DiscordOAuthProvider + DISCORD_* secrets (client id/secret,\n // bot token, per-env redirect) are registered; the backend provider is\n // config-gated (503 if env unset). Follows the SLACK pattern (OAuth redirect, zero friction),\n // EXCEPT Discord OAuth gives no per-install token: the user's `bot`-scope\n // authorize INVITES the app's single bot into their guild, and we store the\n // returned guild id as the per-user credential. The broker injects the app's\n // BOT token as `accessToken` (from config), so `DISCORD_TOKEN` = that bot\n // token and `DISCORD_GUILD_ID` = the user's guild.\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n // `bot` invites the app's bot into the user's guild (Guild Install);\n // `guilds` lets the exchange read the guild name for display. The bot's\n // channel permissions (View Channels + Read Message History + Send\n // Messages + Send Messages in Threads) are set on the app's bot, NOT here.\n // ⚠️ The app's bot MUST have the Message Content privileged intent enabled\n // or read_messages returns empty content.\n scopes: ['bot', 'guilds'],\n },\n delivery: {\n mcp: {\n // mcp-discord (barryyip0625) — Node stdio, BYO bot token headless via the\n // DISCORD_TOKEN env (never argv). Version PINNED; bump only after\n // re-verifying headless. Tools: list/read channels + messages, send\n // message, reply in thread, add reaction. `DISCORD_GUILD_ID` scopes it to\n // the user's invited guild.\n command: 'npx',\n args: ['-y', 'mcp-discord@1.3.4'],\n envMapping: {\n DISCORD_TOKEN: 'accessToken',\n DISCORD_GUILD_ID: 'guildId',\n },\n },\n },\n },\n resend: {\n id: 'resend',\n name: 'Resend',\n icon: 'resend',\n category: 'comms',\n // LIVE — api_key (like Azure DevOps): the user pastes a Resend API key\n // (`re_…`); Resend has NO OAuth, so there's no OAuth app / GSM secret /\n // config-gated 503 — a backend VALIDATOR proves the key against the Resend\n // API and vaults it. ⚠️ SEND-ONLY email → `sendOnly: true` keeps it OUT of\n // From-Conversation (comms conversation sources need readable threads; Resend\n // has none) while still being a linkable comms tool the agent uses via MCP.\n enabled: true,\n sendOnly: true,\n auth: {\n kind: 'api_key',\n fields: [\n {\n key: 'accessToken',\n label: 'API Key',\n placeholder: 're_xxxxxxxxxxxxxxxx',\n secret: true,\n help: 'Create in Resend → API Keys (https://resend.com/api-keys). \"Sending access\" is enough.',\n },\n ],\n },\n delivery: {\n mcp: {\n // The OFFICIAL resend-mcp (Node) in BYO-token mode: the API key is fed\n // via RESEND_API_KEY (env only, never argv). Version PINNED; bump only\n // after re-verifying headless. Tools: send email + contacts/broadcasts/\n // domains.\n command: 'npx',\n args: ['-y', 'resend-mcp@2.6.1'],\n envMapping: {\n RESEND_API_KEY: 'accessToken',\n },\n },\n },\n },\n posthog: {\n id: 'posthog',\n name: 'PostHog',\n icon: 'posthog',\n category: 'observability',\n // LIVE — api_key: the user pastes a PostHog Personal API Key (phx_…, created\n // with the \"MCP Server\" preset). PostHog's MCP is HOSTED-ONLY\n // (mcp.posthog.com) over HTTP, and its stdio bridge (mcp-remote) forces its\n // own browser OAuth — incompatible with our headless broker. So delivery\n // uses the shim's HTTP transport: it relays to the hosted MCP with the key\n // as a Bearer header (exactly Cursor's {url, headers} config). No OAuth, no\n // GSM secret; a backend VALIDATOR proves the key + vaults it.\n enabled: true,\n auth: {\n kind: 'api_key',\n fields: [\n {\n key: 'accessToken',\n label: 'Personal API Key',\n placeholder: 'phx_xxxxxxxxxxxxxxxx',\n secret: true,\n help: 'PostHog → Settings → Personal API keys → create with the \"MCP Server\" preset (scopes it to a project).',\n },\n ],\n },\n delivery: {\n mcp: {\n // HTTP transport (not a spawned stdio server) — the shim relays to\n // PostHog's hosted MCP with the key as a Bearer header. The key stays\n // server-side of the shim, never on argv.\n command: '',\n args: [],\n envMapping: {},\n httpUrl: 'https://mcp.posthog.com/mcp',\n httpHeaders: { Authorization: 'Bearer {accessToken}' },\n },\n },\n },\n datadog: {\n id: 'datadog',\n name: 'Datadog',\n icon: 'datadog',\n category: 'observability',\n // LIVE — api_key: the user pastes a Datadog API key (org-scoped) + an\n // Application key (user-scoped, grants the read scopes) + their regional\n // site. Datadog's OFFICIAL hosted MCP (mcp.<site>) accepts these as headers\n // headless (verified) — no OAuth, no GSM secret. Delivery uses the shim's\n // HTTP transport with the site templated into the URL and both keys sent as\n // DD-API-KEY / DD-APPLICATION-KEY headers.\n enabled: true,\n auth: {\n kind: 'api_key',\n fields: [\n {\n key: 'accessToken',\n label: 'API Key',\n placeholder: 'Datadog API key',\n secret: true,\n help: 'Organization API key — Datadog → Organization Settings → API Keys.',\n },\n {\n key: 'appKey',\n label: 'Application Key',\n placeholder: 'Datadog application key',\n secret: true,\n help: 'User Application key (grants the read scopes) — Datadog → Organization Settings → Application Keys.',\n },\n {\n key: 'host',\n label: 'Site',\n placeholder: 'datadoghq.com',\n secret: false,\n help: 'Your Datadog site — US1: datadoghq.com · EU: datadoghq.eu · US3: us3.datadoghq.com · US5: us5.datadoghq.com · AP1: ap1.datadoghq.com.',\n },\n ],\n },\n delivery: {\n mcp: {\n // HTTP transport — the shim relays to Datadog's hosted MCP for the\n // user's site, with both keys as headers. Keys stay server-side of the\n // shim, never on argv.\n command: '',\n args: [],\n envMapping: {},\n httpUrl: 'https://mcp.{host}/api/unstable/mcp-server/mcp',\n httpHeaders: {\n 'DD-API-KEY': '{accessToken}',\n 'DD-APPLICATION-KEY': '{appKey}',\n },\n },\n },\n },\n notion: {\n id: 'notion',\n name: 'Notion',\n icon: 'notion',\n category: 'docs',\n // LIVE — the Notion public OAuth integration is registered and\n // NOTION_OAUTH_CLIENT_ID/SECRET/REDIRECT_URI are in Secret Manager\n // (prod+dev). The backend NotionOAuthProvider is config-gated (503 if env\n // unset) so this is safe even mid-rollout before the secrets mount.\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n // Notion does NOT use per-request OAuth scopes — access is governed by\n // the integration's configured CAPABILITIES (read/update/insert content\n // + read user info), set once on the Notion integration, not passed in\n // the authorize URL. So there is no `scope` param to request here.\n scopes: [],\n },\n delivery: {\n mcp: {\n // Notion's OFFICIAL stdio MCP server (Node). BYO-token headless: the\n // OAuth access token is fed via NOTION_TOKEN and the server sends it as\n // `Authorization: Bearer` + `Notion-Version: 2022-06-28` (env only,\n // never argv). No discriminator — the token alone authenticates its\n // workspace. Version PINNED; bump only after re-verifying headless.\n command: 'npx',\n args: ['-y', '@notionhq/notion-mcp-server@2.4.1'],\n envMapping: {\n NOTION_TOKEN: 'accessToken',\n },\n },\n },\n },\n azure_devops: {\n id: 'azure_devops',\n name: 'Azure DevOps',\n icon: 'azure_devops',\n category: 'tracker',\n // LIVE — the FIRST api_key (PAT) integration. No OAuth: Azure DevOps OAuth\n // apps are being sunset by Microsoft in favor of Entra ID, and PATs are the\n // native, reliable ADO auth. The user pastes their org URL + a PAT; the\n // backend validates against the ADO REST API and vaults it. No env secrets\n // to configure (config-gated 503 doesn't apply — there's no OAuth app).\n enabled: true,\n auth: {\n kind: 'api_key',\n fields: [\n {\n key: 'orgUrl',\n label: 'Organization URL',\n placeholder: 'https://dev.azure.com/your-org',\n secret: false,\n help: 'Your Azure DevOps organization URL — e.g. https://dev.azure.com/contoso',\n },\n {\n key: 'accessToken',\n label: 'Personal Access Token',\n placeholder: 'Paste your PAT',\n secret: true,\n help: 'Create in Azure DevOps → User settings → Personal access tokens. Recommended scopes: Work Items (Read & Write), Code (Read), Build (Read), Project and Team (Read).',\n },\n ],\n },\n delivery: {\n mcp: {\n // The @tiberriver256 ADO MCP server (Node) in PAT mode: the PAT is fed\n // via AZURE_DEVOPS_PAT (Basic auth) + the org via AZURE_DEVOPS_ORG_URL\n // (env only, never argv). AZURE_DEVOPS_AUTH_METHOD=pat pins the PAT path\n // (the alternative, azure-identity, uses DefaultAzureCredential and\n // would ignore our token). Version PINNED; bump only after re-verifying\n // headless.\n command: 'npx',\n args: ['-y', '@tiberriver256/mcp-server-azure-devops@0.1.46'],\n envMapping: {\n AZURE_DEVOPS_PAT: 'accessToken',\n AZURE_DEVOPS_ORG_URL: 'orgUrl',\n },\n staticEnv: { AZURE_DEVOPS_AUTH_METHOD: 'pat' },\n },\n },\n },\n figma: {\n id: 'figma',\n name: 'Figma',\n icon: 'figma',\n category: 'design',\n // LIVE. OAuth is the PRIMARY flow (app pending Figma's review — until it\n // passes the authorize URL errors \"OAuth app doesn't exist\"), so a PAT\n // fallback (`apiKeyFallback`) ships alongside it and works TODAY: the user\n // pastes a Figma personal access token. Same OAuth-primary + PAT-secondary\n // shape as GitHub. Flip of the OAuth review is transparent (bead codeagent-m4ix).\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n // Granular READ-ONLY scopes (legacy `files:read` is deprecated for\n // OAuth). Asset export (GET /v1/images) rides file_content:read.\n // file_variables:read is Enterprise-only and would break linking for\n // normal accounts — deliberately excluded. ⚠️ Changing these requires\n // the user to RE-LINK Figma.\n scopes: [\n 'current_user:read',\n 'file_content:read',\n 'file_metadata:read',\n 'file_dev_resources:read',\n 'library_content:read',\n ],\n // SECONDARY: paste a Figma personal access token instead of OAuth. The\n // PAT uses the `X-Figma-Token` header, so the delivery env var differs\n // from OAuth (`FIGMA_API_KEY` vs `FIGMA_OAUTH_TOKEN`) — hence its own\n // envMapping, used when the credential was linked via PAT.\n apiKeyFallback: {\n fields: [\n {\n key: 'accessToken',\n label: 'Figma personal access token',\n placeholder: 'figd_…',\n secret: true,\n help: 'Figma → Settings → Security → Personal access tokens (read-only scopes).',\n },\n ],\n envMapping: {\n FIGMA_API_KEY: 'accessToken',\n },\n },\n },\n delivery: {\n mcp: {\n // Framelink figma-developer-mcp (Node, stdio) in BYO-token headless\n // mode — the ONLY known Figma MCP server that accepts an OAuth\n // Bearer token: FIGMA_OAUTH_TOKEN → `Authorization: Bearer` (env\n // only, never argv). Figma's official servers can't be used here\n // (remote = interactive OAuth + client allowlist; Dev Mode =\n // desktop app). Version PINNED; bump only after re-verifying\n // headless. Tools: get_figma_data (condensed layout extraction) +\n // download_figma_images (asset export).\n command: 'npx',\n args: ['-y', 'figma-developer-mcp@0.13.2', '--stdio', '--no-telemetry'],\n envMapping: {\n FIGMA_OAUTH_TOKEN: 'accessToken',\n },\n },\n },\n },\n stitch: {\n id: 'stitch',\n name: 'Stitch',\n icon: 'stitch',\n category: 'design',\n // LIVE — api_key: the user pastes a Stitch API key (Google Stitch →\n // Settings → API Key). Stitch's MCP is hosted-only (stitch.googleapis.com)\n // and accepts the key as the `X-Goog-Api-Key` header headless (verified) —\n // no OAuth, no GSM secret. Delivery uses the shim's HTTP transport.\n // (Stitch ALSO supports Google OAuth 2.0, but that's a follow-up — our model\n // is one auth kind per integration and api_key is the simple, headless path.)\n enabled: true,\n auth: {\n kind: 'api_key',\n fields: [\n {\n key: 'accessToken',\n label: 'API Key',\n placeholder: 'Stitch API key',\n secret: true,\n help: 'Google Stitch → profile menu → Stitch Settings → API Key → Create Key.',\n },\n ],\n },\n delivery: {\n mcp: {\n // HTTP transport — the shim relays to Stitch's hosted MCP with the key\n // as the X-Goog-Api-Key header. Key stays server-side of the shim.\n command: '',\n args: [],\n envMapping: {},\n httpUrl: 'https://stitch.googleapis.com/mcp',\n httpHeaders: { 'X-Goog-Api-Key': '{accessToken}' },\n },\n },\n },\n vercel: {\n id: 'vercel',\n name: 'Vercel',\n icon: 'vercel',\n category: 'infrastructure',\n // DARK — OAuth (PKCE public client). The user authorizes our registered\n // Vercel OAuth app; the access token calls Vercel's OFFICIAL hosted remote\n // MCP (mcp.vercel.com, Streamable HTTP) as a Bearer header — the SAME shim\n // HTTP transport as PostHog/Datadog. Vercel MCP is approved-clients + per-\n // connection OAuth consent, but that consent is the ONE-TIME authorize (not\n // per MCP request), so a backend-brokered access token works headless.\n // Endpoints (from mcp.vercel.com/.well-known/oauth-protected-resource →\n // vercel.com/.well-known/oauth-authorization-server): authorize\n // https://vercel.com/oauth/authorize, token\n // https://vercel.com/api/login/oauth/token, PKCE S256, token-endpoint auth\n // 'none' (public). LIVE — the headless Bearer against mcp.vercel.com was\n // verified (2026-07-27).\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n scopes: ['openid', 'offline_access'],\n },\n delivery: {\n mcp: {\n command: '',\n args: [],\n envMapping: {},\n httpUrl: 'https://mcp.vercel.com',\n httpHeaders: { Authorization: 'Bearer {accessToken}' },\n },\n },\n },\n trello: {\n id: 'trello',\n name: 'Trello',\n icon: 'trello',\n category: 'tracker',\n // LIVE — api_key rail (like Azure DevOps). Trello's auth is a developer API\n // key + a user token, NOT an OAuth2 code exchange: its token returns via a\n // URL fragment / OAuth 1.0a, incompatible with our server-side broker. The\n // user pastes both; the backend validates against the Trello REST API and\n // vaults them. No OAuth app, no GSM secrets, no config-gated 503.\n enabled: true,\n auth: {\n kind: 'api_key',\n fields: [\n {\n key: 'apiKey',\n label: 'API Key',\n placeholder: 'Your Trello API key',\n secret: false,\n help: 'Create a Power-Up at https://trello.com/power-ups/admin and copy its API key.',\n },\n {\n key: 'accessToken',\n label: 'Token',\n placeholder: 'Your Trello token',\n secret: true,\n help: 'On the API-key page, click the \"Token\" link to authorize + generate a token (scope read,write, never-expiring).',\n },\n ],\n },\n delivery: {\n mcp: {\n // mcp-server-trello (Node): TRELLO_API_KEY + TRELLO_TOKEN via env only,\n // never argv. Version PINNED; bump only after re-verifying headless.\n command: 'npx',\n args: ['-y', 'mcp-server-trello@1.0.4'],\n envMapping: {\n TRELLO_API_KEY: 'apiKey',\n TRELLO_TOKEN: 'accessToken',\n },\n },\n },\n },\n clickup: {\n id: 'clickup',\n name: 'ClickUp',\n icon: 'clickup',\n category: 'tracker',\n // DARK — OAuth 2.0 (authorization code). ClickUp's token DOES NOT expire and\n // has NO refresh token, and its OAuth has NO granular scopes (the app gets\n // the workspaces the user authorizes). enabled:false until the OAuth app is\n // registered (client_id/secret) + secrets land + Step-8 live-verify.\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n scopes: [],\n },\n delivery: {\n mcp: {\n // @taazkareem/clickup-mcp-server (Node): CLICKUP_API_KEY (the OAuth\n // access token — ClickUp accepts it in the Authorization header) +\n // CLICKUP_TEAM_ID (the workspace id, captured at exchange) via env only.\n // Version PINNED; bump only after re-verifying headless.\n command: 'npx',\n args: ['-y', '@taazkareem/clickup-mcp-server@0.14.4'],\n envMapping: {\n CLICKUP_API_KEY: 'accessToken',\n CLICKUP_TEAM_ID: 'teamId',\n },\n },\n },\n },\n n8n: {\n id: 'n8n',\n name: 'n8n',\n icon: 'n8n',\n category: 'automation',\n // LIVE — api_key rail (like Trello / Azure DevOps). n8n auth is an API key\n // (Settings → n8n API) + the instance URL (n8n.cloud or self-hosted). The\n // user pastes both; the backend validates against the n8n REST API + vaults\n // them. No OAuth app, no GSM secrets, no config-gated 503.\n // ⚠️ The instance MUST be PUBLICLY reachable (n8n.cloud or a public self-host)\n // — a localhost/private n8n can't be reached from the deploy box.\n enabled: true,\n auth: {\n kind: 'api_key',\n fields: [\n {\n key: 'instanceUrl',\n label: 'Instance URL',\n placeholder: 'https://your-workspace.app.n8n.cloud',\n secret: false,\n help: 'Your n8n base URL — n8n.cloud or your public self-hosted instance.',\n },\n {\n key: 'accessToken',\n label: 'API Key',\n placeholder: 'n8n_api_…',\n secret: true,\n help: 'Create in n8n → Settings → n8n API → Create an API key.',\n },\n ],\n },\n delivery: {\n mcp: {\n // n8n-mcp (Node): N8N_API_URL + N8N_API_KEY via env only, never argv.\n // Version PINNED; bump only after re-verifying headless.\n command: 'npx',\n args: ['-y', 'n8n-mcp@2.66.2'],\n envMapping: {\n N8N_API_URL: 'instanceUrl',\n N8N_API_KEY: 'accessToken',\n },\n // stdio transport (the CLI mcp-shim spawns it over stdio, not HTTP).\n staticEnv: { MCP_MODE: 'stdio' },\n },\n },\n },\n postman: {\n id: 'postman',\n name: 'Postman',\n icon: 'postman',\n category: 'api_tools',\n // LIVE — api_key rail. Postman auth is a personal API key (Settings → API\n // keys). The user pastes it; the backend validates via GET /me + vaults it.\n // No OAuth (Postman's API has no user OAuth), no secrets, no config-503.\n enabled: true,\n auth: {\n kind: 'api_key',\n fields: [\n {\n key: 'accessToken',\n label: 'API Key',\n placeholder: 'PMAK-…',\n secret: true,\n help: 'Postman → Settings → API keys → Generate API Key.',\n },\n ],\n },\n delivery: {\n mcp: {\n // Postman's OFFICIAL stdio MCP server (Node). BYO key via POSTMAN_API_KEY\n // (env only). Version PINNED; bump only after re-verifying headless.\n command: 'npx',\n args: ['-y', '@postman/postman-mcp-server@2.11.2'],\n envMapping: {\n POSTMAN_API_KEY: 'accessToken',\n },\n },\n },\n },\n mixpanel: {\n id: 'mixpanel',\n name: 'Mixpanel',\n icon: 'mixpanel',\n category: 'analytics',\n // LIVE — api_key rail. Mixpanel query/export auth is a SERVICE ACCOUNT\n // (username + password, Basic auth) scoped to a project (project id). The\n // user pastes all three; the backend validates + vaults them. No OAuth\n // redirect (service accounts are machine-to-machine), no GSM secrets.\n enabled: true,\n auth: {\n kind: 'api_key',\n fields: [\n {\n key: 'accessToken',\n label: 'Service Account Username',\n placeholder: 'your-sa.mp-service-account',\n secret: false,\n help: 'Mixpanel → Organization Settings → Service Accounts → create one with project access.',\n },\n {\n key: 'secretKey',\n label: 'Service Account Secret',\n placeholder: 'the service account secret',\n secret: true,\n help: 'Shown once when you create the service account — copy it then.',\n },\n {\n key: 'projectId',\n label: 'Project ID',\n placeholder: '1234567',\n secret: false,\n help: 'Mixpanel → Project Settings → Project ID.',\n },\n ],\n },\n delivery: {\n mcp: {\n // @mendeel/mcp-mixpanel (Node): the service account + project id via env\n // only (MIXPANEL_SERVICE_ACCOUNT_USERNAME/PASSWORD + MIXPANEL_PROJECT_ID).\n // Version PINNED; bump only after re-verifying headless. Default region US.\n command: 'npx',\n args: ['-y', '@mendeel/mcp-mixpanel@1.0.4'],\n envMapping: {\n MIXPANEL_SERVICE_ACCOUNT_USERNAME: 'accessToken',\n MIXPANEL_SERVICE_ACCOUNT_PASSWORD: 'secretKey',\n MIXPANEL_PROJECT_ID: 'projectId',\n },\n },\n },\n },\n supabase: {\n id: 'supabase',\n name: 'Supabase',\n icon: 'supabase',\n category: 'database',\n // LIVE — OAuth 2.0 (authorization code, Confidential). authorize\n // api.supabase.com/v1/oauth/authorize; token /v1/oauth/token\n // (form-urlencoded, client creds via HTTP Basic); rotating refresh tokens.\n // The OAuth app (Organization → OAuth Apps) is registered and\n // SUPABASE_OAUTH_CLIENT_ID/SECRET/REDIRECT_URI are in Secret Manager\n // (prod+dev); the config-gated 503 keeps this safe mid-rollout.\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n // ⚠️ Supabase scopes are configured ON THE OAUTH APP, NOT sent in the\n // authorize URL — this list is informational (what the app was granted).\n scopes: ['all'],\n },\n delivery: {\n mcp: {\n // Supabase's OFFICIAL MCP server (Node). The OAuth access token IS a\n // Management API token → fed via SUPABASE_ACCESS_TOKEN (env only, never\n // argv). Version PINNED; bump only after re-verifying headless.\n command: 'npx',\n args: ['-y', '@supabase/mcp-server-supabase@0.9.0'],\n envMapping: {\n SUPABASE_ACCESS_TOKEN: 'accessToken',\n },\n },\n },\n },\n convex: {\n id: 'convex',\n name: 'Convex',\n icon: 'convex',\n category: 'database',\n // LIVE — api_key rail (the user pastes a Convex DEPLOY KEY) + a BUILT-IN MCP.\n // ⚠️ Convex's OWN `convex mcp start` server CANNOT work headlessly: it\n // categorically requires an INTERACTIVE `npx convex dev`/`login`\n // personal-access-token and REJECTS every headless credential — OAuth token,\n // dev/prod deploy key, self-hosted admin key, with every flag\n // (--deployment/--prod/--project-dir) — all return \"Not Authorized: Run\n // `npx convex dev` to login\" (verified LIVE, exhaustively, 2026-08-03; it\n // also HANGS on the failed auth = the mareado/no-Stop wedge). BUT the\n // deployment's own HTTP admin API accepts the deploy key directly\n // (`Authorization: Convex <deployKey>` → 200; verified). So we serve Convex's\n // tools ourselves via a BUILT-IN MCP (delivery.builtin) against that admin\n // API — see apps/cli/src/integrations/convex-admin-mcp.ts. The user still\n // pastes a deploy key (Dashboard → Project Settings → Deploy Keys).\n enabled: true,\n auth: {\n kind: 'api_key',\n fields: [\n {\n key: 'accessToken',\n label: 'Convex deploy key',\n placeholder: 'prod:happy-animal-123|eyJ2…',\n secret: true,\n help: 'Convex Dashboard → Project Settings → Deploy Keys → Generate a deploy key.',\n },\n ],\n },\n delivery: {\n mcp: {\n // BUILT-IN MCP: the CLI serves Convex's tools itself against the\n // deployment admin REST API with the brokered deploy key (Convex's own\n // `convex mcp start` rejects all headless creds — see the note above).\n // No spawned child, no npx. command/args/envMapping are empty.\n builtin: 'convex-admin',\n command: '',\n args: [],\n envMapping: {},\n },\n },\n },\n confluence: {\n id: 'confluence',\n name: 'Confluence',\n icon: 'confluence',\n category: 'docs',\n // Confluence is served by the SAME Atlassian OAuth + the SAME `mcp-atlassian`\n // server as Jira (the `jira` entry already requests Confluence scopes). This\n // entry is a DISPLAY ALIAS so Confluence is explicit in the Knowledge tab: it\n // BORROWS jira's vaulted credential (`aliasOf: 'jira'`) and has no link flow of\n // its own — connecting Atlassian (the Jira/Atlassian card) lights it up, and\n // the backend resolves its token by delegating to `jira`.\n enabled: true,\n auth: { kind: 'derived', aliasOf: 'jira' },\n delivery: {\n mcp: {\n // Same mcp-atlassian server + BYO-token env as Jira — it exposes the\n // Confluence tools when the deploy selects Confluence without Jira. If\n // BOTH are selected the deploy manifest dedupes identical MCP specs.\n command: 'uvx',\n args: ['mcp-atlassian==0.22.1'],\n envMapping: {\n ATLASSIAN_OAUTH_ACCESS_TOKEN: 'accessToken',\n ATLASSIAN_OAUTH_CLOUD_ID: 'cloudId',\n },\n staticEnv: { ATLASSIAN_OAUTH_ENABLE: 'true' },\n },\n },\n },\n cloudflare: {\n id: 'cloudflare',\n name: 'Cloudflare',\n icon: 'cloudflare',\n category: 'infrastructure',\n // LIVE — Cloudflare self-managed OAuth (dash.cloudflare.com OAuth server,\n // the same one Wrangler uses). Confidential (Client Secret POST). The agent\n // inspects/deploys Workers, Pages, DNS, KV, R2 + reads analytics/logs on the\n // user's account. CLOUDFLARE_OAUTH_CLIENT_ID/SECRET/REDIRECT_URI in Secret\n // Manager (prod+dev); the backend CloudflareOAuthProvider is config-gated (503).\n enabled: true,\n auth: {\n kind: 'oauth_redirect',\n // Cloudflare OAuth scope IDs == API-token permission-group names (dot\n // form, e.g. `workers-scripts.write`). ⚠️ MUST exactly match the scopes\n // configured on the OAuth client (b2b4b87e…). `offline_access` → refresh.\n scopes: [\n // Developer Platform (Workers / Pages / KV / R2 / D1 / observability)\n 'd1.read',\n 'page.read',\n 'page.write',\n 'workers-ci.read',\n 'workers-ci.write',\n 'containers.read',\n 'containers.write',\n 'workers-kv-storage.read',\n 'workers-kv-storage.write',\n 'workers-observability.read',\n 'workers-observability-telemetry.write',\n 'workers-observability.write',\n 'r2-catalog.read',\n 'r2-catalog.write',\n 'r2-catalog-sql.read',\n 'workers-r2-bucket-item.read',\n 'workers-r2-bucket-item.write',\n 'workers-r2.read',\n 'workers-r2.write',\n 'workers-routes.read',\n 'workers-routes.write',\n 'workers-scripts.read',\n 'workers-scripts.write',\n 'workers-tail.read',\n // DNS & Zones\n 'account-dns-settings.read',\n 'account-dns-settings.write',\n 'dns-firewall.read',\n 'dns-firewall.write',\n 'dns.read',\n 'dns-view.read',\n 'dns.write',\n 'registrar-domains.admin',\n 'registrar-sandbox-domains.admin',\n 'zone-dns-settings.read',\n 'zone-dns-settings.write',\n 'zone.read',\n 'zone-settings.read',\n 'zone-settings.write',\n // Analytics & Logs\n 'account-analytics.read',\n 'analytics.read',\n 'account-logs.read',\n 'logs.read',\n 'radar.read',\n // Account & Billing\n 'account-api-gateway.read',\n 'account-settings.read',\n 'memberships.read',\n 'notifications.read',\n 'user-details.read',\n 'offline_access',\n ],\n },\n delivery: {\n mcp: {\n // Headless stdio Cloudflare MCP (Cloudflare API v4) — BYO token via env,\n // the OAuth access token works as a Bearer against api.cloudflare.com.\n // The account id is the discriminator for account-level ops. PINNED.\n command: 'npx',\n args: ['-y', '@itunified.io/mcp-cloudflare@2026.4.10-1'],\n envMapping: {\n CLOUDFLARE_API_TOKEN: 'accessToken',\n CLOUDFLARE_ACCOUNT_ID: 'accountId',\n },\n },\n },\n },\n};\n\nexport function getEnabledIntegrations(): IntegrationDefinition[] {\n return Object.values(INTEGRATION_REGISTRY).filter((m) => m.enabled);\n}\n\nexport function getIntegration(id: IntegrationId): IntegrationDefinition {\n const meta = INTEGRATION_REGISTRY[id];\n if (!meta) throw new Error(`Unknown integration id: ${id}`);\n return meta;\n}\n\nexport function isKnownIntegrationId(id: string): id is IntegrationId {\n return id in INTEGRATION_REGISTRY;\n}\n\nexport function getIntegrationsByCategory(\n category: IntegrationCategory,\n): IntegrationDefinition[] {\n return Object.values(INTEGRATION_REGISTRY).filter(\n (m) => m.category === category && m.enabled,\n );\n}\n","/**\n * Agent Toolkits — centralized integration branding catalog.\n * Spec: docs/superpowers/specs/2026-07-10-agent-toolkits-integrations-design.md\n *\n * Shared is pure TS (no React, no platform imports), so this catalog is DATA:\n * raw SVG markup strings + display metadata. Renderers stay per-app (RN\n * `SvgXml` on mobile, inline/`<img>` on web) — this module never renders\n * anything itself.\n *\n * `logoSvg` values are the OFFICIAL brand marks. jira/slack are the\n * multicolor originals (from the vendor). Every other entry (the 6 live\n * integrations' single-path marks + the whole COMING SOON set) is a\n * simple-icons single-path mark that ships with a black fill by default —\n * that fill has been rewritten here to #FFFFFF so the mark reads on the\n * dark surfaces this catalog targets; consumers may re-tint via\n * `brandColor` (e.g. an SVG `<mask>`/currentColor wrapper) if a different\n * treatment is needed. `pendo` + `amplitude` are NOT in simple-icons\n * (brand-guideline restrictions) so they carry faithful hand-authored\n * monochrome marks in the same 24×24 single-path shape.\n */\nexport interface IntegrationBranding {\n /** Stable id — registry ids ('jira') plus upcoming ones not yet in IntegrationId. */\n id: string;\n name: string;\n vendor: string;\n /** One-line value prop shown under the name. */\n tagline: string;\n /** Brand accent for tinted containers/pills on dark surfaces. */\n brandColor: string;\n /** Official logo as raw SVG markup (renderers: SvgXml on RN, inline/img on web). */\n logoSvg: string;\n}\n\nexport const INTEGRATION_BRANDING: Record<string, IntegrationBranding> = {\n // GitLab — the official multicolour Tanuki (from GitLab's own header markup),\n // like jira/slack. Kept verbatim: the four paths are the shape + the two\n // cheeks + the chin, and flattening them to one colour loses the mark.\n // `aria-hidden`/`role`/`class` were stripped — the renderers own a11y.\n gitlab: {\n id: 'gitlab',\n name: 'GitLab',\n vendor: 'GitLab',\n tagline: 'Merge requests, reviews & CI',\n brandColor: '#FC6D26',\n logoSvg:\n '<svg width=\"25\" height=\"24\" viewBox=\"0 0 25 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m24.507 9.5-.034-.09L21.082.562a.896.896 0 0 0-1.694.091l-2.29 7.01H7.825L5.535.653a.898.898 0 0 0-1.694-.09L.451 9.411.416 9.5a6.297 6.297 0 0 0 2.09 7.278l.012.01.03.022 5.16 3.867 2.56 1.935 1.554 1.176a1.051 1.051 0 0 0 1.268 0l1.555-1.176 2.56-1.935 5.197-3.89.014-.01A6.297 6.297 0 0 0 24.507 9.5Z\" fill=\"#E24329\"/><path d=\"m24.507 9.5-.034-.09a11.44 11.44 0 0 0-4.56 2.051l-7.447 5.632 4.742 3.584 5.197-3.89.014-.01A6.297 6.297 0 0 0 24.507 9.5Z\" fill=\"#FC6D26\"/><path d=\"m7.707 20.677 2.56 1.935 1.555 1.176a1.051 1.051 0 0 0 1.268 0l1.555-1.176 2.56-1.935-4.743-3.584-4.755 3.584Z\" fill=\"#FCA326\"/><path d=\"M5.01 11.461a11.43 11.43 0 0 0-4.56-2.05L.416 9.5a6.297 6.297 0 0 0 2.09 7.278l.012.01.03.022 5.16 3.867 4.745-3.584-7.444-5.632Z\" fill=\"#FC6D26\"/></svg>',\n },\n // GitHub — now a REAL `IntegrationId` (`version_control`, `kind: 'connection'`).\n // It started life here as a brand-only entry, back when GitHub was rendered by\n // a hand-written special-case row; the mark is unchanged, it's just also the\n // catalog row's logo now. Still used by the PR/MR Command Center surfaces.\n github: {\n id: 'github',\n name: 'GitHub',\n vendor: 'GitHub',\n tagline: 'Pull requests, reviews & merges',\n brandColor: '#FFFFFF',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>GitHub</title><path fill=\"#FFFFFF\" d=\"M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\"/></svg>',\n },\n // GitHub Issues — the `tracker`-category toolkit integration (a real\n // `IntegrationId`, unlike the `github` entry above). Same official mark, its\n // own name/tagline so the catalog row reads as the issue tracker rather than\n // the code host.\n github_issues: {\n id: 'github_issues',\n name: 'GitHub Issues',\n vendor: 'GitHub',\n tagline: 'Issues & project tracking',\n brandColor: '#FFFFFF',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>GitHub</title><path fill=\"#FFFFFF\" d=\"M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\"/></svg>',\n },\n // npm — a brand-only entry (NOT an `IntegrationId`): the registry the\n // codeam-cli ships to. Present so surfaces like the Wiki can render the\n // official npm mark from the ONE shared catalog instead of a loose asset.\n npm: {\n id: 'npm',\n name: 'npm',\n vendor: 'npm, Inc.',\n tagline: 'The Node package registry',\n brandColor: '#CB3837',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>npm</title><path fill=\"#CB3837\" d=\"M1.763 0C.786 0 0 .786 0 1.763v20.474C0 23.214.786 24 1.763 24h20.474c.977 0 1.763-.786 1.763-1.763V1.763C24 .786 23.214 0 22.237 0zM5.13 5.323l13.837.019-.009 13.836h-3.464l.01-10.382h-3.456L12.08 19.17H5.113z\"/></svg>',\n },\n jira: {\n // Branding key kept 'jira' for id-stability; DISPLAY rebranded to Atlassian\n // (the one integration fronts both Jira + Confluence via mcp-atlassian).\n id: 'jira',\n name: 'Atlassian',\n vendor: 'Atlassian',\n tagline: 'Jira · Confluence',\n brandColor: '#357DE8',\n logoSvg:\n '<svg viewBox=\"0 0 32 32\" height=\"32\" xmlns=\"http://www.w3.org/2000/svg\" focusable=\"false\" aria-hidden=\"true\"><defs><linearGradient id=\"uid18\" x1=\"14.8402\" y1=\"15.8324\" x2=\"8.6599\" y2=\"26.5369\" gradientUnits=\"userSpaceOnUse\"><stop stop-color=\"#2684FF\" stop-opacity=\"0.4\" offset=\"0%\"></stop><stop stop-color=\"#2684FF\" offset=\"0.9228\"></stop></linearGradient></defs><path fill=\"url(#uid18)\" d=\"M11.6397 14.0398C11.2789 13.643 10.7378 13.679 10.4852 14.148L4.64091 25.8728C4.42446 26.3418 4.74912 26.8829 5.25419 26.8829H13.4074C13.6599 26.8829 13.9125 26.7386 14.0207 26.4861C15.7885 22.8424 14.7061 17.3227 11.6397 14.0398Z\"></path><path fill=\"#357DE8\" d=\"M15.9343 3.36124C12.6513 8.55622 12.8678 14.2923 15.0324 18.6215C17.1969 22.9506 18.8565 26.2336 18.9647 26.4861C19.0729 26.7386 19.3254 26.8829 19.578 26.8829H27.7312C28.2363 26.8829 28.597 26.3418 28.3445 25.8728C28.3445 25.8728 17.3774 3.93846 17.0887 3.39732C16.8723 2.89225 16.259 2.85618 15.9343 3.36124Z\"></path></svg>',\n },\n confluence: {\n id: 'confluence',\n name: 'Confluence',\n vendor: 'Atlassian',\n tagline: 'Part of Atlassian · docs & wiki',\n brandColor: '#1868DB',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Confluence</title><path fill=\"#FFFFFF\" d=\"M.87 18.257c-.248.382-.53.875-.763 1.245a.764.764 0 0 0 .255 1.04l4.965 3.054a.764.764 0 0 0 1.058-.26c.199-.332.454-.763.733-1.221 1.967-3.247 3.945-2.853 7.508-1.146l4.957 2.337a.764.764 0 0 0 1.028-.382l2.364-5.346a.764.764 0 0 0-.382-1c-1.048-.494-3.124-1.478-4.965-2.361C10.911 10.97 5.224 11.185.87 18.257zM23.131 5.743c.249-.405.531-.875.764-1.25a.764.764 0 0 0-.256-1.034L18.675.404a.764.764 0 0 0-1.058.26c-.195.335-.451.763-.734 1.225-1.966 3.246-3.945 2.85-7.508 1.146L4.437.694a.764.764 0 0 0-1.027.382L1.046 6.422a.764.764 0 0 0 .382 1c1.039.49 3.105 1.467 4.965 2.361 6.698 3.246 12.392 3.029 16.738-4.04z\"/></svg>',\n },\n slack: {\n id: 'slack',\n name: 'Slack',\n vendor: 'Salesforce',\n tagline: 'Team messaging & alerts',\n brandColor: '#E01E5A',\n logoSvg:\n '<svg width=\"54\" height=\"54\" viewBox=\"0 0 54 54\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><g clip-path=\"url(#clip0_4127_70105)\"><path d=\"M11.379 33.9993C11.379 37.1358 8.84512 39.6507 5.7276 39.6507C2.61008 39.6507 0.0572205 37.1168 0.0572205 33.9993C0.0572205 30.8817 2.5911 28.3479 5.70862 28.3479H11.36V33.9993H11.379Z\" fill=\"#E01E5A\"/><path d=\"M14.1962 33.9997C14.1962 30.8632 16.7301 28.3483 19.8476 28.3483C22.9651 28.3483 25.499 30.8822 25.499 33.9997V48.1353C25.499 51.2718 22.9651 53.7867 19.8476 53.7867C16.7301 53.7867 14.1962 51.2718 14.1962 48.1353V33.9997Z\" fill=\"#E01E5A\"/><path d=\"M19.8662 11.2673C16.7296 11.2673 14.2148 8.73347 14.2148 5.61594C14.2148 2.49842 16.7486 -0.0354538 19.8662 -0.0354538C22.9837 -0.0354538 25.5175 2.49842 25.5175 5.61594V11.2673H19.8662Z\" fill=\"#36C5F0\"/><path d=\"M19.8682 14.1334C23.0047 14.1334 25.5196 16.6673 25.5196 19.7848C25.5196 22.9023 22.9857 25.4362 19.8682 25.4362H5.67566C2.53916 25.4362 0.0242615 22.9023 0.0242615 19.7848C0.0242615 16.6673 2.55814 14.1334 5.67566 14.1334H19.8682Z\" fill=\"#36C5F0\"/><path d=\"M42.5323 19.7853C42.5323 16.6488 45.0662 14.1339 48.1837 14.1339C51.3012 14.1339 53.8351 16.6678 53.8351 19.7853C53.8351 22.9028 51.3012 25.4367 48.1837 25.4367H42.5323V19.7853Z\" fill=\"#2EB67D\"/><path d=\"M39.7126 19.7934C39.7126 22.9299 37.1787 25.4448 34.0612 25.4448C30.9436 25.4448 28.4098 22.911 28.4098 19.7934V5.61986C28.4098 2.48336 30.9436 -0.0315399 34.0612 -0.0315399C37.1787 -0.0315399 39.7126 2.48336 39.7126 5.61986V19.7934Z\" fill=\"#2EB67D\"/><path d=\"M34.0376 42.482C37.1741 42.482 39.689 45.0158 39.689 48.1334C39.689 51.2509 37.1552 53.7848 34.0376 53.7848C30.9201 53.7848 28.3862 51.2509 28.3862 48.1334V42.482H34.0376Z\" fill=\"#ECB22E\"/><path d=\"M34.0381 39.6507C30.9016 39.6507 28.3867 37.1168 28.3867 33.9993C28.3867 30.8818 30.9206 28.3479 34.0381 28.3479H48.2306C51.3671 28.3479 53.882 30.8818 53.882 33.9993C53.882 37.1168 51.3482 39.6507 48.2306 39.6507H34.0381Z\" fill=\"#ECB22E\"/></g><defs><clipPath id=\"clip0_4127_70105\"><rect width=\"54\" height=\"54\" fill=\"white\"/></clipPath></defs></svg>',\n },\n microsoft_teams: {\n id: 'microsoft_teams',\n name: 'Microsoft Teams',\n vendor: 'Microsoft',\n tagline: 'Team chat & collaboration',\n brandColor: '#6264A7',\n logoSvg:\n '<svg width=\"54\" height=\"54\" viewBox=\"0 0 54 54\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M36.6 22h12.3c1 0 1.8.8 1.8 1.8v10.4a7.2 7.2 0 0 1-7.2 7.2 7.2 7.2 0 0 1-7.2-7.2V22z\" fill=\"#5059C9\"/><circle cx=\"44\" cy=\"14.4\" r=\"4.6\" fill=\"#5059C9\"/><circle cx=\"27.2\" cy=\"12\" r=\"6.6\" fill=\"#7B83EB\"/><path d=\"M35.4 22H16.9c-1 .02-1.8.86-1.78 1.86v11.9A12 12 0 0 0 26.9 47.6a12 12 0 0 0 10.28-11.84V23.86c.02-1-.78-1.84-1.78-1.86z\" fill=\"#7B83EB\"/><path opacity=\".12\" d=\"M28 22v18.4a1.86 1.86 0 0 1-1.72 1.84H15.72A12.7 12.7 0 0 1 15.12 38V23.86c-.02-1 .78-1.84 1.78-1.86H28z\" fill=\"#000\"/><rect x=\"2.5\" y=\"15\" width=\"23.5\" height=\"23.5\" rx=\"2.2\" fill=\"#4B53BC\"/><path d=\"M19.8 21.4H8.7v3.05h4v11.1h3.1v-11.1h4V21.4z\" fill=\"#fff\"/></svg>',\n },\n google_chat: {\n id: 'google_chat',\n name: 'Google Chat',\n vendor: 'Google',\n tagline: 'Team messaging & spaces',\n brandColor: '#00AC47',\n logoSvg:\n '<svg width=\"54\" height=\"54\" viewBox=\"0 0 54 54\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M46 6H8a3.5 3.5 0 0 0-3.5 3.5v26A3.5 3.5 0 0 0 8 39h4.5v8.2a1.3 1.3 0 0 0 2.15 1L26 39h20a3.5 3.5 0 0 0 3.5-3.5v-26A3.5 3.5 0 0 0 46 6z\" fill=\"#00AC47\"/><circle cx=\"19.5\" cy=\"22.5\" r=\"3.1\" fill=\"#fff\"/><circle cx=\"34.5\" cy=\"22.5\" r=\"3.1\" fill=\"#fff\"/></svg>',\n },\n discord: {\n id: 'discord',\n name: 'Discord',\n vendor: 'Discord',\n tagline: 'Voice, video & text chat',\n brandColor: '#5865F2',\n logoSvg:\n '<svg width=\"54\" height=\"54\" viewBox=\"0 0 54 54\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M43.6 12.2A38 38 0 0 0 34.1 9.3a26 26 0 0 0-1.2 2.5 35.3 35.3 0 0 0-10.6 0 26 26 0 0 0-1.2-2.5 38 38 0 0 0-9.5 2.9C4.6 21.2 3 30 3.8 38.6a38.4 38.4 0 0 0 11.6 5.9 28 28 0 0 0 2.5-4 24.8 24.8 0 0 1-3.9-1.9c.33-.24.65-.5.95-.75a27.5 27.5 0 0 0 23.5 0c.3.27.62.52.95.75a24.8 24.8 0 0 1-3.9 1.9 28 28 0 0 0 2.5 4 38.3 38.3 0 0 0 11.6-5.9c.94-9.9-1.6-18.6-6.6-26.4zM19.4 33.3c-2.3 0-4.2-2.1-4.2-4.7s1.85-4.7 4.2-4.7 4.24 2.13 4.2 4.7c0 2.6-1.87 4.7-4.2 4.7zm15.3 0c-2.3 0-4.2-2.1-4.2-4.7s1.85-4.7 4.2-4.7 4.24 2.13 4.2 4.7c0 2.6-1.85 4.7-4.2 4.7z\" fill=\"#5865F2\"/></svg>',\n },\n linear: {\n id: 'linear',\n name: 'Linear',\n vendor: 'Linear',\n tagline: 'Issue tracking for product teams',\n brandColor: '#5E6AD2',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Linear</title><path fill=\"#FFFFFF\" d=\"M2.886 4.18A11.982 11.982 0 0 1 11.99 0C18.624 0 24 5.376 24 12.009c0 3.64-1.62 6.903-4.18 9.105L2.887 4.18ZM1.817 5.626l16.556 16.556c-.524.33-1.075.62-1.65.866L.951 7.277c.247-.575.537-1.126.866-1.65ZM.322 9.163l14.515 14.515c-.71.172-1.443.282-2.195.322L0 11.358a12 12 0 0 1 .322-2.195Zm-.17 4.862 9.823 9.824a12.02 12.02 0 0 1-9.824-9.824Z\"/></svg>',\n },\n sentry: {\n id: 'sentry',\n name: 'Sentry',\n vendor: 'Sentry',\n tagline: 'Error & performance monitoring',\n brandColor: '#7B68C7',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 50 44\" xmlns=\"http://www.w3.org/2000/svg\"><title>Sentry</title><path fill=\"#FFFFFF\" d=\"M29,2.26a4.67,4.67,0,0,0-8,0L14.42,13.53A32.21,32.21,0,0,1,32.17,40.19H27.55A27.68,27.68,0,0,0,12.09,17.47L6,28a15.92,15.92,0,0,1,9.23,12.17H4.62A.76.76,0,0,1,4,39.06l2.94-5a10.74,10.74,0,0,0-3.36-1.9l-2.91,5a4.54,4.54,0,0,0,1.69,6.24A4.66,4.66,0,0,0,4.62,44H19.15a19.4,19.4,0,0,0-8-17.31l2.31-4A23.87,23.87,0,0,1,23.76,44H36.07a35.88,35.88,0,0,0-16.41-31.8l4.67-8a.77.77,0,0,1,1.05-.27c.53.29,20.29,34.77,20.66,35.17a.76.76,0,0,1-.68,1.13H40.6q.09,1.91,0,3.81h4.78A4.59,4.59,0,0,0,50,39.43a4.49,4.49,0,0,0-.62-2.28Z\"></path></svg>',\n },\n notion: {\n id: 'notion',\n name: 'Notion',\n vendor: 'Notion Labs',\n tagline: 'Docs, wikis & knowledge',\n brandColor: '#E8E7E4',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Notion</title><path fill=\"#FFFFFF\" d=\"M4.459 4.208c.746.606 1.026.56 2.428.466l13.215-.793c.28 0 .047-.28-.046-.326L17.86 1.968c-.42-.326-.981-.7-2.055-.607L3.01 2.295c-.466.046-.56.28-.374.466zm.793 3.08v13.904c0 .747.373 1.027 1.214.98l14.523-.84c.841-.046.935-.56.935-1.167V6.354c0-.606-.233-.933-.748-.887l-15.177.887c-.56.047-.747.327-.747.933zm14.337.745c.093.42 0 .84-.42.888l-.7.14v10.264c-.608.327-1.168.514-1.635.514-.748 0-.935-.234-1.495-.933l-4.577-7.186v6.952L12.21 19s0 .84-1.168.84l-3.222.186c-.093-.186 0-.653.327-.746l.84-.233V9.854L7.822 9.76c-.094-.42.14-1.026.793-1.073l3.456-.233 4.764 7.279v-6.44l-1.215-.139c-.093-.514.28-.887.747-.933zM1.936 1.035l13.31-.98c1.634-.14 2.055-.047 3.082.7l4.249 2.986c.7.513.934.653.934 1.213v16.378c0 1.026-.373 1.634-1.68 1.726l-15.458.934c-.98.047-1.448-.093-1.962-.747l-3.129-4.06c-.56-.747-.793-1.306-.793-1.96V2.667c0-.839.374-1.54 1.447-1.632z\"/></svg>',\n },\n azure_devops: {\n id: 'azure_devops',\n name: 'Azure DevOps',\n vendor: 'Microsoft',\n tagline: 'Boards, Repos & Pipelines',\n brandColor: '#0078D7',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Azure DevOps</title><path fill=\"#FFFFFF\" d=\"M0 8.877L2.247 5.91l8.405-3.416V.022l7.37 5.393L2.966 8.338v8.225L0 15.707zm24-4.45v14.651l-5.753 4.9-9.303-3.057v3.056l-5.978-7.416 15.057 1.798V5.415z\"/></svg>',\n },\n gmail: {\n id: 'gmail',\n name: 'Gmail',\n vendor: 'Google',\n tagline: 'Read, search & send email',\n brandColor: '#EA4335',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Gmail</title><path fill=\"#FFFFFF\" d=\"M24 5.457v13.909c0 .904-.732 1.636-1.636 1.636h-3.819V11.73L12 16.64l-6.545-4.91v9.273H1.636A1.636 1.636 0 0 1 0 19.366V5.457c0-2.023 2.309-3.178 3.927-1.964L5.455 4.64 12 9.548l6.545-4.91 1.528-1.145C21.69 2.28 24 3.434 24 5.457z\"/></svg>',\n },\n posthog: {\n id: 'posthog',\n name: 'PostHog',\n vendor: 'PostHog',\n tagline: 'Product analytics & feature flags',\n brandColor: '#1D4AFF',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>PostHog</title><path fill=\"#FFFFFF\" d=\"M9.854 14.5 5 9.647.854 5.5A.5.5 0 0 0 0 5.854V8.44a.5.5 0 0 0 .146.353L5 13.647l.147.146L9.854 18.5l.146.147v-.049c.065.03.134.049.207.049h2.586a.5.5 0 0 0 .353-.854L9.854 14.5zm0-5-4-4a.487.487 0 0 0-.409-.144.515.515 0 0 0-.356.21.493.493 0 0 0-.089.288V8.44a.5.5 0 0 0 .147.353l9 9a.5.5 0 0 0 .853-.354v-2.585a.5.5 0 0 0-.146-.354l-5-5zm1-4a.5.5 0 0 0-.854.354V8.44a.5.5 0 0 0 .147.353l4 4a.5.5 0 0 0 .853-.354V9.854a.5.5 0 0 0-.146-.354l-4-4zm12.647 11.515a3.863 3.863 0 0 1-2.232-1.1l-4.708-4.707a.5.5 0 0 0-.854.354v6.585a.5.5 0 0 0 .5.5H23.5a.5.5 0 0 0 .5-.5v-.6c0-.276-.225-.497-.499-.532zm-5.394.032a.8.8 0 1 1 0-1.6.8.8 0 0 1 0 1.6zM.854 15.5a.5.5 0 0 0-.854.354v2.293a.5.5 0 0 0 .5.5h2.293c.222 0 .39-.135.462-.309a.493.493 0 0 0-.109-.545L.854 15.501zM5 14.647.854 10.5a.5.5 0 0 0-.854.353v2.586a.5.5 0 0 0 .146.353L4.854 18.5l.146.147h2.793a.5.5 0 0 0 .353-.854L5 14.647z\"/></svg>',\n },\n clickup: {\n id: 'clickup',\n name: 'ClickUp',\n vendor: 'ClickUp',\n tagline: 'Tasks, docs & project management',\n brandColor: '#7B68EE',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>ClickUp</title><path fill=\"#FFFFFF\" d=\"M2 18.439l3.69-2.828c1.961 2.56 4.044 3.739 6.363 3.739 2.307 0 4.33-1.166 6.203-3.704L22 18.405C19.298 22.065 15.941 24 12.053 24 8.178 24 4.788 22.078 2 18.439zM12.04 6.15l-6.568 5.66-3.036-3.52L12.055 0l9.543 8.296-3.05 3.509z\"/></svg>',\n },\n figma: {\n id: 'figma',\n name: 'Figma',\n vendor: 'Figma',\n tagline: 'Designs, files & comments',\n brandColor: '#F24E1E',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Figma</title><path fill=\"#FFFFFF\" d=\"M15.852 8.981h-4.588V0h4.588c2.476 0 4.49 2.014 4.49 4.49s-2.014 4.491-4.49 4.491zM12.735 7.51h3.117c1.665 0 3.019-1.355 3.019-3.019s-1.355-3.019-3.019-3.019h-3.117V7.51zm0 1.471H8.148c-2.476 0-4.49-2.014-4.49-4.49S5.672 0 8.148 0h4.588v8.981zm-4.587-7.51c-1.665 0-3.019 1.355-3.019 3.019s1.354 3.02 3.019 3.02h3.117V1.471H8.148zm4.587 15.019H8.148c-2.476 0-4.49-2.014-4.49-4.49s2.014-4.49 4.49-4.49h4.588v8.98zM8.148 8.981c-1.665 0-3.019 1.355-3.019 3.019s1.355 3.019 3.019 3.019h3.117V8.981H8.148zM8.172 24c-2.489 0-4.515-2.014-4.515-4.49s2.014-4.49 4.49-4.49h4.588v4.441c0 2.503-2.047 4.539-4.563 4.539zm-.024-7.51a3.023 3.023 0 0 0-3.019 3.019c0 1.665 1.365 3.019 3.044 3.019 1.705 0 3.093-1.376 3.093-3.068v-2.97H8.148zm7.704 0h-.098c-2.476 0-4.49-2.014-4.49-4.49s2.014-4.49 4.49-4.49h.098c2.476 0 4.49 2.014 4.49 4.49s-2.014 4.49-4.49 4.49zm-.097-7.509c-1.665 0-3.019 1.355-3.019 3.019s1.355 3.019 3.019 3.019h.098c1.665 0 3.019-1.355 3.019-3.019s-1.355-3.019-3.019-3.019h-.098z\"/></svg>',\n },\n stitch: {\n id: 'stitch',\n name: 'Stitch',\n vendor: 'Google',\n tagline: 'AI UI design → code',\n brandColor: '#A855F7',\n // A simple 4-point design \"sparkle\" mark (Google Stitch has no simple-icons\n // entry) — white on dark, re-tintable via brandColor like the other marks.\n logoSvg:\n '<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path fill=\"#FFFFFF\" d=\"M12 1.5l1.9 6.3a4 4 0 0 0 2.3 2.3l6.3 1.9-6.3 1.9a4 4 0 0 0-2.3 2.3L12 22.5l-1.9-6.3a4 4 0 0 0-2.3-2.3L1.5 12l6.3-1.9a4 4 0 0 0 2.3-2.3L12 1.5z\"/></svg>',\n },\n trello: {\n id: 'trello',\n name: 'Trello',\n vendor: 'Atlassian',\n tagline: 'Boards, lists & cards',\n brandColor: '#0052CC',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Trello</title><path fill=\"#FFFFFF\" d=\"M21.147 0H2.853A2.86 2.86 0 000 2.853v18.294A2.86 2.86 0 002.853 24h18.294A2.86 2.86 0 0024 21.147V2.853A2.86 2.86 0 0021.147 0zM10.34 17.287a.953.953 0 01-.953.953h-4a.954.954 0 01-.954-.953V5.38a.953.953 0 01.954-.953h4a.954.954 0 01.953.953zm9.233-5.467a.944.944 0 01-.953.947h-4a.947.947 0 01-.953-.947V5.38a.953.953 0 01.953-.953h4a.954.954 0 01.953.953z\"/></svg>',\n },\n resend: {\n id: 'resend',\n name: 'Resend',\n vendor: 'Resend',\n tagline: 'Transactional email delivery',\n brandColor: '#FFFFFF',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Resend</title><path fill=\"#FFFFFF\" d=\"M14.679 0c4.648 0 7.413 2.765 7.413 6.434s-2.765 6.434-7.413 6.434H12.33L24 24h-8.245l-8.88-8.44c-.636-.588-.93-1.273-.93-1.86 0-.831.587-1.565 1.713-1.883l4.574-1.224c1.737-.465 2.936-1.81 2.936-3.572 0-2.153-1.761-3.4-3.939-3.4H0V0z\"/></svg>',\n },\n vercel: {\n id: 'vercel',\n name: 'Vercel',\n vendor: 'Vercel',\n tagline: 'Deployments, logs & projects',\n brandColor: '#FFFFFF',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Vercel</title><path fill=\"#FFFFFF\" d=\"m12 1.608 12 20.784H0Z\"/></svg>',\n },\n supabase: {\n id: 'supabase',\n name: 'Supabase',\n vendor: 'Supabase',\n tagline: 'Postgres, auth & storage',\n brandColor: '#3FCF8E',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Supabase</title><path fill=\"#FFFFFF\" d=\"M11.9 1.036c-.015-.986-1.26-1.41-1.874-.637L.764 12.05C-.33 13.427.65 15.455 2.409 15.455h9.579l.113 7.51c.014.985 1.259 1.408 1.873.636l9.262-11.653c1.093-1.375.113-3.403-1.645-3.403h-9.642z\"/></svg>',\n },\n convex: {\n id: 'convex',\n name: 'Convex',\n vendor: 'Convex',\n tagline: 'Reactive database & backend',\n brandColor: '#EE342F',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Convex</title><path fill=\"#FFFFFF\" d=\"M15.09 18.916c3.488-.387 6.776-2.246 8.586-5.348-.857 7.673-9.247 12.522-16.095 9.545a3.47 3.47 0 0 1-1.547-1.314c-1.539-2.417-2.044-5.492-1.318-8.282 2.077 3.584 6.3 5.78 10.374 5.399m-10.501-7.65c-1.414 3.266-1.475 7.092.258 10.24-6.1-4.59-6.033-14.41-.074-18.953a3.44 3.44 0 0 1 1.893-.707c2.825-.15 5.695.942 7.708 2.977-4.09.04-8.073 2.66-9.785 6.442m11.757-5.437C14.283 2.951 11.053.992 7.515.933c6.84-3.105 15.253 1.929 16.17 9.37a3.6 3.6 0 0 1-.334 2.02c-1.278 2.594-3.647 4.607-6.416 5.352 2.029-3.763 1.778-8.36-.589-11.847\"/></svg>',\n },\n cloudflare: {\n id: 'cloudflare',\n name: 'Cloudflare',\n vendor: 'Cloudflare',\n tagline: 'Workers, Pages, DNS & R2',\n brandColor: '#F6821F',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 460 271.2\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\"><title>Cloudflare</title><path fill=\"#FBAD41\" d=\"M328.6,125.6c-0.8,0-1.5,0.6-1.8,1.4l-4.8,16.7c-2.1,7.2-1.3,13.8,2.2,18.7c3.2,4.5,8.6,7.1,15.1,7.4l26.2,1.6c0.8,0,1.5,0.4,1.9,1c0.4,0.6,0.5,1.5,0.3,2.2c-0.4,1.2-1.6,2.1-2.9,2.2l-27.3,1.6c-14.8,0.7-30.7,12.6-36.3,27.2l-2,5.1c-0.4,1,0.3,2,1.4,2h93.8c1.1,0,2.1-0.7,2.4-1.8c1.6-5.8,2.5-11.9,2.5-18.2c0-37-30.2-67.2-67.3-67.2C330.9,125.5,329.7,125.5,328.6,125.6z\"/><path fill=\"#F6821F\" d=\"M292.8,204.4c2.1-7.2,1.3-13.8-2.2-18.7c-3.2-4.5-8.6-7.1-15.1-7.4l-123.1-1.6c-0.8,0-1.5-0.4-1.9-1s-0.5-1.4-0.3-2.2c0.4-1.2,1.6-2.1,2.9-2.2l124.2-1.6c14.7-0.7,30.7-12.6,36.3-27.2l7.1-18.5c0.3-0.8,0.4-1.6,0.2-2.4c-8-36.2-40.3-63.2-78.9-63.2c-35.6,0-65.8,23-76.6,54.9c-7-5.2-15.9-8-25.5-7.1c-17.1,1.7-30.8,15.4-32.5,32.5c-0.4,4.4-0.1,8.7,0.9,12.7c-27.9,0.8-50.2,23.6-50.2,51.7c0,2.5,0.2,5,0.5,7.5c0.2,1.2,1.2,2.1,2.4,2.1h227.2c1.3,0,2.5-0.9,2.9-2.2L292.8,204.4z\"/></svg>',\n },\n asana: {\n id: 'asana',\n name: 'Asana',\n vendor: 'Asana',\n tagline: 'Tasks, projects & workflows',\n brandColor: '#F06A6A',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Asana</title><path fill=\"#FFFFFF\" d=\"M18.78 12.653c-2.882 0-5.22 2.336-5.22 5.22s2.338 5.22 5.22 5.22 5.22-2.34 5.22-5.22-2.336-5.22-5.22-5.22zm-13.56 0c-2.88 0-5.22 2.337-5.22 5.22s2.338 5.22 5.22 5.22 5.22-2.338 5.22-5.22-2.336-5.22-5.22-5.22zm12-6.525c0 2.883-2.337 5.22-5.22 5.22-2.882 0-5.22-2.337-5.22-5.22 0-2.88 2.338-5.22 5.22-5.22 2.883 0 5.22 2.34 5.22 5.22z\"/></svg>',\n },\n postman: {\n id: 'postman',\n name: 'Postman',\n vendor: 'Postman',\n tagline: 'APIs, collections & environments',\n brandColor: '#FF6C37',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Postman</title><path fill=\"#FFFFFF\" d=\"M13.527.099C6.955-.744.942 3.9.099 10.473c-.843 6.572 3.8 12.584 10.373 13.428 6.573.843 12.587-3.801 13.428-10.374C24.744 6.955 20.101.943 13.527.099zm2.471 7.485a.855.855 0 0 0-.593.25l-4.453 4.453-.307-.307-.643-.643c4.389-4.376 5.18-4.418 5.996-3.753zm-4.863 4.861l4.44-4.44a.62.62 0 1 1 .847.903l-4.699 4.125-.588-.588zm.33.694l-1.1.238a.06.06 0 0 1-.067-.032.06.06 0 0 1 .01-.073l.645-.645.512.512zm-2.803-.459l1.172-1.172.879.878-1.979.426a.074.074 0 0 1-.085-.039.072.072 0 0 1 .013-.093zm-3.646 6.058a.076.076 0 0 1-.069-.083.077.077 0 0 1 .022-.046h.002l.946-.946 1.222 1.222-2.123-.147zm2.425-1.256a.228.228 0 0 0-.117.256l.203.865a.125.125 0 0 1-.211.117h-.003l-.934-.934-.294-.295 3.762-3.758 1.82-.393.874.874c-1.255 1.102-2.971 2.201-5.1 3.268zm5.279-3.428h-.002l-.839-.839 4.699-4.125a.952.952 0 0 0 .119-.127c-.148 1.345-2.029 3.245-3.977 5.091zm3.657-6.46l-.003-.002a1.822 1.822 0 0 1 2.459-2.684l-1.61 1.613a.119.119 0 0 0 0 .169l1.247 1.247a1.817 1.817 0 0 1-2.093-.343zm2.578 0a1.714 1.714 0 0 1-.271.218h-.001l-1.207-1.207 1.533-1.533c.661.72.637 1.832-.054 2.522zM18.855 6.05a.143.143 0 0 0-.053.157.416.416 0 0 1-.053.45.14.14 0 0 0 .023.197.141.141 0 0 0 .084.03.14.14 0 0 0 .106-.05.691.691 0 0 0 .087-.751.138.138 0 0 0-.194-.033z\"/></svg>',\n },\n n8n: {\n id: 'n8n',\n name: 'n8n',\n vendor: 'n8n',\n tagline: 'Workflow automation & webhooks',\n brandColor: '#EA4B71',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>n8n</title><path fill=\"#FFFFFF\" d=\"M21.4737 5.6842c-1.1772 0-2.1663.8051-2.4468 1.8947h-2.8955c-1.235 0-2.289.893-2.492 2.111l-.1038.623a1.263 1.263 0 0 1-1.246 1.0555H11.289c-.2805-1.0896-1.2696-1.8947-2.4468-1.8947s-2.1663.8051-2.4467 1.8947H4.973c-.2805-1.0896-1.2696-1.8947-2.4468-1.8947C1.1311 9.4737 0 10.6047 0 12s1.131 2.5263 2.5263 2.5263c1.1772 0 2.1663-.8051 2.4468-1.8947h1.4223c.2804 1.0896 1.2696 1.8947 2.4467 1.8947 1.1772 0 2.1663-.8051 2.4468-1.8947h1.0008a1.263 1.263 0 0 1 1.2459 1.0555l.1038.623c.203 1.218 1.257 2.111 2.492 2.111h.3692c.2804 1.0895 1.2696 1.8947 2.4468 1.8947 1.3952 0 2.5263-1.131 2.5263-2.5263s-1.131-2.5263-2.5263-2.5263c-1.1772 0-2.1664.805-2.4468 1.8947h-.3692a1.263 1.263 0 0 1-1.246-1.0555l-.1037-.623A2.52 2.52 0 0 0 13.9607 12a2.52 2.52 0 0 0 .821-1.4794l.1038-.623a1.263 1.263 0 0 1 1.2459-1.0555h2.8955c.2805 1.0896 1.2696 1.8947 2.4468 1.8947 1.3952 0 2.5263-1.131 2.5263-2.5263s-1.131-2.5263-2.5263-2.5263m0 1.2632a1.263 1.263 0 0 1 1.2631 1.2631 1.263 1.263 0 0 1-1.2631 1.2632 1.263 1.263 0 0 1-1.2632-1.2632 1.263 1.263 0 0 1 1.2632-1.2631M2.5263 10.7368A1.263 1.263 0 0 1 3.7895 12a1.263 1.263 0 0 1-1.2632 1.2632A1.263 1.263 0 0 1 1.2632 12a1.263 1.263 0 0 1 1.2631-1.2632m6.3158 0A1.263 1.263 0 0 1 10.1053 12a1.263 1.263 0 0 1-1.2632 1.2632A1.263 1.263 0 0 1 7.579 12a1.263 1.263 0 0 1 1.2632-1.2632m10.1053 3.7895a1.263 1.263 0 0 1 1.2631 1.2632 1.263 1.263 0 0 1-1.2631 1.2631 1.263 1.263 0 0 1-1.2632-1.2631 1.263 1.263 0 0 1 1.2632-1.2632\"/></svg>',\n },\n stripe: {\n id: 'stripe',\n name: 'Stripe',\n vendor: 'Stripe',\n tagline: 'Payments, customers & invoices',\n brandColor: '#635BFF',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Stripe</title><path fill=\"#FFFFFF\" d=\"M13.976 9.15c-2.172-.806-3.356-1.426-3.356-2.409 0-.831.683-1.305 1.901-1.305 2.227 0 4.515.858 6.09 1.631l.89-5.494C18.252.975 15.697 0 12.165 0 9.667 0 7.589.654 6.104 1.872 4.56 3.147 3.757 4.992 3.757 7.218c0 4.039 2.467 5.76 6.476 7.219 2.585.92 3.445 1.574 3.445 2.583 0 .98-.84 1.545-2.354 1.545-1.875 0-4.965-.921-6.99-2.109l-.9 5.555C5.175 22.99 8.385 24 11.714 24c2.641 0 4.843-.624 6.328-1.813 1.664-1.305 2.525-3.236 2.525-5.732 0-4.128-2.524-5.851-6.594-7.305h.003z\"/></svg>',\n },\n mixpanel: {\n id: 'mixpanel',\n name: 'Mixpanel',\n vendor: 'Mixpanel',\n tagline: 'Product & user analytics',\n brandColor: '#7856FF',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Mixpanel</title><path fill=\"#FFFFFF\" d=\"M6.967 9.996h3.053c-.763-.477-1.048-1.145-1.431-2.384L7.443 3.366C6.919 1.458 6.49.551 4.39.551H.004v1.145h.621c1.286 0 1.431.477 1.814 1.908L3.44 7.326c.524 1.814 1.337 2.67 3.53 2.67h-.003Zm7.06 0h3.053c2.194 0 2.956-.86 3.484-2.67l1.001-3.722c.382-1.431.57-1.908 1.814-1.908H24V.551h-4.34c-2.146 0-2.576.86-3.053 2.815l-1.145 4.246c-.384 1.286-.673 1.907-1.435 2.384Zm-4.007 4.008h4.007V9.996H10.02v4.008ZM0 23.449h4.39c2.1 0 2.529-.907 3.053-2.815l1.146-4.246c.383-1.239.668-1.907 1.431-2.384H6.967c-2.194 0-3.007.86-3.531 2.67l-1.001 3.722c-.383 1.431-.524 1.907-1.814 1.907H0v1.146Zm19.65 0h4.343v-1.146h-.622c-1.239 0-1.431-.476-1.814-1.907l-1.001-3.722c-.524-1.814-1.286-2.67-3.483-2.67h-3.046c.762.477 1.041 1.098 1.424 2.384l1.145 4.246c.477 1.955.907 2.815 3.054 2.815Z\"/></svg>',\n },\n pendo: {\n id: 'pendo',\n name: 'Pendo',\n vendor: 'Pendo',\n tagline: 'Product analytics & user guides',\n brandColor: '#EC2588',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Pendo</title><path fill=\"#FFFFFF\" d=\"M3 3h13.5A4.5 4.5 0 0 1 21 7.5v9A4.5 4.5 0 0 1 16.5 21H3V3Zm5 4v10h3v-3h2.2a3.5 3.5 0 0 0 0-7H8Zm3 2h1.9a1.5 1.5 0 0 1 0 3H11V9Z\"/></svg>',\n },\n pagerduty: {\n id: 'pagerduty',\n name: 'PagerDuty',\n vendor: 'PagerDuty',\n tagline: 'Incidents, alerts & on-call',\n brandColor: '#06AC38',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>PagerDuty</title><path fill=\"#FFFFFF\" d=\"M16.965 1.18C15.085.164 13.769 0 10.683 0H3.73v14.55h6.926c2.743 0 4.8-.164 6.61-1.37 1.975-1.303 3.004-3.484 3.004-6.007 0-2.716-1.262-4.896-3.305-5.994zm-5.5 10.326h-4.21V3.113l3.977-.027c3.62-.028 5.43 1.234 5.43 4.128 0 3.113-2.248 4.292-5.197 4.292zM3.73 17.61h3.525V24H3.73Z\"/></svg>',\n },\n amplitude: {\n id: 'amplitude',\n name: 'Amplitude',\n vendor: 'Amplitude',\n tagline: 'Digital analytics & experiments',\n brandColor: '#1F6FFF',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Amplitude</title><path fill=\"#FFFFFF\" d=\"M1 21h2V11H1v10Zm4 0h2V6H5v15Zm4 0h2V3H9v18Zm4 0h2V6h-2v15Zm4 0h2v-8h-2v8Zm4 0h2v-5h-2v5Z\"/></svg>',\n },\n datadog: {\n id: 'datadog',\n name: 'Datadog',\n vendor: 'Datadog',\n tagline: 'Metrics, logs & monitoring',\n brandColor: '#632CA6',\n logoSvg:\n '<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>Datadog</title><path fill=\"#FFFFFF\" d=\"M19.57 17.04l-1.997-1.316-1.665 2.782-1.937-.567-1.706 2.604.087.82 9.274-1.71-.538-5.794zm-8.649-2.498l1.488-.204c.241.108.409.15.697.223.45.117.97.23 1.741-.16.18-.088.553-.43.704-.625l6.096-1.106.622 7.527-10.444 1.882zm11.325-2.712l-.602.115L20.488 0 .789 2.285l2.427 19.693 2.306-.334c-.184-.263-.471-.581-.96-.989-.68-.564-.44-1.522-.039-2.127.53-1.022 3.26-2.322 3.106-3.956-.056-.594-.15-1.368-.702-1.898-.02.22.017.432.017.432s-.227-.289-.34-.683c-.112-.15-.2-.199-.319-.4-.085.233-.073.503-.073.503s-.186-.437-.216-.807c-.11.166-.137.48-.137.48s-.241-.69-.186-1.062c-.11-.323-.436-.965-.343-2.424.6.421 1.924.321 2.44-.439.171-.251.288-.939-.086-2.293-.24-.868-.835-2.16-1.066-2.651l-.028.02c.122.395.374 1.223.47 1.625.293 1.218.372 1.642.234 2.204-.116.488-.397.808-1.107 1.165-.71.358-1.653-.514-1.713-.562-.69-.55-1.224-1.447-1.284-1.883-.062-.477.275-.763.445-1.153-.243.07-.514.192-.514.192s.323-.334.722-.624c.165-.109.262-.178.436-.323a9.762 9.762 0 0 0-.456.003s.42-.227.855-.392c-.318-.014-.623-.003-.623-.003s.937-.419 1.678-.727c.509-.208 1.006-.147 1.286.257.367.53.752.817 1.569.996.501-.223.653-.337 1.284-.509.554-.61.99-.688.99-.688s-.216.198-.274.51c.314-.249.66-.455.66-.455s-.134.164-.259.426l.03.043c.366-.22.797-.394.797-.394s-.123.156-.268.358c.277-.002.838.012 1.056.037 1.285.028 1.552-1.374 2.045-1.55.618-.22.894-.353 1.947.68.903.888 1.609 2.477 1.259 2.833-.294.295-.874-.115-1.516-.916a3.466 3.466 0 0 1-.716-1.562 1.533 1.533 0 0 0-.497-.85s.23.51.23.96c0 .246.03 1.165.424 1.68-.039.076-.057.374-.1.43-.458-.554-1.443-.95-1.604-1.067.544.445 1.793 1.468 2.273 2.449.453.927.186 1.777.416 1.997.065.063.976 1.197 1.15 1.767.306.994.019 2.038-.381 2.685l-1.117.174c-.163-.045-.273-.068-.42-.153.08-.143.241-.5.243-.572l-.063-.111c-.348.492-.93.97-1.414 1.245-.633.359-1.363.304-1.838.156-1.348-.415-2.623-1.327-2.93-1.566 0 0-.01.191.048.234.34.383 1.119 1.077 1.872 1.56l-1.605.177.759 5.908c-.337.048-.39.071-.757.124-.325-1.147-.946-1.895-1.624-2.332-.599-.384-1.424-.47-2.214-.314l-.05.059a2.851 2.851 0 0 1 1.863.444c.654.413 1.181 1.481 1.375 2.124.248.822.42 1.7-.248 2.632-.476.662-1.864 1.028-2.986.237.3.481.705.876 1.25.95.809.11 1.577-.03 2.106-.574.452-.464.69-1.434.628-2.456l.714-.104.258 1.834 11.827-1.424zM15.05 6.848c-.034.075-.085.125-.007.37l.004.014.013.032.032.073c.14.287.295.558.552.696.067-.011.136-.019.207-.023.242-.01.395.028.492.08.009-.048.01-.119.005-.222-.018-.364.072-.982-.626-1.308-.264-.122-.634-.084-.757.068a.302.302 0 0 1 .058.013c.186.066.06.13.027.207m1.958 3.392c-.092-.05-.52-.03-.821.005-.574.068-1.193.267-1.328.372-.247.191-.135.523.047.66.511.382.96.638 1.432.575.29-.038.546-.497.728-.914.124-.288.124-.598-.058-.698m-5.077-2.942c.162-.154-.805-.355-1.556.156-.554.378-.571 1.187-.041 1.646.053.046.096.078.137.104a4.77 4.77 0 0 1 1.396-.412c.113-.125.243-.345.21-.745-.044-.542-.455-.456-.146-.749\"/></svg>',\n },\n};\n\nexport const UPCOMING_INTEGRATION_IDS = [\n 'gmail',\n 'asana',\n 'stripe',\n 'pendo',\n 'pagerduty',\n 'amplitude',\n] as const;\n\nexport function getIntegrationBranding(id: string): IntegrationBranding | null {\n return INTEGRATION_BRANDING[id] ?? null;\n}\n","// Repo stack → recommended integrations (Session Tools \"Recommended\").\n//\n// PURE logic only — no filesystem. The CLI reads the repo's dependency\n// manifests (package.json, requirements.txt, go.mod, …), collects the raw\n// dependency NAMES, and calls `recommendForDeps(names)`. Keeping the mapping\n// here (single source) lets the CLI and any UI agree on what a dep implies.\nimport type { IntegrationId } from './types';\n\nexport type RepoStack = 'frontend' | 'backend' | 'fullstack' | 'mobile' | 'unknown';\n\n/** The Session Tools stack-detection result (wire type, carried by the\n * SESSION_STACK_DETECTED event). `source` distinguishes the deterministic\n * dependency scan from the agent one-shot fallback. */\nexport interface RepoStackDetection {\n stack: RepoStack;\n /** Integrations the repo's dependencies directly evidence (high confidence). */\n detected: IntegrationId[];\n /** Integrations commonly paired with the classified stack, minus `detected`. */\n recommended: IntegrationId[];\n source: 'scan' | 'agent';\n}\n\n/** Exact dependency name → integration (across ecosystems: npm, pip, go, gem…). */\nexport const DEP_TO_INTEGRATION: Record<string, IntegrationId> = {\n // errors / observability\n '@sentry/node': 'sentry',\n '@sentry/react': 'sentry',\n '@sentry/nextjs': 'sentry',\n '@sentry/browser': 'sentry',\n 'sentry-sdk': 'sentry',\n 'dd-trace': 'datadog',\n 'datadog-api-client': 'datadog',\n 'datadog-metrics': 'datadog',\n // analytics\n 'posthog-js': 'posthog',\n 'posthog-node': 'posthog',\n posthog: 'posthog',\n mixpanel: 'mixpanel',\n 'mixpanel-browser': 'mixpanel',\n // database / backend platforms\n convex: 'convex',\n '@supabase/supabase-js': 'supabase',\n 'supabase': 'supabase',\n // infra / deploy\n vercel: 'vercel',\n '@vercel/node': 'vercel',\n wrangler: 'cloudflare',\n // comms\n '@slack/web-api': 'slack',\n '@slack/bolt': 'slack',\n 'discord.js': 'discord',\n 'discord-py': 'discord',\n 'discord': 'discord',\n // trackers / docs / design\n '@linear/sdk': 'linear',\n '@notionhq/client': 'notion',\n 'jira.js': 'jira',\n jira: 'jira',\n newman: 'postman',\n 'figma-api': 'figma',\n 'figma-js': 'figma',\n // email / automation\n resend: 'resend',\n n8n: 'n8n',\n};\n\n/** Dependency name PREFIX → integration (scoped-package families). */\nconst DEP_PREFIX_TO_INTEGRATION: Array<[string, IntegrationId]> = [\n ['@sentry/', 'sentry'],\n ['@vercel/', 'vercel'],\n ['@cloudflare/', 'cloudflare'],\n ['@supabase/', 'supabase'],\n ['@slack/', 'slack'],\n ['@datadog/', 'datadog'],\n ['@linear/', 'linear'],\n ['@notionhq/', 'notion'],\n];\n\n/** Stack classification markers (dependency name → stack signal). */\nconst FRONTEND_MARKERS = [\n 'react',\n 'react-dom',\n 'next',\n 'vue',\n 'nuxt',\n '@angular/core',\n 'svelte',\n '@sveltejs/kit',\n 'solid-js',\n 'astro',\n 'gatsby',\n 'remix',\n '@remix-run/react',\n];\nconst MOBILE_MARKERS = ['react-native', 'expo', '@react-native/core', '@ionic/core', 'flutter'];\nconst BACKEND_MARKERS = [\n 'express',\n 'fastify',\n '@nestjs/core',\n 'koa',\n '@hapi/hapi',\n 'django',\n 'flask',\n 'fastapi',\n 'rails',\n 'sinatra',\n 'gin-gonic',\n 'laravel/framework',\n 'actix-web',\n 'spring-boot',\n];\n\nfunction hasAny(deps: Set<string>, markers: string[]): boolean {\n for (const m of markers) if (deps.has(m)) return true;\n return false;\n}\n\n/** Classify the repo stack from its dependency NAMES. */\nexport function classifyStack(depNames: string[]): RepoStack {\n const deps = new Set(depNames);\n const mobile = hasAny(deps, MOBILE_MARKERS);\n if (mobile) return 'mobile';\n const frontend = hasAny(deps, FRONTEND_MARKERS);\n const backend = hasAny(deps, BACKEND_MARKERS);\n if (frontend && backend) return 'fullstack';\n if (frontend) return 'frontend';\n if (backend) return 'backend';\n return 'unknown';\n}\n\n/** Integrations directly evidenced by the repo's dependencies (deduped, stable order). */\nexport function detectedIntegrationsFromDeps(depNames: string[]): IntegrationId[] {\n const out: IntegrationId[] = [];\n const seen = new Set<IntegrationId>();\n const push = (id: IntegrationId) => {\n if (!seen.has(id)) {\n seen.add(id);\n out.push(id);\n }\n };\n for (const name of depNames) {\n const exact = DEP_TO_INTEGRATION[name];\n if (exact) {\n push(exact);\n continue;\n }\n for (const [prefix, id] of DEP_PREFIX_TO_INTEGRATION) {\n if (name.startsWith(prefix)) {\n push(id);\n break;\n }\n }\n }\n return out;\n}\n\n/** Commonly-paired integrations per classified stack (the curated inference layer). */\nexport const STACK_TO_RECOMMENDED: Record<RepoStack, IntegrationId[]> = {\n frontend: ['figma', 'sentry', 'posthog', 'vercel'],\n backend: ['sentry', 'datadog', 'supabase', 'convex', 'cloudflare'],\n fullstack: ['sentry', 'posthog', 'vercel', 'supabase', 'convex'],\n mobile: ['sentry', 'posthog'],\n unknown: [],\n};\n\n/**\n * The deterministic core of Session Tools \"Recommended\": classify the stack and\n * combine direct-evidence detections with stack inference. `recommended`\n * excludes anything already `detected`. Returns `source:'scan'`. When both\n * `detected` and `recommended` are empty (unrecognized stack), the CLI falls\n * back to the agent one-shot (B).\n */\nexport function recommendForDeps(depNames: string[]): RepoStackDetection {\n const stack = classifyStack(depNames);\n const detected = detectedIntegrationsFromDeps(depNames);\n const detectedSet = new Set(detected);\n const recommended = STACK_TO_RECOMMENDED[stack].filter((id) => !detectedSet.has(id));\n return { stack, detected, recommended, source: 'scan' };\n}\n","import type { SkillDefinition } from './types';\n\n// QUALITY playbook; the mechanical gh steps stay in buildAgentReviewPrompt —\n// this skill is complementary reviewing guidance.\nconst CODE_REVIEW_BODY = `Use this skill when reviewing a pull request. It defines what a high-signal\nreview looks like so your inline comments are worth the author's time.\n\n## Review priorities (in order)\n1. **Correctness** — does the change do what the PR says, and only that? Trace the\n changed paths for logic errors, off-by-one, null/undefined, wrong branch, and\n inverted conditions. State a concrete failure scenario (inputs → wrong output)\n for anything you flag as a bug.\n2. **Security** — untrusted input reaching a sink (SQL, shell, path, HTML), secrets\n in code/logs, authz gaps, credentials passed via argv instead of env.\n3. **Tests** — does the change carry tests that would fail without it? Missing\n coverage on a bug-prone path is a finding.\n4. **Clarity / reuse** — duplicated logic, a simpler existing helper, a name that\n misleads. Only raise these when they materially affect maintainability.\n\n## Comment discipline\n- One finding per comment, anchored to the exact line.\n- Lead with severity: **blocker**, **should-fix**, or **nit**.\n- Say WHY (the failure or risk), not just WHAT. Propose the fix when it is short.\n- Do NOT restate the diff, praise trivially, or nitpick style a formatter owns.\n- If the PR is correct and well-tested, say so plainly and approve — a clean review\n is a valid outcome, not a failure to find something.\n\n## Scope\nReview only what the diff changes and its direct blast radius. Do not demand\nunrelated refactors.`;\n\nconst CODE_REVIEW_INSTRUCTION = `When reviewing this PR, prioritize correctness first, then security, then test\ncoverage, then clarity/reuse. One finding per inline comment, anchored to the exact\nline, each led by a severity tag (blocker/should-fix/nit) and a concrete reason\n(the failure scenario or risk), not a restatement of the diff. If the change is\ncorrect and well-tested, approve and say so — finding nothing is a valid outcome.\nReview only the diff and its direct blast radius; do not demand unrelated refactors.`;\n\nexport const codeReviewSkill: SkillDefinition = {\n id: 'code-review',\n name: 'Code Review',\n description: `High-signal PR review: prioritize correctness → security → tests → clarity, one anchored finding per comment.`,\n source: 'curated',\n delivery: {\n skillFile: { body: CODE_REVIEW_BODY },\n instruction: { body: CODE_REVIEW_INSTRUCTION },\n },\n};\n","import type { SkillDefinition } from './types';\n\nconst RESOLVE_CONFLICTS_BODY = `Use this skill when resolving merge conflicts on a pull request. The goal is a\nmerge that preserves BOTH sides' intent, not one that just makes the file compile.\n\n## Method\n1. Understand each conflict hunk before editing: what did HEAD change, what did the\n base branch change, and WHY. Read the surrounding function, not just the markers.\n2. Prefer a union of intents. Drop a side only when the two changes are genuinely\n mutually exclusive — and when you do, keep the side that matches the PR's purpose.\n3. Never leave a conflict marker (\\`<<<<<<<\\`, \\`=======\\`, \\`>>>>>>>\\`) behind. Grep for\n them before committing.\n4. After resolving, the code must build and its tests must pass. Run them. A merge\n that resolves markers but breaks the build is not done.\n5. For lockfiles/generated files, regenerate rather than hand-merge.\n\n## Commit\nOne commit that explains what was reconciled and any intent you had to choose\nbetween. Then push the branch.`;\n\nconst RESOLVE_CONFLICTS_INSTRUCTION = `When resolving these merge conflicts, preserve both sides' intent — read each hunk's\nsurrounding code to understand what HEAD and the base branch each changed and why,\nand prefer a union of intents; drop a side only when the two are mutually exclusive,\nkeeping the side that matches the PR's purpose. Leave no conflict markers behind\n(grep for them). Regenerate lockfiles rather than hand-merging them. The result must\nbuild and pass tests — run them — before you commit and push.`;\n\nexport const resolveConflictsSkill: SkillDefinition = {\n id: 'resolve-conflicts',\n name: 'Resolve Conflicts',\n description: `Merge-conflict resolution that preserves both sides' intent, leaves no markers, and keeps the build green.`,\n source: 'curated',\n delivery: {\n skillFile: { body: RESOLVE_CONFLICTS_BODY },\n instruction: { body: RESOLVE_CONFLICTS_INSTRUCTION },\n },\n};\n","import type { SkillDefinition } from './types';\n\n// An improved, adaptive take on spec-driven development: right-size the ceremony,\n// ground in the real codebase, clarify without stalling, gate against\n// over-engineering, and — the part most spec workflows lack — verify each\n// acceptance criterion + adversarially self-review before calling it done. Tracks\n// real work in beads, never scaffolds spec/plan/tasks files into the user's repo.\nconst SPEC_DRIVEN_BODY = `Use this skill for any coding task that isn't a trivial one-liner. Build the right\nthing, provably, with the least ceremony the task warrants — specification before\ncode, but its depth scales to the work. Skipping it yields code that looks right yet\nsolves the wrong problem or breaks something you never checked.\n\n## Step 0 — Right-size the work (always first)\n- **Quick** — a typo, copy tweak, one-line fix, a single file with no unknowns.\n No ceremony: make the change, run the relevant test/build, confirm it. Do NOT\n write a spec for a typo.\n- **Standard** — a feature or fix across a few files, some unknowns, a testable\n outcome. A light inline pass (a 2-3 sentence spec + a short plan), then build\n test-first, then verify. No scaffolding files.\n- **Deep** — large, ambiguous, risky, or touching many files / shared contracts /\n data / auth. The full flow below, tracking the work in beads (\\`bd\\`), not scratch\n files.\nWhen unsure, start one level lighter and escalate the moment real ambiguity or risk\nappears. State which level you picked in one line.\n\n## Step 1 — Ground in the real codebase (before specifying anything non-trivial)\nYou are almost never in a greenfield. Before you spec or plan, survey the real code:\nthe existing patterns for this kind of change, the files you'll touch, the test\nsetup, prior art, and the constraints (auth, data, shared types, CLAUDE.md\nconventions). A spec written in a vacuum produces a plan that fights the codebase.\nRead first; never assume.\n\n## Step 2 — Specify: the WHAT and WHY (not the HOW)\nState the user-visible outcome and why it matters, then the acceptance criteria —\neach concrete and testable (\"tapping X shows Y\", \"the endpoint returns 409 when Z\"),\nnever vague (\"works well\"). List what is out of scope. Put NO implementation detail\nhere (no file names, no libraries). If a requirement is ambiguous, mark it rather\nthan guess.\n\n## Step 3 — Clarify: resolve ambiguity, but don't stall\nGather the ambiguities that would actually change what you build. Ask the\nhighest-leverage ones — batched, at most ~3, phrased as concrete choices. For\nlow-stakes unknowns, pick a sensible default and SAY so (\"assuming X unless you tell\nme otherwise\") instead of asking. On a conversational/mobile client every round-trip\nis expensive — don't pester; decide what you safely can.\n\n## Step 4 — Plan: the HOW, grounded and simple\nDesign against the real code. Apply the simplicity gates before committing:\n- **Fewest moving parts** that satisfy the criteria. If you add a layer or\n abstraction, justify it or drop it.\n- **Use the framework/library directly** — don't wrap it for flexibility you don't\n need yet.\n- **Minimal blast radius** — touch what the change needs, nothing more.\nState the test strategy (what proves each criterion) and name the real risks. Keep\nthe plan short.\n\n## Step 5 — Tasks: small, verifiable, ordered\nSplit the plan into tasks that each end in something you can run and check. Mark\nindependent ones as parallelizable. Each task is the smallest unit worth its own\ncheck. Sequence by dependency.\n\n## Step 6 — Implement: test-first by default\nFor each task: write the test that would fail without the change, watch it fail, make\nit pass, keep it green. Follow the patterns you found in Step 1. Commit in logical\nunits. Escape hatch: for a genuine spike or exploratory UI where test-first is\nimpractical, say so explicitly and add the test right after — never skip it silently.\n\n## Step 7 — Verify + self-review (what separates \"done\" from \"looks done\")\nReturn to the acceptance criteria and prove EACH one is met — run the tests, diff the\nbehavior, look at the real output. Then review your own work adversarially:\n- What did I NOT test?\n- What did I change that I didn't need to?\n- What could this have broken (the blast radius)?\n- Does anything contradict the spec?\nFix what you find before declaring done. \"The tests I wrote pass\" is not \"it works\".\n\n## Step 8 — Done + handoff\nDone means acceptance criteria met, tests green, no known regressions. Summarize what\nchanged in plain terms. File any deferred work or follow-ups to beads (\\`bd\\`) so\nnothing is lost. Never claim done on unverified work.\n\n## Principles (the constitution)\n- Clarify before you build; verify before you call it done.\n- Testable beats descriptive — a criterion you can't check isn't one.\n- Grounded beats greenfield — fit the codebase that exists.\n- Simple beats clever — the least structure that works.\n- Scale the process to the task — ceremony on a typo is a bug.\n- Real work goes to beads, not throwaway files in someone's repo.`;\n\nconst SPEC_DRIVEN_INSTRUCTION = `Follow spec-driven development, scaled to the task:\n1. Right-size first. A trivial change (typo, one file, no unknowns) → just make it\n well and verify, no ceremony. A feature/ambiguous/risky change → spec → plan →\n build → verify.\n2. Ground in the real codebase before planning — read the existing patterns, tests,\n and constraints. Never assume; read first.\n3. Specify the WHAT and WHY as testable acceptance criteria, not the HOW. Mark\n ambiguities instead of guessing.\n4. Clarify only the highest-leverage unknowns (batch <=3, concrete choices); default\n the low-stakes ones and say so. Don't stall on round-trips.\n5. Plan the simplest approach that fits the code: fewest moving parts, use libraries\n directly, minimal blast radius. State how each criterion will be tested.\n6. Implement test-first by default; follow existing patterns; commit in logical units.\n7. Verify EACH acceptance criterion (run tests, diff behavior), then self-review\n adversarially: what's untested? what did I change needlessly? what could I have\n broken? what contradicts the spec? Fix before declaring done.\n8. Track deferred work in beads (bd), not scratch files. Simple beats clever; verify\n before done.`;\n\nexport const specDrivenDevelopmentSkill: SkillDefinition = {\n id: 'spec-driven-development',\n name: 'Spec-Driven Development',\n description: `Spec-driven development scaled to the task: right-size, ground in the code, write testable acceptance criteria, plan simply, build test-first, and verify every criterion before done.`,\n source: 'curated',\n delivery: {\n skillFile: { body: SPEC_DRIVEN_BODY },\n instruction: { body: SPEC_DRIVEN_INSTRUCTION },\n },\n};\n","import type { SkillDefinition } from './types';\n\n// Naming guidance distilled from CodeAesthetic — \"Naming Things in Code\"\n// (https://www.youtube.com/watch?v=-J3wNP6u5YU). The video's five rules are the\n// spine (no abbreviations, no type-in-variable, units when they matter, no\n// type-in-type, refactor the \"Utils\" bucket); the rest are consistent, widely\n// held extensions. Deliberately framed as a REVIEW/REFACTOR guide with strong\n// \"don't mass-rename\" guardrails — the skill makes better names available, it\n// does NOT license renaming code that was already clear.\n\nconst CODE_NAMING_BODY = `Use this skill when NAMING or RENAMING code — variables, functions, classes,\ninterfaces, types, files, modules, components, hooks, constants — while writing\nnew code, refactoring, or reviewing a diff. A name should reveal intent and\ndomain meaning so a reader understands the code without translating it in their\nhead, and so it needs fewer comments.\n\n## The five core rules (name the concept, not the implementation)\n\n1. **Don't abbreviate, and don't use single letters for meaningful values.**\n \\`usr\\`/\\`cfg\\`/\\`authReq\\` → \\`user\\`/\\`config\\`/\\`authRequest\\`; \\`u\\`/\\`p\\` → \\`user\\`/\\`project\\`.\n Abbreviations rely on context the next reader may not have. Allowed: standard\n acronyms the domain already uses (API, URL, HTTP, ID, UUID, CLI, SDK, PR, OAuth),\n loop indices \\`i\\`/\\`j\\`, coordinates \\`x\\`/\\`y\\`, and generic type params \\`T\\`/\\`K\\`/\\`V\\`.\n\n2. **Don't put the data TYPE in a variable name.** \\`userList\\` → \\`users\\`,\n \\`nameString\\` → \\`displayName\\`, \\`isActiveBool\\` → \\`isActive\\`, \\`invoiceMap\\` →\n \\`invoicesById\\`. The type system and editor already show the type; the name\n should carry the meaning.\n\n3. **Include the UNIT when a number has one** (unless the type makes it\n unmistakable). \\`timeout\\` → \\`timeoutMs\\`, \\`delay\\` → \\`retryDelaySeconds\\`,\n \\`size\\` → \\`fileSizeBytes\\`, \\`price\\` → \\`priceUsd\\`. Especially time, distance,\n money, bytes, rates, percentages, token/rate limits.\n\n4. **Don't put the type CONSTRUCT in a type's name.** \\`UserClass\\` → \\`User\\`,\n \\`PaymentInterface\\`/\\`IPayment\\` → \\`Payment\\` (or a role like \\`PaymentGateway\\`),\n \\`ConfigType\\` → \\`Config\\`, \\`StatusEnum\\` → \\`Status\\`. Name the concept, not\n \"what kind of programming thing it is.\"\n\n5. **Refactor when you're reaching for \\`Utils\\`/\\`Helpers\\`/\\`Common\\`.** A generic\n bucket usually hides a missing concept. Group by domain instead:\n \\`utils/parseCookie\\` → a \\`cookies/\\` module or a \\`CookieStore\\`/\\`CookieJar\\` class;\n \\`utils/formatDate\\` → \\`dates/\\` or a \\`DateRangeFormatter\\`. Same for vague\n \\`Base\\`/\\`Abstract\\`/\\`Manager\\`/\\`Helper\\` class names — prefer the role\n (\\`Repository\\`, \\`BillingService\\`, \\`UserProvisioner\\`) unless the project\n convention explicitly requires \\`Base\\`/\\`Abstract\\`.\n\n## Shape rules\n\n- **Functions are verbs / verb phrases:** \\`createInvoice()\\`, \\`sendPasswordResetEmail()\\`,\n \\`findRepositoryById()\\`. Booleans read like a question in an \\`if\\`:\n \\`isActive\\`, \\`hasAccess\\`, \\`canDeploy\\`, \\`shouldRetry\\` (prefix \\`is/has/can/should/needs\\`).\n- **Classes / interfaces / types are nouns or roles:** \\`Invoice\\`, \\`PaymentGateway\\`,\n \\`AgentSession\\` — not \\`InvoiceData\\`, \\`SubscriptionThing\\`, \\`AgentSessionType\\`.\n- **Collections are plural, or \\`…ById\\`/\\`…ByX\\` when indexed:** \\`users\\`,\n \\`activeSessions\\`, \\`invoicesByCustomerId\\`.\n- **Don't encode a temporary implementation in a name that may change:**\n \\`postgresUserRepository\\` → \\`userRepository\\`, \\`redisCache\\` → \\`cache\\` — unless the\n implementation IS the meaningful distinction. Name the role, not today's backend.\n- **Follow the project's existing conventions** (casing, file naming, \\`useX\\`\n hooks, \\`handleX\\` handlers). Match the surrounding code; don't introduce a new\n style without a strong reason.\n\n## Words to distrust (infer the real role, then name it)\n\n\\`data\\`, \\`info\\`, \\`item\\`, \\`obj\\`, \\`temp\\`, \\`result\\`, \\`value\\`, \\`thing\\`, \\`stuff\\`,\n\\`manager\\`, \\`helper\\`, \\`utils\\`, \\`common\\`, \\`base\\`, \\`abstract\\`, \\`processor\\`,\n\\`handler\\`, \\`payload\\`. They're acceptable only when the surrounding context makes\nthem precise (e.g. \\`config\\` inside one \\`DatabaseConnection\\`); vague when passed\nacross many layers.\n\n## Guardrails — this is a review/refactor guide, NOT a mass-rename mandate\n\n- Names that are already clear and idiomatic are DONE — leave them.\n- Before renaming: read how the identifier is used and infer the real domain\n concept. Prefer the **smallest** clear rename.\n- Do NOT do large unrelated renames, and do NOT rename purely for personal\n preference. Stay inside the change you were asked to make.\n- Preserve public API / exported names unless the task explicitly allows a break;\n when you do rename, update every reference, tests, and docs in the same change.\n- No clever names, jokes, or metaphors in production code; don't make names\n needlessly long either.\n\nWhen reviewing, raise a naming issue only when a clearer name would materially\nhelp a reader — one suggestion per finding: \\`current → suggested — why\\`.`;\n\nconst CODE_NAMING_INSTRUCTION = `When naming or renaming code, name the domain concept, not the implementation:\nno abbreviations or single-letter names for meaningful values (standard acronyms\nlike API/URL/ID/OAuth are fine); don't put the data type in a variable name\n(userList → users); include the unit when a number has one (timeout → timeoutMs);\ndon't put the type construct in a type's name (IPayment → Payment); and refactor\ngeneric Utils/Helper/Manager/Base buckets into a domain module or a role-named\nclass. Functions are verbs (createInvoice), booleans read like questions\n(isActive/hasAccess), classes/types are nouns/roles, collections are plural or\n…ById. This is a review/refactor guide, not a mandate to mass-rename: leave\nalready-clear names alone, prefer the smallest clear rename, don't rename\nunrelated code, and preserve public/exported names unless the task allows a break.`;\n\nexport const codeNamingSkill: SkillDefinition = {\n id: 'code-naming',\n name: 'Code Naming',\n description: `Naming review & refactor: name the domain concept, not the implementation — no abbreviations, no type-in-name, units when they matter, no Utils/Manager/Base buckets; rename minimally, never mass-rename.`,\n source: 'curated',\n delivery: {\n skillFile: { body: CODE_NAMING_BODY },\n instruction: { body: CODE_NAMING_INSTRUCTION },\n },\n};\n","// The registry assembles every curated Agent Skill from its own file into one\n// lookup. Adding a skill = a new `<id>.ts` file (definition + content) + one import\n// line here + widening `SkillId` in `types.ts`. Because content is bundled and\n// delivered as data, a new curated skill needs a client release but no backend logic.\nimport type { SkillDefinition, SkillId, SkillRail } from './types';\nimport { codeReviewSkill } from './code-review';\nimport { resolveConflictsSkill } from './resolve-conflicts';\nimport { specDrivenDevelopmentSkill } from './spec-driven-development';\nimport { codeNamingSkill } from './code-naming';\n\nexport const SKILL_REGISTRY: Record<SkillId, SkillDefinition> = {\n 'code-review': codeReviewSkill,\n 'resolve-conflicts': resolveConflictsSkill,\n 'spec-driven-development': specDrivenDevelopmentSkill,\n 'code-naming': codeNamingSkill,\n};\n\nexport function isSkillId(id: string): id is SkillId {\n return Object.prototype.hasOwnProperty.call(SKILL_REGISTRY, id);\n}\n\nexport function getSkillDefinition(id: string): SkillDefinition | null {\n return isSkillId(id) ? SKILL_REGISTRY[id] : null;\n}\n\nexport function skillHasRail(id: SkillId, rail: SkillRail): boolean {\n return Boolean(SKILL_REGISTRY[id].delivery[rail]);\n}\n","/**\n * Production API base URL for all CodeAgent Mobile clients.\n *\n * History note: prod migrated from Vercel (`https://api.codeagent-mobile.com`)\n * to Cloud Run / api-v2 (`https://api.codeagent-mobile.com`) in 2026-05. The\n * Vercel deployment is now gated by Vercel deployment protection and returns\n * 403 for unauthed traffic — DO NOT fall back to it.\n *\n * Override at runtime with `CODEAM_API_URL` (full URL override) OR set\n * `CODEAM_TEST_MODE=1` to point every client request at the dev\n * preview without having to know its host.\n */\nexport const DEFAULT_API_BASE_URL = 'https://api.codeagent-mobile.com' as const;\n\n/**\n * Dev-preview API base URL. Same Cloud Run service as prod but routed\n * to the `dev` revision (auto-deploys from the `dev` branch in the\n * backend repo). Manual smoke tests + load runs land here.\n */\nexport const DEV_API_BASE_URL = 'https://dev-api.codeagent-mobile.com' as const;\n\n/**\n * Resolve the active API base URL, honoring in priority order:\n *\n * 1. Explicit `CODEAM_API_URL` env var — full URL, takes precedence.\n * 2. `CODEAM_TEST_MODE=1` shortcut — flips to [DEV_API_BASE_URL]\n * without the user having to know the dev host.\n * 3. The `DEFAULT_API_BASE_URL` constant (prod).\n *\n * Used by every CLI service that talks to the backend so one env var\n * flips heartbeats, command relay, chunk uploads, and the pairing\n * flow in lockstep — eliminates the cross-environment misroute where\n * pairing succeeds in dev (shared Redis) but the CLI keeps\n * heartbeating to prod.\n */\nexport function resolveApiBaseUrl(): string {\n // Guard against non-Node runtimes (browser bundles import this\n // module). `process` is undefined there; treat as prod default.\n // `@codeam/shared` deliberately avoids depending on `@types/node`\n // so its types stay consumable from the mobile RN bundle too, so we\n // reach for the env via a structural cast rather than NodeJS.ProcessEnv.\n const env = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env;\n const explicit = env?.CODEAM_API_URL?.trim();\n if (explicit) return explicit;\n const testFlag = env?.CODEAM_TEST_MODE?.trim();\n if (testFlag === '1' || testFlag?.toLowerCase() === 'true') return DEV_API_BASE_URL;\n return DEFAULT_API_BASE_URL;\n}\n","/**\n * Headroom provisioning manifest — the SINGLE source of truth for what a\n * Headroom install consists of, rendered by every provisioning surface:\n *\n * - codespace bootstrap (bash composer in the backend repo,\n * `apps/api-v2/src/codespaces/github-ssh.service.ts` — adopts in PR-2),\n * - self-hosted deploy (TS installer, CLI `commands/host-agent.ts`\n * `setupHeadroomForSelfHosted`),\n * - on-demand local sessions (\"Session add-ons → Cost-saving\", CLI\n * `services/headroom/configure.ts`).\n *\n * Values are DATA-first (arrays/records, plus tiny pure renderers) so both\n * the TS installer and a bash composer can interpolate from them. Renderers\n * are byte-exact with the literals they replaced — guarded by\n * `packages/shared/__tests__/headroom-manifest.test.ts`.\n *\n * ⚠️ The extras matter: `[proxy,code]` pulls the ONNX compression engines\n * (Kompress + tree-sitter CodeCompressor). NEVER add `[ml]` — that's\n * multi-GB PyTorch, and a broken/cold torch wedges every prompt at\n * \"Thinking…\". The models are pre-downloaded at provision time because the\n * proxy eager-loads with `allow_download=False` and a cold cache defers the\n * ~840 MB download to the first prompt (blowing the agent's ~90 s idle\n * timeout).\n */\n\n/** Local proxy port the agent's config is routed to. */\nexport const HEADROOM_PROXY_PORT = 8787;\n\n/**\n * Env that pins the ONNX backend on the proxy process — never imports\n * torch. Spread into the proxy launch env on every surface.\n */\nexport const HEADROOM_BACKEND_ENV = {\n HEADROOM_KOMPRESS_BACKEND: 'onnx_cpu',\n} as const;\n\n/**\n * The proxy's HTTP/server companion packages, installed alongside the\n * `headroom-ai[...]` package. The COMPRESSION ENGINES come from the\n * headroom-ai extras — NOT this list.\n */\nexport const HEADROOM_PIP_COMPANIONS: readonly string[] = [\n 'fastapi',\n 'uvicorn',\n 'httpx[http2]',\n 'websockets',\n 'zstandard',\n];\n\n/** The three provisioning surfaces (see module doc). */\nexport type HeadroomSurface = 'codespace' | 'selfHosted' | 'onDemand';\n\n/**\n * pip extras per surface. `onDemand` additionally ships `image`\n * (image-compression support, added with the Session add-ons path in\n * codeam-cli@2.49.0); the older codespace/self-hosted install strings\n * remain `[proxy,code]` byte-for-byte.\n */\nexport const HEADROOM_EXTRAS_BY_SURFACE: Readonly<Record<HeadroomSurface, readonly string[]>> = {\n codespace: ['proxy', 'code'],\n selfHosted: ['proxy', 'code'],\n onDemand: ['proxy', 'code', 'image'],\n};\n\n/** `headroom-ai[<extras>]` — the pip requirement string. */\nexport function headroomPipPackage(extras: readonly string[]): string {\n return `headroom-ai[${extras.join(',')}]`;\n}\n\n/** One HuggingFace repo to pre-warm into the HF cache at provision time. */\nexport interface HeadroomModelSpec {\n repo: string;\n /** `snapshot_download(..., allow_patterns=[…])` filter. */\n allowPatterns: readonly string[];\n}\n\n/**\n * The two HF repos Kompress needs. kompress-v2-base is the ONNX model\n * (skip its .pt/.safetensors torch artifacts); ModernBERT-base is the\n * TOKENIZER ONLY (skip its model weights).\n */\nexport const HEADROOM_MODELS: readonly HeadroomModelSpec[] = [\n {\n repo: 'chopratejas/kompress-v2-base',\n allowPatterns: ['*.json', 'onnx/*.onnx', 'kompress-int8-wo.onnx'],\n },\n {\n repo: 'answerdotai/ModernBERT-base',\n allowPatterns: ['*.json', 'tokenizer*', '*.txt', 'vocab*', 'merges*'],\n },\n];\n\n/** Formatting knob so each surface can stay byte-identical to its\n * historical literal (the CLI joins patterns with `,`, the codespace\n * bash composer with `, `). */\nexport interface HeadroomPythonRenderOpts {\n /** Put a space after the commas between allow_patterns entries. */\n spaceAfterComma?: boolean;\n}\n\n/** Render one `snapshot_download(...)` python line for a model. */\nexport function headroomSnapshotDownloadLine(\n model: HeadroomModelSpec,\n opts: HeadroomPythonRenderOpts = {},\n): string {\n const sep = opts.spaceAfterComma ? ', ' : ',';\n const patterns = model.allowPatterns.map((p) => `\"${p}\"`).join(sep);\n return `snapshot_download(\"${model.repo}\", allow_patterns=[${patterns}])`;\n}\n\n/**\n * The full model pre-download python snippet (import + one\n * `snapshot_download` per model), newline-joined — what the surfaces pass\n * to `python -c` / a heredoc.\n */\nexport function headroomModelPredownloadScript(opts: HeadroomPythonRenderOpts = {}): string {\n return [\n 'from huggingface_hub import snapshot_download',\n ...HEADROOM_MODELS.map((m) => headroomSnapshotDownloadLine(m, opts)),\n ].join('\\n');\n}\n","/**\n * Canonical names of the per-user SSE bus events (`/api/users/me/stream`).\n *\n * The authoritative list is the `UserEvent` discriminated union in the\n * backend repo: codeagent-mobile/apps/api-v2/src/user-events/user-events.types.ts.\n * Every `type:` literal of that union appears here exactly once — when a new\n * variant lands on the union, add its name here (and in the backend mirror of\n * this file at codeagent-mobile/packages/shared/src/types/events.ts).\n *\n * Producers (CLI event posts, backend `userEvents.publish` calls) and\n * consumers (the `useUserEventsSSE` hooks' switch cases) should reference\n * `USER_EVENTS.*` instead of re-typing the string, so a typo becomes a\n * compile error instead of a silently dropped event.\n */\nexport const USER_EVENTS = {\n PAIRED_SESSION_STATUS: 'paired_session_status',\n PAIRED_SESSION_ADDED: 'paired_session_added',\n PAIRED_SESSION_REMOVED: 'paired_session_removed',\n PAIRED_SESSION_BRANCH_CHANGED: 'paired_session_branch_changed',\n SHARED_WITH_ME_ADDED: 'shared_with_me_added',\n SHARED_WITH_ME_REVOKED: 'shared_with_me_revoked',\n USAGE_CHANGED: 'usage_changed',\n TASK_DONE: 'task_done',\n HUNK_PENDING_REVIEW_ADDED: 'hunk_pending_review_added',\n HUNK_REVIEW_RESOLVED: 'hunk_review_resolved',\n FILE_CHANGED: 'file_changed',\n FILES_BATCH_CHANGED: 'files_batch_changed',\n AGENT_STREAMING_CHUNK: 'agent_streaming_chunk',\n AGENT_AWAITING_ANSWER: 'agent_awaiting_answer',\n AWAITING_INPUT_ADDED: 'awaiting_input_added',\n AGENT_ANSWER_RESOLVED: 'agent_answer_resolved',\n TEMPLATE_ADDED: 'template_added',\n TEMPLATE_REMOVED: 'template_removed',\n TEMPLATE_UPDATED: 'template_updated',\n AGENT_TASK_DISPATCHED: 'agent_task_dispatched',\n AGENT_TASK_COMPLETED: 'agent_task_completed',\n LINKED_AGENT_ADDED: 'linked_agent_added',\n QUOTA_REACHED: 'quota_reached',\n LINKED_AGENT_LINK_FAILED: 'linked_agent_link_failed',\n CODESPACE_AGENT_INSTALLED: 'codespace_agent_installed',\n AGENT_CREDENTIALS_REFRESHED: 'agent_credentials_refreshed',\n CREDENTIAL_INVALID: 'credential_invalid',\n CODESPACE_WAKING: 'codespace_waking',\n CODESPACE_BILLING_BLOCKED: 'codespace_billing_blocked',\n COST_SAVING_UPDATED: 'cost_saving_updated',\n COMMAND_COMPLETED: 'command_completed',\n AI_SUMMARY_PENDING: 'ai_summary_pending',\n AI_SUMMARY_READY: 'ai_summary_ready',\n AI_INSIGHT_PENDING: 'ai_insight_pending',\n AI_INSIGHT_READY: 'ai_insight_ready',\n PUSH_TOKEN_INVALIDATED: 'push_token_invalidated',\n PREVIEW_DETECTION_PENDING: 'preview_detection_pending',\n PREVIEW_DETECTION_READY: 'preview_detection_ready',\n PREVIEW_STARTING: 'preview_starting',\n PREVIEW_READY: 'preview_ready',\n PREVIEW_STOPPED: 'preview_stopped',\n PREVIEW_ERROR: 'preview_error',\n PREVIEW_PROGRESS: 'preview_progress',\n BEADS_STATE_CHANGED: 'beads_state_changed',\n BEADS_PROVISIONING: 'beads_provisioning',\n BEADS_TEAM_MEMORY_CHANGED: 'beads_team_memory_changed',\n AUDIT_EVENT_ADDED: 'audit_event_added',\n SELF_HOSTED_HOST_ADDED: 'self_hosted_host_added',\n SELF_HOSTED_HOST_STATUS: 'self_hosted_host_status',\n SELF_HOSTED_HOST_REMOVED: 'self_hosted_host_removed',\n SELF_HOSTED_HOST_TELEMETRY: 'self_hosted_host_telemetry',\n SELF_HOSTED_HOST_METRICS: 'self_hosted_host_metrics',\n SELF_HOSTED_HOST_SESSIONS: 'self_hosted_host_sessions',\n SELF_HOSTED_DEPLOY_PROGRESS: 'self_hosted_deploy_progress',\n /** Fleet rescue: the user's CodeAgent Box reached RUNNING (host enrolled\n * + online). Drives the mobile \"Use a free CodeAgent Box\" flow to\n * auto-deploy the user's presets instead of hanging on a paired session\n * a box never creates. */\n FLEET_BOX_READY: 'fleet_box_ready',\n REFERRAL_REWARD_EARNED: 'referral_reward_earned',\n HEADROOM_PROGRESS: 'headroom_progress',\n HEADROOM_STATUS: 'headroom_status',\n BEADS_STATUS: 'beads_status',\n LINKED_AGENT_HEADROOM_BUDGET_UPDATED: 'linked_agent_headroom_budget_updated',\n CLI_UPDATE_AVAILABLE: 'cli_update_available',\n AGENT_INSTALL_PROGRESS: 'agent_install_progress',\n AGENT_INSTALL_FAILED: 'agent_install_failed',\n CLI_UPDATE_PROGRESS: 'cli_update_progress',\n CLI_UPDATE_FAILED: 'cli_update_failed',\n BATON_STATE: 'baton_state',\n INTEGRATION_LINKED: 'integration_linked',\n INTEGRATION_UNLINKED: 'integration_unlinked',\n INTEGRATION_CREDENTIAL_INVALID: 'integration_credential_invalid',\n // Session Tools — add/remove Agent Toolkit integrations on an ALREADY-ACTIVE\n // session. The backend publishes SESSION_INTEGRATIONS_CHANGED after it persists\n // the new attached set + relays `integrations_sync` to the box (the CLI rewrites\n // the manifest + reprovisions the live agent, answered synchronously via the\n // command result). Drives live UI on the sending device AND any other paired\n // device on the same session. Mirrored in repo A. (`integrations_detect` is a\n // pure request/response over the relay — no event.)\n SESSION_INTEGRATIONS_CHANGED: 'session_integrations_changed',\n // CodeRabbit reviewer — the CLI posts these to /api/coderabbit/events; the\n // backend re-publishes them on the per-user SSE bus (mirrored in repo A).\n CODERABBIT_PROGRESS: 'coderabbit_progress',\n CODERABBIT_STATUS: 'coderabbit_status',\n CODERABBIT_REVIEW: 'coderabbit_review',\n\n // VCS / PR Command Center — the backend publishes this after an agent finishes\n // reviewing a PR (verdict + comment count + findings), driving the mobile\n // completion screen + push. Mirrored in repo A's app-shared events.ts.\n VCS_AGENT_REVIEW_COMPLETE: 'vcs_agent_review_complete',\n /** PR-review launch progress toast — the \"Review with an agent\" flow shows a\n * toast when the review runs server-side (Inngest). Mobile-only surface,\n * produced by api-v2 (the CLI neither produces nor consumes it). Mirrored in\n * repo A. */\n PR_REVIEW_LAUNCH: 'pr_review_launch',\n} as const;\n\nexport type UserEventName = (typeof USER_EVENTS)[keyof typeof USER_EVENTS];\n","/**\n * Prompt the CLI sends to the user's linked agent (Claude, Codex, …)\n * in a headless one-shot to detect how to start the project's dev\n * server. Same pattern as the AI Insights \"summary\" prompt — the\n * agent runs locally with the user's auth, has read access to the\n * project, and returns a tiny JSON blob the CLI parses.\n *\n * Kept here (in `@codeam/shared`) so the CLI build inlines the\n * exact string at compile time without runtime fetch from the backend.\n */\nexport const PREVIEW_DETECT_PROMPT = `\nAnalyze the project in the current working directory and return how to start\nits development server for in-app preview.\n\nRead package.json, Procfile, Dockerfile, docker-compose.yml, manage.py, app.json,\nmix.exs, Cargo.toml, go.mod, requirements.txt, Gemfile, and any other framework\nmarkers you find at depth <= 2.\n\nReturn ONLY a JSON object on stdout (no prose, no markdown fences):\n\n{\n \"framework\": \"<name, or 'unsupported'>\",\n \"command\": \"<executable>\",\n \"args\": [\"...\"],\n \"port\": <number>,\n \"ready_pattern\": \"<regex matching the server-ready stdout line>\",\n \"env\": { \"HOST\": \"0.0.0.0\" },\n \"setup_commands\": [{ \"cmd\": \"<executable>\", \"args\": [\"...\"] }],\n \"notes\": \"<one-line caveat or null>\"\n}\n\nRules:\n- Pick the script the developer would run locally to see the app (typically \"dev\", \"start\", \"serve\").\n- Prefer binding to 0.0.0.0 — most frameworks default to localhost which the tunnel cannot reach.\n- For Expo: framework=\"Expo\", command=\"npx\", args=[\"expo\",\"start\",\"--tunnel\"], port=8081, notes=\"Scan QR with Expo Go\".\n- If no dev server applies (CLI library, lambda, batch script): {\"framework\":\"unsupported\",\"notes\":\"<reason>\"}.\n\nCRITICAL — setup_commands:\n- DO NOT include an install command (npm install, pnpm install, yarn install,\n yarn, bun install) in setup_commands. A lockfile-aware pre-flight installer\n runs BEFORE setup_commands and picks the correct package manager from the\n lockfile present (pnpm-lock.yaml -> pnpm, yarn.lock -> yarn, bun.lockb -> bun,\n else npm). Emitting an install here either duplicates that work or, worse,\n uses the WRONG package manager on top of node_modules just populated by the\n pre-flight, which crashes (e.g. npm errors with \"Cannot read properties of\n null (reading 'matches')\" when run over pnpm's .pnpm/ layout).\n- ONLY include setup_commands for genuinely non-install work the project needs\n before its dev server can boot: prisma generate, codegen, prebuild scripts,\n database migrations against a local SQLite, etc.\n- Each setup_commands entry MUST be an object {\"cmd\": \"...\", \"args\": [\"...\"]} —\n e.g. {\"cmd\": \"npx\", \"args\": [\"prisma\", \"generate\"]}. NOT a bare string.\n- For most projects, setup_commands should be an empty array [].\n\nOUTPUT JSON ONLY. NO MARKDOWN. NO COMMENTARY.\n`.trim();\n"],"mappings":";AAiBO,IAAM,mBAAmB;AAezB,IAAM,uBAAuB;AAS7B,IAAM,gCAAgC;AAOtC,IAAM,wBAAwB;;;ACvC9B,SAAS,cAAc,KAAuB;AACnD,QAAM,SAAmB,CAAC,EAAE;AAC5B,MAAI,MAAM;AACV,MAAI,MAAM;AAEV,WAAS,YAAkB;AACzB,WAAO,OAAO,UAAU,IAAK,QAAO,KAAK,EAAE;AAAA,EAC7C;AAEA,WAAS,UAAU,IAAkB;AACnC,cAAU;AACV,QAAI,MAAM,OAAO,GAAG,EAAE,QAAQ;AAC5B,aAAO,GAAG,IAAI,OAAO,GAAG,EAAE,MAAM,GAAG,GAAG,IAAI,KAAK,OAAO,GAAG,EAAE,MAAM,MAAM,CAAC;AAAA,IAC1E,OAAO;AACL,aAAO,OAAO,GAAG,EAAE,SAAS,IAAK,QAAO,GAAG,KAAK;AAChD,aAAO,GAAG,KAAK;AAAA,IACjB;AACA;AAAA,EACF;AAEA,MAAI,IAAI;AACR,SAAO,IAAI,IAAI,QAAQ;AACrB,UAAM,KAAK,IAAI,CAAC;AAEhB,QAAI,OAAO,QAAQ;AACjB;AACA,UAAI,KAAK,IAAI,OAAQ;AAErB,UAAI,IAAI,CAAC,MAAM,KAAK;AAClB;AACA,YAAI,QAAQ;AACZ,eAAO,IAAI,IAAI,UAAU,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,EAAG,UAAS,IAAI,GAAG;AAChE,cAAM,MAAM,IAAI,CAAC,KAAK;AACtB,cAAM,IAAI,SAAS,KAAK,KAAK;AAE7B,YAAS,QAAQ,KAAK;AAAE,gBAAM,KAAK,IAAI,GAAG,MAAM,CAAC;AAAA,QAAG,WAC3C,QAAQ,KAAK;AAAE,iBAAO;AAAG,oBAAU;AAAA,QAAG,WACtC,QAAQ,KAAK;AAAE,iBAAO;AAAA,QAAG,WACzB,QAAQ,KAAK;AAAE,gBAAM,KAAK,IAAI,GAAG,MAAM,CAAC;AAAA,QAAG,WAC3C,QAAQ,KAAK;AAAE,gBAAM,KAAK,IAAI,GAAG,IAAI,CAAC;AAAA,QAAG,WACzC,QAAQ,OAAO,QAAQ,KAAK;AACnC,gBAAM,IAAI,MAAM,MAAM,GAAG;AACzB,gBAAM,KAAK,IAAI,IAAI,SAAS,EAAE,CAAC,KAAK,GAAG,KAAK,KAAK,CAAC;AAClD,gBAAM,KAAK,IAAI,IAAI,SAAS,EAAE,CAAC,KAAK,GAAG,KAAK,KAAK,CAAC;AAClD,oBAAU;AAAA,QACZ,WAAW,QAAQ,KAAK;AACtB,cAAI,UAAU,OAAO,UAAU,KAAK;AAClC,mBAAO,SAAS;AAAG,mBAAO,CAAC,IAAI;AAAI,kBAAM;AAAG,kBAAM;AAAA,UACpD,WAAW,UAAU,KAAK;AACxB,qBAAS,IAAI,GAAG,IAAI,KAAK,IAAK,QAAO,CAAC,IAAI;AAC1C,mBAAO,GAAG,IAAI,IAAI,OAAO,GAAG,IAAI,OAAO,GAAG,EAAE,MAAM,GAAG;AAAA,UACvD,OAAO;AACL,mBAAO,GAAG,IAAI,OAAO,GAAG,EAAE,MAAM,GAAG,GAAG;AACtC,mBAAO,OAAO,MAAM,CAAC;AAAA,UACvB;AAAA,QACF,WAAW,QAAQ,KAAK;AACtB,oBAAU;AACV,cAAS,UAAU,MAAM,UAAU,IAAK,QAAO,GAAG,IAAI,OAAO,GAAG,EAAE,MAAM,GAAG,GAAG;AAAA,mBACrE,UAAU,IAAK,QAAO,GAAG,IAAI,IAAI,OAAO,GAAG,IAAI,OAAO,GAAG,EAAE,MAAM,GAAG;AAAA,mBACpE,UAAU,IAAK,QAAO,GAAG,IAAI;AAAA,QACxC,WAAW,QAAQ,QAAQ,UAAU,WAAW,UAAU,QAAQ;AAChE,iBAAO,SAAS;AAAG,iBAAO,CAAC,IAAI;AAAI,gBAAM;AAAG,gBAAM;AAAA,QACpD,WAAW,QAAQ,QAAQ,UAAU,WAAW,UAAU,QAAQ;AAChE,iBAAO,SAAS;AAAG,iBAAO,CAAC,IAAI;AAAI,gBAAM;AAAG,gBAAM;AAAA,QACpD;AAAA,MACF,WAAW,IAAI,CAAC,MAAM,KAAK;AACzB;AACA,eAAO,IAAI,IAAI,QAAQ;AACrB,cAAI,IAAI,CAAC,MAAM,OAAQ;AACvB,cAAI,IAAI,CAAC,MAAM,UAAU,IAAI,IAAI,IAAI,UAAU,IAAI,IAAI,CAAC,MAAM,MAAM;AAAE;AAAK;AAAA,UAAO;AAClF;AAAA,QACF;AAAA,MACF;AAAA,IACF,WAAW,OAAO,MAAM;AACtB,UAAI,IAAI,IAAI,IAAI,UAAU,IAAI,IAAI,CAAC,MAAM,MAAM;AAC7C;AAAO,cAAM;AAAG,kBAAU;AAAG;AAAA,MAC/B,OAAO;AACL,cAAM;AAAA,MACR;AAAA,IACF,WAAW,OAAO,MAAM;AACtB;AAAO,YAAM;AAAG,gBAAU;AAAA,IAC5B,WAAW,MAAM,OAAO,OAAO,KAAM;AACnC,gBAAU,EAAE;AAAA,IACd;AAEA;AAAA,EACF;AAEA,SAAO;AACT;;;AClGA,SAAS,SAAS;AAmBlB,IAAM,sBAAsB,EAAE,OAAO;AAAA,EACnC,IAAI,EAAE,OAAO;AAAA,EACb,WAAW,EAAE,OAAO;AAAA,EACpB,UAAU,EAAE,OAAO;AAAA,EACnB,MAAM,EAAE,OAAO;AAAA;AAAA;AAAA,EAGf,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAAE,QAAQ;AAAA,EACnD,QAAQ,EAAE,OAAO;AAAA,EACjB,WAAW,EAAE,OAAO;AACtB,CAAC;AAOM,SAAS,gBAAgB,KAAoC;AAClE,QAAM,SAAS,oBAAoB,UAAU,GAAG;AAChD,MAAI,CAAC,OAAO,QAAS,QAAO;AAC5B,QAAM,EAAE,SAAS,GAAG,KAAK,IAAI,OAAO;AACpC,SAAO,EAAE,GAAG,MAAM,SAAS,WAAW,CAAC,EAAE;AAC3C;;;AClCO,IAAM,gBAA8C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOzD,mBAAmB,EAAE,OAAO,IAAI,QAAQ,IAAI,WAAW,KAAM,YAAY,MAAM;AAAA,EAC/E,mBAAmB,EAAE,OAAO,IAAI,QAAQ,IAAI,WAAW,KAAM,YAAY,MAAM;AAAA,EAC/E,qBAAqB,EAAE,OAAO,GAAG,QAAQ,IAAI,WAAW,KAAM,YAAY,KAAK;AAAA;AAAA;AAAA;AAAA,EAI/E,oBAAoB,EAAE,OAAO,KAAM,QAAQ,GAAG,WAAW,MAAM,YAAY,EAAE;AAAA,EAC7E,mBAAmB,EAAE,OAAO,GAAG,QAAQ,IAAI,WAAW,KAAM,YAAY,KAAK;AAAA,EAC7E,iBAAiB,EAAE,OAAO,IAAI,QAAQ,IAAI,WAAW,KAAM,YAAY,MAAM;AAAA,EAC7E,qBAAqB,EAAE,OAAO,GAAG,QAAQ,IAAI,WAAW,KAAM,YAAY,KAAK;AAAA,EAC/E,oBAAoB,EAAE,OAAO,KAAM,QAAQ,GAAG,WAAW,MAAM,YAAY,EAAE;AAAA,EAC7E,kBAAkB,EAAE,OAAO,MAAM,QAAQ,MAAM,WAAW,MAAM,YAAY,IAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASjF,WAAW,EAAE,OAAO,MAAM,QAAQ,IAAI,WAAW,OAAO,YAAY,KAAK;AAAA,EACzE,WAAW,EAAE,OAAO,MAAM,QAAQ,IAAI,WAAW,OAAO,YAAY,KAAK;AAAA,EACzE,gBAAgB,EAAE,OAAO,MAAM,QAAQ,GAAG,WAAW,OAAO,YAAY,KAAK;AAAA,EAC7E,iBAAiB,EAAE,OAAO,MAAM,QAAQ,IAAI,WAAW,OAAO,YAAY,KAAK;AAAA,EAC/E,WAAW,EAAE,OAAO,MAAM,QAAQ,IAAI,WAAW,OAAO,YAAY,KAAK;AAAA,EACzE,qBAAqB,EAAE,OAAO,MAAM,QAAQ,IAAI,WAAW,OAAO,YAAY,KAAK;AACrF;AAEO,IAAM,uBAA+C;AAAA;AAAA,EAE1D,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,iBAAiB;AAAA,EACjB,mBAAmB;AAAA,EACnB,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,kBAAkB;AAAA;AAAA,EAGlB,WAAW;AAAA,EACX,WAAW;AAAA,EACX,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX,qBAAqB;AACvB;AAEA,IAAM,yBAAyB;AAQ/B,SAAS,mBAAsB,OAA0B,OAA8B;AACrF,MAAI;AACJ,MAAI,UAAU;AACd,aAAW,CAAC,QAAQ,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AACnD,QAAI,OAAO,SAAS,WAAW,MAAM,WAAW,MAAM,GAAG;AACvD,aAAO;AACP,gBAAU,OAAO;AAAA,IACnB;AAAA,EACF;AACA,SAAO;AACT;AAIO,SAAS,aAAa,OAAwB;AACnD,SAAO,mBAAmB,eAAe,KAAK,MAAM;AACtD;AAUO,IAAM,wBAAsC;AAAA,EACjD,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,YAAY;AACd;AAQO,SAAS,WAAW,OAA6B;AACtD,SAAO,mBAAmB,eAAe,KAAK,KAAK;AACrD;AAEO,SAAS,iBAAiB,OAA8B;AAC7D,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,mBAAmB,sBAAsB,KAAK,KAAK;AAC5D;AAUO,SAAS,oBAAoB,OAA0C;AAC5E,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,mBAAmB,sBAAsB,KAAK;AACvD;;;AChIO,IAAM,iBAAiD;AAAA,EAC5D,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,IAKT,oBAAoB,CAAC,eAAe,eAAe,SAAS;AAAA,IAC5D,mBAAmB;AAAA,IACnB,mBAAmB;AAAA,IACnB,cAAc;AAAA;AAAA,IAEd,KAAK;AAAA,EACP;AAAA,EACA,OAAO;AAAA,IACL,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,oBAAoB,CAAC,eAAe,SAAS;AAAA,IAC7C,mBAAmB;AAAA,IACnB,mBAAmB;AAAA,IACnB,cAAc;AAAA;AAAA,IAEd,KAAK;AAAA;AAAA;AAAA,IAGL,YAAY;AAAA,IACZ,eAAe;AAAA,EACjB;AAAA,EACA,SAAS;AAAA,IACP,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,oBAAoB,CAAC,aAAa;AAAA,IAClC,mBAAmB;AAAA;AAAA;AAAA,IAGnB,mBAAmB;AAAA,IACnB,cAAc;AAAA,IACd,KAAK;AAAA,EACP;AAAA,EACA,YAAY;AAAA,IACV,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOT,oBAAoB,CAAC,eAAe,SAAS;AAAA,IAC7C,mBAAmB;AAAA,IACnB,mBAAmB;AAAA;AAAA,IAEnB,KAAK;AAAA,EACP;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMT,oBAAoB,CAAC,aAAa;AAAA,IAClC,mBAAmB;AAAA;AAAA;AAAA,IAGnB,mBAAmB;AAAA;AAAA,IAEnB,KAAK;AAAA;AAAA;AAAA,IAGL,YAAY;AAAA,IACZ,eAAe;AAAA,EACjB;AAAA,EACA,OAAO;AAAA,IACL,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,SAAS;AAAA;AAAA;AAAA;AAAA,IAIT,oBAAoB,CAAC,SAAS;AAAA,IAC9B,mBAAmB;AAAA,IACnB,mBAAmB;AAAA;AAAA,IAEnB,KAAK;AAAA,EACP;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,IAKT,oBAAoB,CAAC,eAAe,SAAS;AAAA,IAC7C,mBAAmB;AAAA;AAAA,IAEnB,mBAAmB;AAAA;AAAA,IAEnB,KAAK;AAAA,EACP;AAAA,EACA,MAAM;AAAA,IACJ,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMT,oBAAoB,CAAC,WAAW,aAAa;AAAA,IAC7C,mBAAmB;AAAA;AAAA,IAEnB,mBAAmB;AAAA;AAAA,IAEnB,KAAK;AAAA,EACP;AAAA,EACA,UAAU;AAAA,IACR,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,IAKT,oBAAoB,CAAC,SAAS;AAAA,IAC9B,mBAAmB;AAAA;AAAA,IAEnB,mBAAmB;AAAA;AAAA,IAEnB,KAAK;AAAA,EACP;AACF;AAEO,SAAS,mBAAoC;AAClD,SAAO,OAAO,OAAO,cAAc,EAAE,OAAO,OAAK,EAAE,OAAO;AAC5D;AAEO,SAAS,SAAS,IAA4B;AACnD,QAAM,OAAO,eAAe,EAAE;AAC9B,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,qBAAqB,EAAE,EAAE;AACpD,SAAO;AACT;AAEO,SAAS,eAAe,IAA2B;AACxD,SAAO,MAAM;AACf;;;ACzIO,IAAM,iBAAiB;AAGvB,IAAM,uBAAuB;AAG7B,IAAM,mBAAmB;AACzB,IAAM,qBAAqB;AAC3B,IAAM,uBAAuB;AAqB7B,IAAM,mBAA6C;AAAA,EACxD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,SAAS,gBAAgB,OAAuC;AACrE,SAAQ,iBAAuC,SAAS,KAAK;AAC/D;AAgBO,IAAM,qBAET;AAAA,EACF,aAAa;AAAA;AAAA,EAEb,QAAQ;AAAA,EACR,OAAO;AAAA;AAAA;AAAA,EAGP,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,MAAM;AAAA;AAAA;AAAA,EAGN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQV,YAAY;AAAA;AAAA;AAAA,EAGZ,CAAC,cAAc,GAAG;AACpB;AAOO,IAAM,qBAAwE;AAAA,EACnF,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,UAAU;AACZ;AAEA,SAAS,sBAAsB,GAAsD;AAEnF,SAAO,OAAO,UAAU,eAAe,KAAK,oBAAoB,CAAC;AACnE;AAGO,SAAS,iBAAiB,UAAkC;AACjE,SAAO,sBAAsB,QAAQ,IAAI,mBAAmB,QAAQ,IAAI;AAC1E;AAGO,SAAS,iBAAiB,UAAyC;AACxE,SAAO,mBAAmB,QAAQ,KAAK;AACzC;AAKO,IAAM,wBAAwB;AAOrC,IAAM,mBAAsD;AAAA,EAC1D,aAAa;AAAA,EACb,eAAe;AAAA,EACf,yBAAyB;AAAA,EACzB,qBAAqB;AAAA,EACrB,uBAAuB;AAAA,EACvB,oBAAoB;AAAA,EACpB,4BAA4B;AAAA,EAC5B,wBAAwB;AAAA,EACxB,kBAAkB;AAAA,EAClB,kCAAkC;AACpC;AAeO,SAAS,iBAAiB,KAA6B;AAC5D,QAAM,SAAS,OAAO,IAAI,KAAK,EAAE,YAAY;AAC7C,MAAI,CAAC,MAAO,QAAO;AAEnB,MAAI,eAAe,KAAK,EAAG,QAAO;AAElC,QAAM,aAAa,MAAM,WAAW,qBAAqB,IACrD,MAAM,MAAM,sBAAsB,MAAM,IACxC;AACJ,MAAI,eAAe,UAAU,EAAG,QAAO;AAEvC,SAAO,iBAAiB,UAAU,KAAK;AACzC;AAsBO,SAAS,gBAAgB,SAAsC;AACpE,QAAM,cAAc,WAAW,IAAI,YAAY,EAAE,QAAQ,SAAS,EAAE;AACpE,MAAI,CAAC,WAAY,QAAO;AACxB,aAAW,QAAQ,OAAO,OAAO,cAAc,GAAG;AAChD,QAAI,KAAK,iBAAiB,UAAa,WAAW,WAAW,KAAK,EAAE,GAAG;AACrE,aAAO,KAAK;AAAA,IACd;AAAA,EACF;AACA,SAAO;AACT;AAOO,SAAS,oBAAoB,SAA0B;AAC5D,SAAO,gBAAgB,OAAO,MAAM;AACtC;;;ACjOO,IAAM,uBAAqE;AAAA,EAChF,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,IAKJ,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,MAKN,QAAQ;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,KAAK;AAAA;AAAA;AAAA;AAAA,QAIH,SAAS;AAAA,QACT,MAAM,CAAC,uBAAuB;AAAA,QAC9B,YAAY;AAAA,UACV,8BAA8B;AAAA,UAC9B,0BAA0B;AAAA,QAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAMA,WAAW,EAAE,wBAAwB,OAAO;AAAA,MAC9C;AAAA,IACF;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA,IAKV,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAON,QAAQ;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,QAKH,SAAS;AAAA;AAAA;AAAA;AAAA,QAIT,MAAM;AAAA,UACJ;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QACA,YAAY;AAAA,UACV,qBAAqB;AAAA,UACrB,aAAa;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA,IAKV,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAON,QAAQ,CAAC,QAAQ,OAAO;AAAA,IAC1B;AAAA,IACA,UAAU;AAAA,MACR,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QASH,SAAS;AAAA,QACT,MAAM,CAAC,MAAM,kBAAkB;AAAA,QAC/B,YAAY;AAAA,UACV,gBAAgB;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAeV,SAAS;AAAA,IACT,MAAM,EAAE,MAAM,cAAc,YAAY,SAAS;AAAA;AAAA,IAEjD,UAAU,CAAC;AAAA,EACb;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAUV,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAQN,QAAQ,CAAC,OAAO,kBAAkB;AAAA,IACpC;AAAA;AAAA;AAAA,IAGA,UAAU,CAAC;AAAA,EACb;AAAA,EACA,eAAe;AAAA,IACb,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAUV,SAAS;AAAA,IACT,MAAM,EAAE,MAAM,WAAW,aAAa,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAO/C,UAAU,CAAC;AAAA,EACb;AAAA,EACA,OAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMV,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAON,QAAQ;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,QAEA;AAAA,MACF;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QASH,SAAS;AAAA,QACT,MAAM,CAAC,MAAM,8CAA8C;AAAA,QAC3D,YAAY;AAAA,UACV,iBAAiB;AAAA,UACjB,eAAe;AAAA,QACjB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,iBAAiB;AAAA,IACf,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQV,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,MAAM;AAAA;AAAA;AAAA;AAAA,MAIN,QAAQ;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,KAAK;AAAA;AAAA;AAAA;AAAA,QAIH,SAAS;AAAA,QACT,MAAM,CAAC,MAAM,+BAA+B;AAAA,QAC5C,YAAY;AAAA,UACV,YAAY;AAAA,QACd;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,aAAa;AAAA,IACX,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA,IAIV,SAAS;AAAA,IACT,MAAM,EAAE,MAAM,iBAAiB;AAAA,IAC/B,UAAU,CAAC;AAAA,EACb;AAAA,EACA,SAAS;AAAA,IACP,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASV,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAON,QAAQ,CAAC,OAAO,QAAQ;AAAA,IAC1B;AAAA,IACA,UAAU;AAAA,MACR,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAMH,SAAS;AAAA,QACT,MAAM,CAAC,MAAM,mBAAmB;AAAA,QAChC,YAAY;AAAA,UACV,eAAe;AAAA,UACf,kBAAkB;AAAA,QACpB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOV,SAAS;AAAA,IACT,UAAU;AAAA,IACV,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,QAAQ;AAAA,QACN;AAAA,UACE,KAAK;AAAA,UACL,OAAO;AAAA,UACP,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,MAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,QAKH,SAAS;AAAA,QACT,MAAM,CAAC,MAAM,kBAAkB;AAAA,QAC/B,YAAY;AAAA,UACV,gBAAgB;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQV,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,QAAQ;AAAA,QACN;AAAA,UACE,KAAK;AAAA,UACL,OAAO;AAAA,UACP,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,MAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,KAAK;AAAA;AAAA;AAAA;AAAA,QAIH,SAAS;AAAA,QACT,MAAM,CAAC;AAAA,QACP,YAAY,CAAC;AAAA,QACb,SAAS;AAAA,QACT,aAAa,EAAE,eAAe,uBAAuB;AAAA,MACvD;AAAA,IACF;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOV,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,QAAQ;AAAA,QACN;AAAA,UACE,KAAK;AAAA,UACL,OAAO;AAAA,UACP,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,MAAM;AAAA,QACR;AAAA,QACA;AAAA,UACE,KAAK;AAAA,UACL,OAAO;AAAA,UACP,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,MAAM;AAAA,QACR;AAAA,QACA;AAAA,UACE,KAAK;AAAA,UACL,OAAO;AAAA,UACP,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,MAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,KAAK;AAAA;AAAA;AAAA;AAAA,QAIH,SAAS;AAAA,QACT,MAAM,CAAC;AAAA,QACP,YAAY,CAAC;AAAA,QACb,SAAS;AAAA,QACT,aAAa;AAAA,UACX,cAAc;AAAA,UACd,sBAAsB;AAAA,QACxB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA,IAKV,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,MAKN,QAAQ,CAAC;AAAA,IACX;AAAA,IACA,UAAU;AAAA,MACR,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAMH,SAAS;AAAA,QACT,MAAM,CAAC,MAAM,mCAAmC;AAAA,QAChD,YAAY;AAAA,UACV,cAAc;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,cAAc;AAAA,IACZ,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMV,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,QAAQ;AAAA,QACN;AAAA,UACE,KAAK;AAAA,UACL,OAAO;AAAA,UACP,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,MAAM;AAAA,QACR;AAAA,QACA;AAAA,UACE,KAAK;AAAA,UACL,OAAO;AAAA,UACP,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,MAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAOH,SAAS;AAAA,QACT,MAAM,CAAC,MAAM,+CAA+C;AAAA,QAC5D,YAAY;AAAA,UACV,kBAAkB;AAAA,UAClB,sBAAsB;AAAA,QACxB;AAAA,QACA,WAAW,EAAE,0BAA0B,MAAM;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AAAA,EACA,OAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMV,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMN,QAAQ;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA;AAAA;AAAA;AAAA;AAAA,MAKA,gBAAgB;AAAA,QACd,QAAQ;AAAA,UACN;AAAA,YACE,KAAK;AAAA,YACL,OAAO;AAAA,YACP,aAAa;AAAA,YACb,QAAQ;AAAA,YACR,MAAM;AAAA,UACR;AAAA,QACF;AAAA,QACA,YAAY;AAAA,UACV,eAAe;AAAA,QACjB;AAAA,MACF;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QASH,SAAS;AAAA,QACT,MAAM,CAAC,MAAM,8BAA8B,WAAW,gBAAgB;AAAA,QACtE,YAAY;AAAA,UACV,mBAAmB;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOV,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,QAAQ;AAAA,QACN;AAAA,UACE,KAAK;AAAA,UACL,OAAO;AAAA,UACP,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,MAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,KAAK;AAAA;AAAA;AAAA,QAGH,SAAS;AAAA,QACT,MAAM,CAAC;AAAA,QACP,YAAY,CAAC;AAAA,QACb,SAAS;AAAA,QACT,aAAa,EAAE,kBAAkB,gBAAgB;AAAA,MACnD;AAAA,IACF;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAaV,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,QAAQ,CAAC,UAAU,gBAAgB;AAAA,IACrC;AAAA,IACA,UAAU;AAAA,MACR,KAAK;AAAA,QACH,SAAS;AAAA,QACT,MAAM,CAAC;AAAA,QACP,YAAY,CAAC;AAAA,QACb,SAAS;AAAA,QACT,aAAa,EAAE,eAAe,uBAAuB;AAAA,MACvD;AAAA,IACF;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMV,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,QAAQ;AAAA,QACN;AAAA,UACE,KAAK;AAAA,UACL,OAAO;AAAA,UACP,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,MAAM;AAAA,QACR;AAAA,QACA;AAAA,UACE,KAAK;AAAA,UACL,OAAO;AAAA,UACP,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,MAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,KAAK;AAAA;AAAA;AAAA,QAGH,SAAS;AAAA,QACT,MAAM,CAAC,MAAM,yBAAyB;AAAA,QACtC,YAAY;AAAA,UACV,gBAAgB;AAAA,UAChB,cAAc;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA,IAKV,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,QAAQ,CAAC;AAAA,IACX;AAAA,IACA,UAAU;AAAA,MACR,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,QAKH,SAAS;AAAA,QACT,MAAM,CAAC,MAAM,uCAAuC;AAAA,QACpD,YAAY;AAAA,UACV,iBAAiB;AAAA,UACjB,iBAAiB;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,KAAK;AAAA,IACH,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOV,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,QAAQ;AAAA,QACN;AAAA,UACE,KAAK;AAAA,UACL,OAAO;AAAA,UACP,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,MAAM;AAAA,QACR;AAAA,QACA;AAAA,UACE,KAAK;AAAA,UACL,OAAO;AAAA,UACP,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,MAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,KAAK;AAAA;AAAA;AAAA,QAGH,SAAS;AAAA,QACT,MAAM,CAAC,MAAM,gBAAgB;AAAA,QAC7B,YAAY;AAAA,UACV,aAAa;AAAA,UACb,aAAa;AAAA,QACf;AAAA;AAAA,QAEA,WAAW,EAAE,UAAU,QAAQ;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA,IAIV,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,QAAQ;AAAA,QACN;AAAA,UACE,KAAK;AAAA,UACL,OAAO;AAAA,UACP,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,MAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,KAAK;AAAA;AAAA;AAAA,QAGH,SAAS;AAAA,QACT,MAAM,CAAC,MAAM,oCAAoC;AAAA,QACjD,YAAY;AAAA,UACV,iBAAiB;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,UAAU;AAAA,IACR,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA,IAKV,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,QAAQ;AAAA,QACN;AAAA,UACE,KAAK;AAAA,UACL,OAAO;AAAA,UACP,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,MAAM;AAAA,QACR;AAAA,QACA;AAAA,UACE,KAAK;AAAA,UACL,OAAO;AAAA,UACP,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,MAAM;AAAA,QACR;AAAA,QACA;AAAA,UACE,KAAK;AAAA,UACL,OAAO;AAAA,UACP,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,MAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,KAAK;AAAA;AAAA;AAAA;AAAA,QAIH,SAAS;AAAA,QACT,MAAM,CAAC,MAAM,6BAA6B;AAAA,QAC1C,YAAY;AAAA,UACV,mCAAmC;AAAA,UACnC,mCAAmC;AAAA,UACnC,qBAAqB;AAAA,QACvB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,UAAU;AAAA,IACR,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOV,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,MAAM;AAAA;AAAA;AAAA,MAGN,QAAQ,CAAC,KAAK;AAAA,IAChB;AAAA,IACA,UAAU;AAAA,MACR,KAAK;AAAA;AAAA;AAAA;AAAA,QAIH,SAAS;AAAA,QACT,MAAM,CAAC,MAAM,qCAAqC;AAAA,QAClD,YAAY;AAAA,UACV,uBAAuB;AAAA,QACzB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAcV,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,QAAQ;AAAA,QACN;AAAA,UACE,KAAK;AAAA,UACL,OAAO;AAAA,UACP,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,MAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,QAKH,SAAS;AAAA,QACT,SAAS;AAAA,QACT,MAAM,CAAC;AAAA,QACP,YAAY,CAAC;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAAA,EACA,YAAY;AAAA,IACV,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOV,SAAS;AAAA,IACT,MAAM,EAAE,MAAM,WAAW,SAAS,OAAO;AAAA,IACzC,UAAU;AAAA,MACR,KAAK;AAAA;AAAA;AAAA;AAAA,QAIH,SAAS;AAAA,QACT,MAAM,CAAC,uBAAuB;AAAA,QAC9B,YAAY;AAAA,UACV,8BAA8B;AAAA,UAC9B,0BAA0B;AAAA,QAC5B;AAAA,QACA,WAAW,EAAE,wBAAwB,OAAO;AAAA,MAC9C;AAAA,IACF;AAAA,EACF;AAAA,EACA,YAAY;AAAA,IACV,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMV,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,MAAM;AAAA;AAAA;AAAA;AAAA,MAIN,QAAQ;AAAA;AAAA,QAEN;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,QAEA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,QAEA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,QAEA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,KAAK;AAAA;AAAA;AAAA;AAAA,QAIH,SAAS;AAAA,QACT,MAAM,CAAC,MAAM,0CAA0C;AAAA,QACvD,YAAY;AAAA,UACV,sBAAsB;AAAA,UACtB,uBAAuB;AAAA,QACzB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,yBAAkD;AAChE,SAAO,OAAO,OAAO,oBAAoB,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO;AACpE;AAEO,SAAS,eAAe,IAA0C;AACvE,QAAM,OAAO,qBAAqB,EAAE;AACpC,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,2BAA2B,EAAE,EAAE;AAC1D,SAAO;AACT;AAEO,SAAS,qBAAqB,IAAiC;AACpE,SAAO,MAAM;AACf;AAEO,SAAS,0BACd,UACyB;AACzB,SAAO,OAAO,OAAO,oBAAoB,EAAE;AAAA,IACzC,CAAC,MAAM,EAAE,aAAa,YAAY,EAAE;AAAA,EACtC;AACF;;;AC3kCO,IAAM,uBAA4D;AAAA;AAAA;AAAA;AAAA;AAAA,EAKvE,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAKA,eAAe;AAAA,IACb,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAIA,KAAK;AAAA,IACH,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA;AAAA;AAAA,IAGJ,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,YAAY;AAAA,IACV,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,OAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,iBAAiB;AAAA,IACf,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,aAAa;AAAA,IACX,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,SAAS;AAAA,IACP,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,cAAc;AAAA,IACZ,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,OAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,SAAS;AAAA,IACP,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,SAAS;AAAA,IACP,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,OAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA;AAAA;AAAA,IAGZ,SACE;AAAA,EACJ;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,UAAU;AAAA,IACR,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,YAAY;AAAA,IACV,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,OAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,SAAS;AAAA,IACP,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,KAAK;AAAA,IACH,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,UAAU;AAAA,IACR,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,OAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,WAAW;AAAA,IACT,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,WAAW;AAAA,IACT,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AAAA,EACA,SAAS;AAAA,IACP,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SACE;AAAA,EACJ;AACF;AAEO,IAAM,2BAA2B;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,SAAS,uBAAuB,IAAwC;AAC7E,SAAO,qBAAqB,EAAE,KAAK;AACrC;;;AC7VO,IAAM,qBAAoD;AAAA;AAAA,EAE/D,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,mBAAmB;AAAA,EACnB,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,sBAAsB;AAAA,EACtB,mBAAmB;AAAA;AAAA,EAEnB,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,SAAS;AAAA,EACT,UAAU;AAAA,EACV,oBAAoB;AAAA;AAAA,EAEpB,QAAQ;AAAA,EACR,yBAAyB;AAAA,EACzB,YAAY;AAAA;AAAA,EAEZ,QAAQ;AAAA,EACR,gBAAgB;AAAA,EAChB,UAAU;AAAA;AAAA,EAEV,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,cAAc;AAAA,EACd,cAAc;AAAA,EACd,WAAW;AAAA;AAAA,EAEX,eAAe;AAAA,EACf,oBAAoB;AAAA,EACpB,WAAW;AAAA,EACX,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,YAAY;AAAA;AAAA,EAEZ,QAAQ;AAAA,EACR,KAAK;AACP;AAGA,IAAM,4BAA4D;AAAA,EAChE,CAAC,YAAY,QAAQ;AAAA,EACrB,CAAC,YAAY,QAAQ;AAAA,EACrB,CAAC,gBAAgB,YAAY;AAAA,EAC7B,CAAC,cAAc,UAAU;AAAA,EACzB,CAAC,WAAW,OAAO;AAAA,EACnB,CAAC,aAAa,SAAS;AAAA,EACvB,CAAC,YAAY,QAAQ;AAAA,EACrB,CAAC,cAAc,QAAQ;AACzB;AAGA,IAAM,mBAAmB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AACA,IAAM,iBAAiB,CAAC,gBAAgB,QAAQ,sBAAsB,eAAe,SAAS;AAC9F,IAAM,kBAAkB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,OAAO,MAAmB,SAA4B;AAC7D,aAAW,KAAK,QAAS,KAAI,KAAK,IAAI,CAAC,EAAG,QAAO;AACjD,SAAO;AACT;AAGO,SAAS,cAAc,UAA+B;AAC3D,QAAM,OAAO,IAAI,IAAI,QAAQ;AAC7B,QAAM,SAAS,OAAO,MAAM,cAAc;AAC1C,MAAI,OAAQ,QAAO;AACnB,QAAM,WAAW,OAAO,MAAM,gBAAgB;AAC9C,QAAM,UAAU,OAAO,MAAM,eAAe;AAC5C,MAAI,YAAY,QAAS,QAAO;AAChC,MAAI,SAAU,QAAO;AACrB,MAAI,QAAS,QAAO;AACpB,SAAO;AACT;AAGO,SAAS,6BAA6B,UAAqC;AAChF,QAAM,MAAuB,CAAC;AAC9B,QAAM,OAAO,oBAAI,IAAmB;AACpC,QAAM,OAAO,CAAC,OAAsB;AAClC,QAAI,CAAC,KAAK,IAAI,EAAE,GAAG;AACjB,WAAK,IAAI,EAAE;AACX,UAAI,KAAK,EAAE;AAAA,IACb;AAAA,EACF;AACA,aAAW,QAAQ,UAAU;AAC3B,UAAM,QAAQ,mBAAmB,IAAI;AACrC,QAAI,OAAO;AACT,WAAK,KAAK;AACV;AAAA,IACF;AACA,eAAW,CAAC,QAAQ,EAAE,KAAK,2BAA2B;AACpD,UAAI,KAAK,WAAW,MAAM,GAAG;AAC3B,aAAK,EAAE;AACP;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAGO,IAAM,uBAA2D;AAAA,EACtE,UAAU,CAAC,SAAS,UAAU,WAAW,QAAQ;AAAA,EACjD,SAAS,CAAC,UAAU,WAAW,YAAY,UAAU,YAAY;AAAA,EACjE,WAAW,CAAC,UAAU,WAAW,UAAU,YAAY,QAAQ;AAAA,EAC/D,QAAQ,CAAC,UAAU,SAAS;AAAA,EAC5B,SAAS,CAAC;AACZ;AASO,SAAS,iBAAiB,UAAwC;AACvE,QAAM,QAAQ,cAAc,QAAQ;AACpC,QAAM,WAAW,6BAA6B,QAAQ;AACtD,QAAM,cAAc,IAAI,IAAI,QAAQ;AACpC,QAAM,cAAc,qBAAqB,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC;AACnF,SAAO,EAAE,OAAO,UAAU,aAAa,QAAQ,OAAO;AACxD;;;AC9KA,IAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2BzB,IAAM,0BAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAOzB,IAAM,kBAAmC;AAAA,EAC9C,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,UAAU;AAAA,IACR,WAAW,EAAE,MAAM,iBAAiB;AAAA,IACpC,aAAa,EAAE,MAAM,wBAAwB;AAAA,EAC/C;AACF;;;AC7CA,IAAM,yBAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkB/B,IAAM,gCAAgC;AAAA;AAAA;AAAA;AAAA;AAAA;AAO/B,IAAM,wBAAyC;AAAA,EACpD,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,UAAU;AAAA,IACR,WAAW,EAAE,MAAM,uBAAuB;AAAA,IAC1C,aAAa,EAAE,MAAM,8BAA8B;AAAA,EACrD;AACF;;;AC7BA,IAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkFzB,IAAM,0BAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBzB,IAAM,6BAA8C;AAAA,EACzD,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,UAAU;AAAA,IACR,WAAW,EAAE,MAAM,iBAAiB;AAAA,IACpC,aAAa,EAAE,MAAM,wBAAwB;AAAA,EAC/C;AACF;;;AC3GA,IAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4EzB,IAAM,0BAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAYzB,IAAM,kBAAmC;AAAA,EAC9C,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,UAAU;AAAA,IACR,WAAW,EAAE,MAAM,iBAAiB;AAAA,IACpC,aAAa,EAAE,MAAM,wBAAwB;AAAA,EAC/C;AACF;;;ACjGO,IAAM,iBAAmD;AAAA,EAC9D,eAAe;AAAA,EACf,qBAAqB;AAAA,EACrB,2BAA2B;AAAA,EAC3B,eAAe;AACjB;AAEO,SAAS,UAAU,IAA2B;AACnD,SAAO,OAAO,UAAU,eAAe,KAAK,gBAAgB,EAAE;AAChE;AAEO,SAAS,mBAAmB,IAAoC;AACrE,SAAO,UAAU,EAAE,IAAI,eAAe,EAAE,IAAI;AAC9C;AAEO,SAAS,aAAa,IAAa,MAA0B;AAClE,SAAO,QAAQ,eAAe,EAAE,EAAE,SAAS,IAAI,CAAC;AAClD;;;ACfO,IAAM,uBAAuB;AAO7B,IAAM,mBAAmB;AAgBzB,SAAS,oBAA4B;AAM1C,QAAM,MAAO,WAA0E,SAAS;AAChG,QAAM,WAAW,KAAK,gBAAgB,KAAK;AAC3C,MAAI,SAAU,QAAO;AACrB,QAAM,WAAW,KAAK,kBAAkB,KAAK;AAC7C,MAAI,aAAa,OAAO,UAAU,YAAY,MAAM,OAAQ,QAAO;AACnE,SAAO;AACT;;;ACrBO,IAAM,sBAAsB;AAM5B,IAAM,uBAAuB;AAAA,EAClC,2BAA2B;AAC7B;AAOO,IAAM,0BAA6C;AAAA,EACxD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAWO,IAAM,6BAAmF;AAAA,EAC9F,WAAW,CAAC,SAAS,MAAM;AAAA,EAC3B,YAAY,CAAC,SAAS,MAAM;AAAA,EAC5B,UAAU,CAAC,SAAS,QAAQ,OAAO;AACrC;AAGO,SAAS,mBAAmB,QAAmC;AACpE,SAAO,eAAe,OAAO,KAAK,GAAG,CAAC;AACxC;AAcO,IAAM,kBAAgD;AAAA,EAC3D;AAAA,IACE,MAAM;AAAA,IACN,eAAe,CAAC,UAAU,eAAe,uBAAuB;AAAA,EAClE;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,eAAe,CAAC,UAAU,cAAc,SAAS,UAAU,SAAS;AAAA,EACtE;AACF;AAWO,SAAS,6BACd,OACA,OAAiC,CAAC,GAC1B;AACR,QAAM,MAAM,KAAK,kBAAkB,OAAO;AAC1C,QAAM,WAAW,MAAM,cAAc,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,KAAK,GAAG;AAClE,SAAO,sBAAsB,MAAM,IAAI,sBAAsB,QAAQ;AACvE;AAOO,SAAS,+BAA+B,OAAiC,CAAC,GAAW;AAC1F,SAAO;AAAA,IACL;AAAA,IACA,GAAG,gBAAgB,IAAI,CAAC,MAAM,6BAA6B,GAAG,IAAI,CAAC;AAAA,EACrE,EAAE,KAAK,IAAI;AACb;;;AC1GO,IAAM,cAAc;AAAA,EACzB,uBAAuB;AAAA,EACvB,sBAAsB;AAAA,EACtB,wBAAwB;AAAA,EACxB,+BAA+B;AAAA,EAC/B,sBAAsB;AAAA,EACtB,wBAAwB;AAAA,EACxB,eAAe;AAAA,EACf,WAAW;AAAA,EACX,2BAA2B;AAAA,EAC3B,sBAAsB;AAAA,EACtB,cAAc;AAAA,EACd,qBAAqB;AAAA,EACrB,uBAAuB;AAAA,EACvB,uBAAuB;AAAA,EACvB,sBAAsB;AAAA,EACtB,uBAAuB;AAAA,EACvB,gBAAgB;AAAA,EAChB,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,uBAAuB;AAAA,EACvB,sBAAsB;AAAA,EACtB,oBAAoB;AAAA,EACpB,eAAe;AAAA,EACf,0BAA0B;AAAA,EAC1B,2BAA2B;AAAA,EAC3B,6BAA6B;AAAA,EAC7B,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,2BAA2B;AAAA,EAC3B,qBAAqB;AAAA,EACrB,mBAAmB;AAAA,EACnB,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,wBAAwB;AAAA,EACxB,2BAA2B;AAAA,EAC3B,yBAAyB;AAAA,EACzB,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,2BAA2B;AAAA,EAC3B,mBAAmB;AAAA,EACnB,wBAAwB;AAAA,EACxB,yBAAyB;AAAA,EACzB,0BAA0B;AAAA,EAC1B,4BAA4B;AAAA,EAC5B,0BAA0B;AAAA,EAC1B,2BAA2B;AAAA,EAC3B,6BAA6B;AAAA;AAAA;AAAA;AAAA;AAAA,EAK7B,iBAAiB;AAAA,EACjB,wBAAwB;AAAA,EACxB,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,cAAc;AAAA,EACd,sCAAsC;AAAA,EACtC,sBAAsB;AAAA,EACtB,wBAAwB;AAAA,EACxB,sBAAsB;AAAA,EACtB,qBAAqB;AAAA,EACrB,mBAAmB;AAAA,EACnB,aAAa;AAAA,EACb,oBAAoB;AAAA,EACpB,sBAAsB;AAAA,EACtB,gCAAgC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQhC,8BAA8B;AAAA;AAAA;AAAA,EAG9B,qBAAqB;AAAA,EACrB,mBAAmB;AAAA,EACnB,mBAAmB;AAAA;AAAA;AAAA;AAAA,EAKnB,2BAA2B;AAAA;AAAA;AAAA;AAAA;AAAA,EAK3B,kBAAkB;AACpB;;;ACrGO,IAAM,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA4CnC,KAAK;","names":[]}
|