@stablekernel/opencode-cursor 0.8.0 → 0.9.0-next.1
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/CHANGELOG.md +47 -0
- package/README.md +110 -11
- package/dist/{chunk-YIEC27VB.js → chunk-HWSH5L4H.js} +24 -5
- package/dist/chunk-HWSH5L4H.js.map +1 -0
- package/dist/plugin/index.js +881 -25
- package/dist/plugin/index.js.map +1 -1
- package/dist/provider/index.js +1 -1
- package/dist/sidecar/agent-host.d.ts +32 -8
- package/dist/sidecar/agent-host.js +16 -1
- package/dist/sidecar/agent-host.js.map +1 -1
- package/dist/sidecar/plugin-tools-mcp.d.ts +189 -0
- package/dist/sidecar/plugin-tools-mcp.js +163 -0
- package/dist/sidecar/plugin-tools-mcp.js.map +1 -0
- package/package.json +7 -7
- package/dist/chunk-YIEC27VB.js.map +0 -1
package/dist/plugin/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/plugin/index.ts","../../src/model-limits.ts","../../src/model-cache.ts","../../src/fallback-models.ts","../../src/model-variants.ts","../../src/model-discovery.ts","../../src/plugin/model-v2.ts","../../src/plugin/mcp-config.ts","../../src/plugin/cursor-tools.ts","../../src/provider/cloud-agent.ts","../../src/provider/delegate.ts","../../src/version-check.ts","../../src/provider/skill-mirror.ts","../../src/plugin/skill-discovery.ts"],"sourcesContent":["import type { Config, Plugin } from \"@opencode-ai/plugin\";\nimport type { Auth } from \"@opencode-ai/sdk/v2\";\nimport type { McpServerConfig } from \"@cursor/sdk\";\nimport { rmSync } from \"node:fs\";\nimport semver from \"semver\";\nimport { resolveCursorApiKey } from \"../api-key.js\";\nimport { discoverModels, toOpencodeModels } from \"../model-discovery.js\";\nimport { defaultModelParams } from \"../model-variants.js\";\nimport { buildModelV2Map, PROVIDER_ID, providerNpm } from \"./model-v2.js\";\nimport {\n\tfindUnshareableOAuthServers,\n\ttype McpStatusMap,\n\ttranslateMcpServers,\n} from \"./mcp-config.js\";\nimport { buildCursorTools } from \"./cursor-tools.js\";\nimport {\n\tgetLocalVersion,\n\tgetLatestVersion,\n\tclearVersionCache,\n\tPLUGIN_CACHE_PATH,\n} from \"../version-check.js\";\nimport { removeSystemRule } from \"../provider/system-rule.js\";\nimport {\n\tclearLogBridge,\n\tpluginLog,\n\tsetLogBridge,\n} from \"../provider/log-bridge.js\";\nimport {\n\twriteSkillMirror,\n\tremoveSkillMirror,\n\tbuildSkillsCatalogue,\n} from \"../provider/skill-mirror.js\";\nimport {\n\tresolveSkills,\n\tskillSetHash,\n\ttype SkillFilterOptions,\n} from \"../plugin/skill-discovery.js\";\nimport {\n\tclearSubagentBridge,\n\tsetSubagentBridge,\n\tsubagentCallChildId,\n\tstampTaskPartSessionId,\n} from \"../provider/subagent-bridge.js\";\n\nfunction apiKeyFromAuth(auth: Auth | undefined): string | undefined {\n\treturn auth?.type === \"api\" ? auth.key : undefined;\n}\n\n/**\n * opencode plugin that adds a \"Cursor\" provider backed by the official Cursor\n * SDK (`@cursor/sdk`).\n *\n * - `auth`: registers an API-key login for Cursor and a `loader` that feeds the\n * key into the AI-SDK provider factory. The key is validated on first use\n * (model discovery / first call), not at login — see the note on `methods`.\n * - `config`: registers the provider (npm package + discovered/fallback models)\n * so it shows up in opencode immediately.\n * - `provider.models`: auth-aware live model discovery via `Cursor.models.list`.\n * - `tool.cursor_refresh_models`: force-refresh the model catalog.\n */\nexport const CursorPlugin: Plugin = async (input) => {\n\t// Single registry fetch shared by both the console warning and the UI\n\t// version-check paths. Throttled to once per 24h via an on-disk cache.\n\t// Fire-and-forget: never block or fail plugin init.\n\tconst _latestVersionPromise: Promise<string | undefined> = (async () => {\n\t\ttry {\n\t\t\tif (process.env.CI || process.env.NO_UPDATE_NOTIFIER) return undefined;\n\t\t\treturn await getLatestVersion();\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t})();\n\n\t// Surfaces the update notice in the UI (toast). Resolved once per plugin\n\t// instance using the shared fetch above.\n\tconst _versionCheckPromise: Promise<{ local: string; latest: string } | null> =\n\t\t(async () => {\n\t\t\ttry {\n\t\t\t\tif (process.env.CI || process.env.NO_UPDATE_NOTIFIER) return null;\n\t\t\t\tconst local = getLocalVersion();\n\t\t\t\tconst latest = await _latestVersionPromise;\n\t\t\t\tif (!local || !latest || !semver.gt(latest, local)) return null;\n\t\t\t\treturn { local, latest };\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t})();\n\tlet _toastShown = false;\n\n\t// The Cursor API key resolved by opencode's auth loader, captured so the\n\t// delegation tools (which don't receive auth directly) can reuse it. Falls\n\t// back to the CURSOR_API_KEY env var when the loader hasn't run.\n\tlet capturedApiKey: string | undefined;\n\n\t// opencode client + MCP-forwarding settings captured at config time so the\n\t// per-turn chat.params hook can re-forward the *live* MCP server set\n\t// (reflecting mid-session enable/disable) rather than the startup snapshot.\n\tconst client = input?.client;\n\n\t// Show a version update toast shortly after startup so it surfaces before\n\t// the user sends their first message. The 2s delay gives the TUI time to\n\t// initialize before we call showToast; it runs AFTER the version fetch\n\t// resolves so a slow network never suspends into the user's first prompt.\n\tvoid _versionCheckPromise\n\t\t.then(async (result) => {\n\t\t\tif (_toastShown || !result || !client) return;\n\t\t\t_toastShown = true;\n\t\t\tawait new Promise<void>((r) => setTimeout(r, 2000));\n\t\t\tconst message = `@stablekernel/opencode-cursor v${result.latest} is available (you have v${result.local}). Use the cursor_update_plugin tool to update, then restart opencode.`;\n\t\t\tvoid client.tui\n\t\t\t\t.showToast({\n\t\t\t\t\tbody: {\n\t\t\t\t\t\ttitle: \"Cursor plugin update available\",\n\t\t\t\t\t\tmessage,\n\t\t\t\t\t\tvariant: \"warning\",\n\t\t\t\t\t\tduration: 15000,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\t.catch(() => {});\n\t\t})\n\t\t.catch(() => {});\n\n\tconst directory = input?.directory;\n\t// Publish the opencode client + directory so the provider stream layer can\n\t// create a real child session for each Cursor subagent (making its `task`\n\t// card clickable / `ctrl+x`-navigable). Same-process handoff via a globalThis\n\t// registry; the provider degrades gracefully when it's absent.\n\tif (client) {\n\t\tsetSubagentBridge({ client, directory });\n\t\tsetLogBridge({ client, directory });\n\t}\n\t// Canonical working directory for the generated system-prompt rule: the\n\t// provider writes `.cursor/rules/opencode.mdc` under this path and dispose\n\t// cleans it up from the same path. The config hook threads it into the\n\t// provider options (respecting a user-configured `cwd` option) so write and\n\t// cleanup can never diverge.\n\tlet resolvedCwd = directory ?? process.cwd();\n\tlet forwardMcp = true;\n\tlet userMcp: Record<string, McpServerConfig> = {};\n\t// Whether to let opencode drive auto-compaction. Default false: the Cursor\n\t// agent self-compacts (preCompact hook, trigger:\"auto\"), so opencode's\n\t// compaction is redundant and is what mints a fresh agentId per compaction.\n\t// Opt in with `provider.cursor.options.autoCompaction: true`.\n\tlet autoCompaction = false;\n\t// Skill forwarding state, mirroring the MCP forwarding pattern.\n\tlet forwardSkills = true;\n\tlet skillFilterOptions: SkillFilterOptions | undefined;\n\tlet lastSkillHash = \"\";\n\tlet currentSkillsCatalogue = \"\";\n\t// OAuth servers we've already warned about, so the toast fires once per\n\t// server rather than on every turn.\n\tconst warnedOAuth = new Set<string>();\n\n\treturn {\n\t\tauth: {\n\t\t\tprovider: PROVIDER_ID,\n\t\t\tloader: async (getAuth) => {\n\t\t\t\tconst apiKey = resolveCursorApiKey(\n\t\t\t\t\tapiKeyFromAuth(await getAuth().catch(() => undefined)),\n\t\t\t\t);\n\t\t\t\tif (apiKey) {\n\t\t\t\t\tcapturedApiKey = apiKey;\n\t\t\t\t\t// The `config` hook (which seeds opencode's model picker) runs without\n\t\t\t\t\t// a key. Warm the catalog cache here — the loader is the hook that\n\t\t\t\t\t// reliably has the key — so the next launch seeds the full live\n\t\t\t\t\t// catalog instead of the static fallback.\n\t\t\t\t\t//\n\t\t\t\t\t// `forceRefresh: true` bypasses the 24h on-disk cache so a live\n\t\t\t\t\t// `Cursor.models.list()` runs on every opencode startup. This is the\n\t\t\t\t\t// stale-while-revalidate write side: the `config` and\n\t\t\t\t\t// `provider.models` hooks still serve the current cache instantly (no\n\t\t\t\t\t// startup latency), while this refreshes it in the background so newly\n\t\t\t\t\t// released Cursor models surface on the next launch instead of waiting\n\t\t\t\t\t// up to 24h for the cache to expire. Fire-and-forget: discovery never\n\t\t\t\t\t// throws and must not block auth/provider load.\n\t\t\t\t\tvoid discoverModels({ apiKey, forceRefresh: true });\n\t\t\t\t}\n\t\t\t\treturn apiKey ? { apiKey } : {};\n\t\t\t},\n\t\t\t// A single API-key method. opencode always shows its built-in \"Enter your\n\t\t\t// API key\" prompt for `type: \"api\"`, so we intentionally do NOT declare\n\t\t\t// custom `prompts` (that asks for the key a second time) or an `authorize`\n\t\t\t// callback. opencode only passes `authorize` the *custom-prompt* inputs —\n\t\t\t// never the built-in key — so validating the key in `authorize` would\n\t\t\t// force that redundant extra prompt. Instead the key is validated on first\n\t\t\t// use (model discovery / the first call both surface a bad key clearly).\n\t\t\tmethods: [{ type: \"api\", label: \"Cursor API Key\" }],\n\t\t},\n\n\t\tconfig: async (config) => {\n\t\t\tconst { models } = await discoverModels({});\n\t\t\tconfig.provider ??= {};\n\t\t\tconst existing = config.provider[PROVIDER_ID] ?? {};\n\t\t\tconst existingOptions = (existing.options ?? {}) as Record<string, unknown>;\n\n\t\t\t// Forward opencode's configured MCP servers to the Cursor\n\t\t\t// agent so it can use the same servers. Opt out via\n\t\t\t// `provider.cursor.options.forwardMcp: false`.\n\t\t\tautoCompaction = existingOptions[\"autoCompaction\"] === true;\n\t\t\tforwardMcp = existingOptions[\"forwardMcp\"] !== false;\n\t\t\tuserMcp = (existingOptions[\"mcpServers\"] ?? {}) as Record<\n\t\t\t\tstring,\n\t\t\t\tMcpServerConfig\n\t\t\t>;\n\t\t\tconst mcpServers = forwardMcp\n\t\t\t\t? { ...userMcp, ...translateMcpServers(config.mcp) }\n\t\t\t\t: userMcp;\n\n\t\t\t// opencode forwards a model's own options.params on the normal chat\n\t\t\t// path, but a subagent inheriting its parent's model reaches the provider\n\t\t\t// with them dropped — letting Cursor's server-side `fast: true` apply.\n\t\t\t// Thread the defaults through provider options (per-provider, survives\n\t\t\t// the drop) so the provider can re-apply them as a floor.\n\t\t\tconst modelParamDefaults: Record<string, Record<string, string>> = {};\n\t\t\tfor (const item of models) {\n\t\t\t\tconst params = defaultModelParams(item);\n\t\t\t\tif (Object.keys(params).length > 0) modelParamDefaults[item.id] = params;\n\t\t\t}\n\n\t\t\t// One canonical cwd for the provider's rule write and our dispose\n\t\t\t// cleanup: an explicit user option wins, else the plugin directory.\n\t\t\tconst optionCwd = existingOptions[\"cwd\"];\n\t\t\tresolvedCwd =\n\t\t\t\t(typeof optionCwd === \"string\" ? optionCwd : undefined) ??\n\t\t\t\tdirectory ??\n\t\t\t\tprocess.cwd();\n\n\t\t\t// Forward opencode's resolved skills (both project and global scope) to\n\t\t\t// the Cursor agent by mirroring them into `<cwd>/.cursor/skills/`. Cursor\n\t\t\t// discovers these natively when the `project` settings layer is loaded.\n\t\t\t// Opt out via `provider.cursor.options.forwardSkills: false`. Manual\n\t\t\t// include/exclude override via `provider.cursor.options.skills`.\n\t\t\tforwardSkills = existingOptions[\"forwardSkills\"] !== false;\n\t\t\tconst skillsOpt = existingOptions[\"skills\"] as\n\t\t\t\t| { include?: string[]; exclude?: string[] }\n\t\t\t\t| undefined;\n\t\t\tskillFilterOptions = skillsOpt;\n\n\t\t\tif (forwardSkills) {\n\t\t\t\ttry {\n\t\t\t\t\tconst resolved = resolveSkills(\n\t\t\t\t\t\tresolvedCwd,\n\t\t\t\t\t\tconfig as Config | undefined,\n\t\t\t\t\t\tskillFilterOptions,\n\t\t\t\t\t);\n\t\t\t\t\twriteSkillMirror(resolvedCwd, resolved.skills, (msg) =>\n\t\t\t\t\t\tpluginLog(\"warn\", msg),\n\t\t\t\t\t);\n\t\t\t\t\tcurrentSkillsCatalogue = buildSkillsCatalogue(resolved.skills) ?? \"\";\n\t\t\t\t\tlastSkillHash = skillSetHash(resolved.skills);\n\t\t\t\t\tif (resolved.withheld.length > 0) {\n\t\t\t\t\t\tpluginLog(\"warn\", \"skills withheld from mirror\", {\n\t\t\t\t\t\t\twithheld: resolved.withheld.map((w) => ({\n\t\t\t\t\t\t\t\tid: w.id,\n\t\t\t\t\t\t\t\treason: w.reason,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tpluginLog(\"warn\", \"skill mirror failed\", {\n\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t\timpact: \"skills unavailable to the Cursor agent this session\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconfig.provider[PROVIDER_ID] = {\n\t\t\t\tname: \"Cursor\",\n\t\t\t\tnpm: providerNpm(),\n\t\t\t\t...existing,\n\t\t\t\toptions: {\n\t\t\t\t\t...existingOptions,\n\t\t\t\t\tcwd: resolvedCwd,\n\t\t\t\t\t...(Object.keys(mcpServers).length > 0 ? { mcpServers } : {}),\n\t\t\t\t\t...(Object.keys(modelParamDefaults).length > 0\n\t\t\t\t\t\t? { modelParamDefaults }\n\t\t\t\t\t\t: {}),\n\t\t\t\t\t...(currentSkillsCatalogue\n\t\t\t\t\t\t? { skillsCatalogue: currentSkillsCatalogue }\n\t\t\t\t\t\t: {}),\n\t\t\t\t},\n\t\t\t\tmodels: {\n\t\t\t\t\t...toOpencodeModels(models, { autoCompaction }),\n\t\t\t\t\t...(existing.models ?? {}),\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\n\t\tprovider: {\n\t\t\tid: PROVIDER_ID,\n\t\t\tmodels: async (_provider, ctx) => {\n\t\t\t\tconst apiKey = apiKeyFromAuth(ctx.auth);\n\t\t\t\tconst { models } = await discoverModels({ apiKey });\n\t\t\t\treturn buildModelV2Map(models, { autoCompaction });\n\t\t\t},\n\t\t},\n\n\t\t// Bridge opencode's session id to the provider: it lands in\n\t\t// providerOptions.cursor.sessionID, which the provider reads to pool/resume a\n\t\t// Cursor agent per session (when the `session` option is enabled).\n\t\t//\n\t\t// Also map opencode's plan AGENT to Cursor's plan mode. This hook fires\n\t\t// after opencode merges the selected variant into `output.options`, so an\n\t\t// explicit mode from the `plan` variant (or model options) wins — the\n\t\t// agent-based default only applies when no mode was set.\n\t\t\"chat.params\": async (input, output) => {\n\t\t\tif (input.model?.providerID !== PROVIDER_ID) return;\n\t\t\toutput.options = {\n\t\t\t\t...(output.options ?? {}),\n\t\t\t\tsessionID: input.sessionID,\n\t\t\t};\n\t\t\tif (input.agent === \"plan\" && output.options[\"mode\"] === undefined) {\n\t\t\t\toutput.options[\"mode\"] = \"plan\";\n\t\t\t}\n\t\t\t// opencode runs its own title-generation call on the same sessionID as\n\t\t\t// a session's real first turn, concurrently, with an unrelated (empty)\n\t\t\t// system prompt. Mark it ephemeral so the provider always treats it as\n\t\t\t// a side-call regardless of whether a pool record exists yet — without\n\t\t\t// this, a race between the two calls' agent-creation round-trips can\n\t\t\t// let the title call's fingerprint win and permanently overwrite the\n\t\t\t// session's pool record (see language-model.ts's `ephemeral` check).\n\t\t\tif (input.agent === \"title\") {\n\t\t\t\toutput.options[\"ephemeral\"] = true;\n\t\t\t}\n\n\t\t\t// Dynamically re-forward MCP servers from opencode's *live* state so\n\t\t\t// mid-session enable/disable reaches the Cursor agent (the config hook\n\t\t\t// only snapshots the set once, at startup). `client.mcp.status()` is the\n\t\t\t// runtime truth (connected/disabled/...) and `client.config.get()`\n\t\t\t// supplies the launch specs. On any failure we leave the static snapshot\n\t\t\t// (already baked into the provider options) in place.\n\t\t\tif (forwardMcp && client) {\n\t\t\t\ttry {\n\t\t\t\t\tconst query = directory ? { query: { directory } } : undefined;\n\t\t\t\t\tconst [cfgRes, statusRes] = await Promise.all([\n\t\t\t\t\t\tclient.config.get(),\n\t\t\t\t\t\tclient.mcp.status(query),\n\t\t\t\t\t]);\n\t\t\t\t\tconst liveMcp = (cfgRes?.data as Config | undefined)?.mcp;\n\t\t\t\t\tconst status = statusRes?.data as McpStatusMap | undefined;\n\t\t\t\t\tif (status) {\n\t\t\t\t\t\toutput.options[\"mcpServers\"] = {\n\t\t\t\t\t\t\t...userMcp,\n\t\t\t\t\t\t\t...translateMcpServers(liveMcp, status),\n\t\t\t\t\t\t};\n\t\t\t\t\t\t// Notify (once) about OAuth servers we can't forward: opencode\n\t\t\t\t\t\t// holds their token and it never reaches config.mcp, so the\n\t\t\t\t\t\t// Cursor agent can't connect. Only those without a shareable\n\t\t\t\t\t\t// client registration are skipped; ones with a clientId are\n\t\t\t\t\t\t// forwarded with an `auth` block for the agent's own OAuth flow.\n\t\t\t\t\t\tconst unshareable = findUnshareableOAuthServers(liveMcp, status).filter(\n\t\t\t\t\t\t\t(name) => !warnedOAuth.has(name),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (unshareable.length > 0) {\n\t\t\t\t\t\t\tfor (const name of unshareable) warnedOAuth.add(name);\n\t\t\t\t\t\t\tconst plural = unshareable.length > 1;\n\t\t\t\t\t\t\tvoid client.tui\n\t\t\t\t\t\t\t\t.showToast({\n\t\t\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\t\t\ttitle: \"Cursor MCP\",\n\t\t\t\t\t\t\t\t\t\tmessage: `Skipped OAuth MCP server${plural ? \"s\" : \"\"}: ${unshareable.join(\", \")}. opencode's token can't be shared with the Cursor agent; configure an OAuth clientId to forward ${plural ? \"them\" : \"it\"}.`,\n\t\t\t\t\t\t\t\t\t\tvariant: \"warning\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t.catch(() => {});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Keep the static snapshot; live forwarding is best-effort.\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Re-sync the skill mirror from opencode's *live* state so skills\n\t\t\t// added/removed mid-session reach the Cursor agent on the next turn.\n\t\t\t// Hash the resolved skill set and skip the write when unchanged.\n\t\t\tif (forwardSkills) {\n\t\t\t\tif (client) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst query = directory ? { query: { directory } } : undefined;\n\t\t\t\t\t\tconst cfgRes = await client.config.get(query);\n\t\t\t\t\t\tconst liveConfig = cfgRes?.data as Config | undefined;\n\t\t\t\t\t\tconst resolved = resolveSkills(\n\t\t\t\t\t\t\tresolvedCwd,\n\t\t\t\t\t\t\tliveConfig,\n\t\t\t\t\t\t\tskillFilterOptions,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst hash = skillSetHash(resolved.skills);\n\t\t\t\t\t\tif (hash !== lastSkillHash) {\n\t\t\t\t\t\t\twriteSkillMirror(resolvedCwd, resolved.skills, (msg) =>\n\t\t\t\t\t\t\t\tpluginLog(\"warn\", msg),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tcurrentSkillsCatalogue = buildSkillsCatalogue(resolved.skills) ?? \"\";\n\t\t\t\t\t\t\tlastSkillHash = hash;\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Keep the existing mirror; live re-sync is best-effort.\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Always override the startup snapshot, including with an empty\n\t\t\t\t// string when all skills were removed or withheld.\n\t\t\t\toutput.options[\"skillsCatalogue\"] = currentSkillsCatalogue;\n\t\t\t}\n\t\t},\n\n\t\t// Stamp the child session id on the RUNNING `task` part. The provider\n\t\t// creates the child session when the Cursor subagent starts and\n\t\t// publishes call→child on the bridge registry; when opencode's\n\t\t// processor lands the task part (`message.part.updated`), patch it\n\t\t// (`part.update`, the native `ctx.metadata` equivalent) so the TUI\n\t\t// card carries `state.metadata.sessionId` from the start — matching\n\t\t// the native task tool, which publishes the id at execute time. The\n\t\t// processor emits a running-state part update for every streamed\n\t\t// tool part, so this fires early; the stamp is idempotent.\n\t\tevent: async (input) => {\n\t\t\tconst evt = input.event;\n\t\t\tif (evt.type !== \"message.part.updated\") return;\n\t\t\tconst part = evt.properties.part;\n\t\t\tif (!part || part.type !== \"tool\" || part.tool !== \"task\") return;\n\t\t\tconst childId = subagentCallChildId(part.callID);\n\t\t\tif (!childId) return;\n\t\t\tvoid stampTaskPartSessionId({\n\t\t\t\tsessionID: part.sessionID,\n\t\t\t\tmessageID: part.messageID,\n\t\t\t\tpartID: part.id,\n\t\t\t\tpart,\n\t\t\t\tchildId,\n\t\t\t});\n\t\t},\n\n\t\ttool: {\n\t\t\tcursor_update_plugin: {\n\t\t\t\tdescription:\n\t\t\t\t\t\"Check if the @stablekernel/opencode-cursor plugin is up to date and update it if not. Call this when the user asks to update, upgrade, or refresh the cursor plugin. Clears the cached install so opencode fetches the latest version on next launch.\",\n\t\t\t\targs: {},\n\t\t\t\texecute: async () => {\n\t\t\t\t\tif (process.env.CI || process.env.NO_UPDATE_NOTIFIER) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttitle: \"cursor plugin (checks disabled)\",\n\t\t\t\t\t\t\toutput: \"Update checks are disabled (CI or NO_UPDATE_NOTIFIER is set).\",\n\t\t\t\t\t\t\tmetadata: {\n\t\t\t\t\t\t\t\tlocal: undefined,\n\t\t\t\t\t\t\t\tlatest: undefined,\n\t\t\t\t\t\t\t\tstatus: \"disabled\" as const,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\tconst local = getLocalVersion();\n\t\t\t\t\tif (!local || !semver.valid(local)) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttitle: \"cursor plugin (unknown version)\",\n\t\t\t\t\t\t\toutput: \"Could not determine the installed plugin version.\",\n\t\t\t\t\t\t\tmetadata: { local, latest: undefined, status: \"failed\" as const },\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\tconst latest = await getLatestVersion();\n\t\t\t\t\tif (!latest || !semver.valid(latest)) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttitle: \"cursor plugin (registry unavailable)\",\n\t\t\t\t\t\t\toutput:\n\t\t\t\t\t\t\t\t\"Could not fetch the latest version from npm. Check your network connection and try again.\",\n\t\t\t\t\t\t\tmetadata: { local, latest, status: \"failed\" as const },\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!semver.gt(latest, local)) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttitle: \"cursor plugin (up to date)\",\n\t\t\t\t\t\t\toutput: `The plugin is up to date (v${local}).`,\n\t\t\t\t\t\t\tmetadata: { local, latest, status: \"up-to-date\" as const },\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\t// Plugin is outdated — clear the opencode plugin cache so it re-fetches on next launch.\n\t\t\t\t\tconst cachePath = PLUGIN_CACHE_PATH;\n\t\t\t\t\tconst removeCommand =\n\t\t\t\t\t\tprocess.platform === \"win32\"\n\t\t\t\t\t\t\t? `rmdir /s /q \"${cachePath}\"`\n\t\t\t\t\t\t\t: `rm -rf ${cachePath}`;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\trmSync(cachePath, { recursive: true, force: true });\n\t\t\t\t\t\tclearVersionCache();\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttitle: \"cursor plugin (updated)\",\n\t\t\t\t\t\t\toutput:\n\t\t\t\t\t\t\t\t`Plugin cache cleared (v${local} → v${latest}).\\n` +\n\t\t\t\t\t\t\t\t`Restart opencode to complete the upgrade — it will fetch v${latest} on next launch.`,\n\t\t\t\t\t\t\tmetadata: { local, latest, status: \"updated\" as const },\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttitle: \"cursor plugin (cache clear failed)\",\n\t\t\t\t\t\t\toutput:\n\t\t\t\t\t\t\t\t`Failed to clear plugin cache: ${message}\\n\\n` +\n\t\t\t\t\t\t\t\t`To update manually, exit opencode and run:\\n\\n` +\n\t\t\t\t\t\t\t\t` ${removeCommand}\\n\\n` +\n\t\t\t\t\t\t\t\t`then restart opencode.`,\n\t\t\t\t\t\t\tmetadata: { local, latest, status: \"failed\" as const },\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\tcursor_refresh_models: {\n\t\t\t\tdescription:\n\t\t\t\t\t\"Refresh the live Cursor model catalog now (bypasses the cache) and report the available models. The catalog also auto-refreshes on every opencode startup; use this to pick up new models mid-session. Note: to update the plugin itself (not just the model list), use the cursor_update_plugin tool.\",\n\t\t\t\targs: {},\n\t\t\t\texecute: async () => {\n\t\t\t\t\tconst result = await discoverModels({ forceRefresh: true });\n\t\t\t\t\tconst lines = result.models.map((m) => `- ${m.id} — ${m.displayName}`);\n\t\t\t\t\tconst header =\n\t\t\t\t\t\tresult.source === \"live\"\n\t\t\t\t\t\t\t? `Refreshed ${result.models.length} Cursor models (live):`\n\t\t\t\t\t\t\t: `Could not fetch live models (${result.source}). ${result.warning ?? \"\"}`.trim();\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttitle: `Cursor models (${result.source})`,\n\t\t\t\t\t\toutput: [header, ...lines].join(\"\\n\"),\n\t\t\t\t\t\tmetadata: { source: result.source, count: result.models.length },\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t},\n\t\t\t// Delegation tools that complement the provider: a cloud/background agent\n\t\t\t// and a permission-gated local delegate. They resolve the Cursor key from\n\t\t\t// the auth loader (captured above) or CURSOR_API_KEY.\n\t\t\t...buildCursorTools({\n\t\t\t\tresolveApiKey: () => resolveCursorApiKey(capturedApiKey),\n\t\t\t\tdefaultCwd: () => input?.directory ?? process.cwd(),\n\t\t\t}),\n\t\t},\n\n\t\tdispose: async () => {\n\t\t\t// Best-effort: drop the generated system-prompt rule and skill mirror\n\t\t\t// so they don't linger in the user's workspace / Cursor IDE after the\n\t\t\t// session ends. Uses the same canonical cwd the provider wrote to;\n\t\t\t// sentinel-guarded, so user-owned files are never deleted.\n\t\t\tremoveSystemRule(resolvedCwd);\n\t\t\tremoveSkillMirror(resolvedCwd);\n\t\t\tclearSubagentBridge();\n\t\t\tclearLogBridge();\n\t\t},\n\t};\n};\n\nexport default CursorPlugin;\n","/**\n * GENERATED FILE — do not edit by hand.\n *\n * Generated by `scripts/sync-model-limits.mjs` from Cursor's published docs:\n * context windows https://cursor.com/docs/account/pricing/request-based-legacy.md\n * pricing https://cursor.com/docs/models-and-pricing.md\n *\n * Data last changed: 2026-08-03\n * (a sync that finds no data change leaves this date alone, so it dates the\n * last change to the generated maps — NOT the last time they were verified.\n * Verification runs on a schedule in CI; see the model-data-drift job.)\n * Regenerate: `npm run sync:model-limits`\n *\n * Only MODEL_CONTEXT_LIMITS and MODEL_COST are derived from the docs.\n * MODEL_OUTPUT_LIMITS further down is hand-maintained, because Cursor's docs\n * publish no output-token column — but it is still emitted from this file's\n * template, so edit it in `scripts/sync-model-limits.mjs`, not here. An edit\n * made here is reverted by the next sync.\n *\n * Pricing is read from the structured Input / Cache write / Cache read /\n * Output columns only. The `Notes` cell is deliberately NOT parsed, even\n * though promotions are announced there in prose (Claude Sonnet 5's row\n * advertises \"$2/M input and $10/M output through August 31, 2026\" while its\n * price columns still read $3 / $15). Extracting money from free text is\n * confidently wrong by construction, promo windows expire, and Cursor's own\n * `agent.getUsage()` -> `chargedCents` is the authoritative source for\n * promotions, discounts, the Cursor Token Fee, and Max Mode multipliers. This\n * map is only the rate card opencode multiplies token counts by.\n */\n\n/**\n * Per-model default context window limits (tokens), keyed by model id prefix.\n * The \"Max context\" column (1M for frontier models) requires Max Mode and is\n * NOT used here — the plugin can't detect Max Mode, so the default window is\n * the honest limit to display.\n *\n * Longest prefix wins: `claude-opus-4-8` (300K) beats `claude-opus-4` (200K).\n */\nconst MODEL_CONTEXT_LIMITS: Record<string, number> = {\n \"auto-smart\": 200_000,\n \"claude-fable-5\": 300_000,\n \"claude-haiku-4-5\": 200_000,\n \"claude-opus-4-5\": 200_000,\n \"claude-opus-4-6\": 200_000,\n \"claude-opus-4-7\": 300_000,\n \"claude-opus-4-8\": 300_000,\n \"claude-opus-5\": 300_000,\n \"claude-sonnet-4\": 200_000,\n \"claude-sonnet-4-5\": 200_000,\n \"claude-sonnet-4-6\": 200_000,\n \"claude-sonnet-5\": 200_000,\n \"composer-2\": 200_000,\n \"composer-2.5\": 200_000,\n \"default\": 200_000,\n \"gemini-2.5-flash\": 200_000,\n \"gemini-3-flash\": 200_000,\n \"gemini-3.1-pro\": 200_000,\n \"gemini-3.5-flash\": 200_000,\n \"gemini-3.6-flash\": 200_000,\n \"glm-5.2\": 200_000,\n \"gpt-5-mini\": 272_000,\n \"gpt-5.1\": 272_000,\n \"gpt-5.2\": 272_000,\n \"gpt-5.3-codex\": 272_000,\n \"gpt-5.4\": 272_000,\n \"gpt-5.4-mini\": 272_000,\n \"gpt-5.4-nano\": 272_000,\n \"gpt-5.5\": 272_000,\n \"gpt-5.6-luna\": 272_000,\n \"gpt-5.6-sol\": 272_000,\n \"gpt-5.6-terra\": 272_000,\n \"grok-4.5\": 256_000,\n};\n\nconst DEFAULT_CONTEXT_LIMIT = 200_000;\n\n/**\n * Sentinel `limit.input` that pushes opencode's auto-compaction threshold out\n * of reach, so auto-compaction never fires. opencode computes the threshold as\n * `limit.input ? limit.input - reserved : limit.context - maxOutput`, so a huge\n * `input` makes it unreachable while `limit.context` stays honest — the TUI\n * context gauge keeps working.\n *\n * Why suppress it: the Cursor agent runtime self-compacts on its own context\n * threshold (`@cursor/sdk` `dist/esm/357.js`, `preCompact` hook with\n * `trigger: \"auto\"`), so opencode-driven compaction is redundant. It is also\n * harmful — each opencode compaction rewrites the transcript, which classifies\n * as `divergence` and mints a fresh Cursor agentId, and every distinct agentId\n * permanently adds a guarded SQLite `store.db`/`-wal`/`-shm` triple that\n * `agent.close()` cannot release.\n *\n * This is NOT a real model capability. Verified against the opencode 1.18.11\n * binary by enumerating the call sites of `Is()` (the threshold function) rather\n * than textual hits on `limit.input`, since consumers reach it transitively:\n * - `vl()` — the proactive auto-compaction trigger. Suppressed here.\n * - `Pd()` — preserve-recent-tokens budget, also used by manual\n * `/compact`. Inert: it is `min(8000, max(2000,\n * floor(Is*0.25)))`, which saturates at 8000 for any\n * `Is >= 32000` — true both before and after the sentinel.\n * Everything else that touches `limit.input` is catalog merge/serialization.\n *\n * Also verified end-to-end (isolated HOME, `opencode models cursor --verbose`)\n * that a config-channel `limit.input` survives validation and reaches\n * `Provider.list()` with `limit.context` intact.\n *\n * Caveat: `Is()` is `max(0, input - reserved)`, so a user setting\n * `compaction.reserved >= this value` would drive the threshold to 0 and make\n * compaction fire every turn. Absurd but user-settable.\n */\nexport const NO_AUTO_COMPACTION_INPUT_LIMIT = 1_000_000_000;\n\n/**\n * Resolve a model's context window by longest-prefix match against\n * {@link MODEL_CONTEXT_LIMITS}. Falls back to 200K for unknown models.\n */\nexport function resolveContextLimit(modelId: string): number {\n let best: number | undefined;\n let bestLen = 0;\n for (const [prefix, limit] of Object.entries(MODEL_CONTEXT_LIMITS)) {\n if (modelId.startsWith(prefix) && prefix.length > bestLen) {\n best = limit;\n bestLen = prefix.length;\n }\n }\n return best ?? DEFAULT_CONTEXT_LIMIT;\n}\n\n/**\n * Per-model API pricing (USD per million tokens), keyed by model id prefix.\n * Cursor Models pool models (Grok 4.5, Composer, Auto) have $0 — they draw\n * from the Cursor Models pool, not the Other Models pool, so there is no\n * per-token API charge and they are absent from the pricing docs entirely.\n *\n * Longest prefix wins: `gpt-5.4-mini` (0.75) beats `gpt-5.4` (2.50).\n */\nconst MODEL_COST: Record<string, { input: number; output: number; cacheRead: number; cacheWrite: number }> = {\n \"auto-smart\": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n \"claude-fable-5\": { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 },\n \"claude-haiku-4-5\": { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 },\n \"claude-opus-4-5\": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },\n \"claude-opus-4-6\": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },\n \"claude-opus-4-7\": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },\n \"claude-opus-4-8\": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },\n \"claude-opus-5\": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },\n \"claude-sonnet-4\": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },\n \"claude-sonnet-4-5\": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },\n \"claude-sonnet-4-6\": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },\n \"claude-sonnet-5\": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },\n \"composer-2\": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n \"composer-2.5\": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n \"default\": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n \"gemini-2.5-flash\": { input: 0.3, output: 2.5, cacheRead: 0.03, cacheWrite: 0 },\n \"gemini-3-flash\": { input: 0.5, output: 3, cacheRead: 0.05, cacheWrite: 0 },\n \"gemini-3.1-pro\": { input: 2, output: 12, cacheRead: 0.2, cacheWrite: 0 },\n \"gemini-3.5-flash\": { input: 1.5, output: 9, cacheRead: 0.15, cacheWrite: 0 },\n \"gemini-3.6-flash\": { input: 1.5, output: 7.5, cacheRead: 0.15, cacheWrite: 0 },\n \"glm-5.2\": { input: 1.4, output: 4.4, cacheRead: 0.26, cacheWrite: 0 },\n \"gpt-5-mini\": { input: 0.25, output: 2, cacheRead: 0.025, cacheWrite: 0 },\n \"gpt-5.1\": { input: 1.25, output: 10, cacheRead: 0.125, cacheWrite: 0 },\n \"gpt-5.2\": { input: 1.75, output: 14, cacheRead: 0.175, cacheWrite: 0 },\n \"gpt-5.3-codex\": { input: 1.75, output: 14, cacheRead: 0.175, cacheWrite: 0 },\n \"gpt-5.4\": { input: 2.5, output: 15, cacheRead: 0.25, cacheWrite: 0 },\n \"gpt-5.4-mini\": { input: 0.75, output: 4.5, cacheRead: 0.075, cacheWrite: 0 },\n \"gpt-5.4-nano\": { input: 0.2, output: 1.25, cacheRead: 0.02, cacheWrite: 0 },\n \"gpt-5.5\": { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 0 },\n \"gpt-5.6-luna\": { input: 0.2, output: 1.2, cacheRead: 0.02, cacheWrite: 0.25 },\n \"gpt-5.6-sol\": { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 6.25 },\n \"gpt-5.6-terra\": { input: 2, output: 12, cacheRead: 0.2, cacheWrite: 2.5 },\n \"grok-4.5\": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n};\n\nconst DEFAULT_COST = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };\n\n/**\n * Resolve a model's per-token cost by longest-prefix match against\n * {@link MODEL_COST}. Falls back to $0 for unknown models (treated as\n * subscription/Cursor Models pool).\n */\nexport function resolveCost(modelId: string): {\n input: number;\n output: number;\n cacheRead: number;\n cacheWrite: number;\n} {\n let best: { input: number; output: number; cacheRead: number; cacheWrite: number } | undefined;\n let bestLen = 0;\n for (const [prefix, cost] of Object.entries(MODEL_COST)) {\n if (modelId.startsWith(prefix) && prefix.length > bestLen) {\n best = cost;\n bestLen = prefix.length;\n }\n }\n return best ?? DEFAULT_COST;\n}\n\n/**\n * NOT DERIVED FROM THE DOCS — hand-maintained in the template inside\n * `scripts/sync-model-limits.mjs`. Cursor's docs publish no output-token\n * column, so there is nothing to generate these from. Editing this map here\n * has no lasting effect; the next sync reverts it.\n *\n * Per-model output token limits, keyed by model id prefix. The Cursor SDK\n * doesn't expose output limits, so these are best-known values. 32K default\n * (the previous hardcoded value); 64K for frontier models known to support\n * higher output. Low priority — the TUI doesn't display output limit.\n */\nconst MODEL_OUTPUT_LIMITS: Record<string, number> = {\n \"claude-opus-4-7\": 64_000,\n \"claude-opus-4-8\": 64_000,\n \"claude-opus-5\": 64_000,\n \"claude-fable-5\": 64_000,\n \"gpt-5.5\": 64_000,\n \"gpt-5.6-sol\": 64_000,\n};\n\nconst DEFAULT_OUTPUT_LIMIT = 32_000;\n\n/**\n * Resolve a model's output limit by longest-prefix match. Falls back to 32K.\n */\nexport function resolveOutputLimit(modelId: string): number {\n let best: number | undefined;\n let bestLen = 0;\n for (const [prefix, limit] of Object.entries(MODEL_OUTPUT_LIMITS)) {\n if (modelId.startsWith(prefix) && prefix.length > bestLen) {\n best = limit;\n bestLen = prefix.length;\n }\n }\n return best ?? DEFAULT_OUTPUT_LIMIT;\n}\n","import { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir, tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { ModelListItem } from \"@cursor/sdk\";\n\n/** Default cache lifetime: 24 hours, overridable via env. */\nconst DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;\n\nfunction ttlMs(): number {\n const raw = process.env.OPENCODE_CURSOR_MODEL_CACHE_TTL_MS;\n const parsed = raw ? Number.parseInt(raw, 10) : NaN;\n return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_TTL_MS;\n}\n\nfunction cacheDir(): string {\n const base =\n process.env.XDG_CACHE_HOME?.trim() ||\n (homedir() ? join(homedir(), \".cache\") : tmpdir());\n return join(base, \"opencode-cursor\");\n}\n\nfunction cacheFile(fingerprint: string): string {\n return join(cacheDir(), `models-${fingerprint}.json`);\n}\n\n/**\n * Key-independent \"latest known catalog\" file. The `config` plugin hook runs\n * without access to the stored API key, so it can't read the per-key cache.\n * This file lets a keyless caller (the config hook) seed opencode's model\n * picker with the real catalog that a previous *authed* load discovered.\n */\nfunction latestCacheFile(): string {\n return join(cacheDir(), \"models-latest.json\");\n}\n\n/** The latest-catalog seed is kept longer than the per-key cache: the catalog\n * is stable and this only feeds pre-auth UI seeding. */\nconst LATEST_TTL_MS = 30 * 24 * 60 * 60 * 1000;\n\ninterface CacheEnvelope {\n savedAt: number;\n models: ModelListItem[];\n}\n\nfunction readCacheFile(file: string, maxAgeMs: number): ModelListItem[] | undefined {\n try {\n const parsed = JSON.parse(readFileSync(file, \"utf8\")) as CacheEnvelope;\n if (!parsed?.savedAt || !Array.isArray(parsed.models)) return undefined;\n if (Date.now() - parsed.savedAt > maxAgeMs) return undefined;\n return parsed.models;\n } catch {\n return undefined;\n }\n}\n\nfunction writeCacheFile(file: string, models: ModelListItem[]): void {\n try {\n mkdirSync(cacheDir(), { recursive: true });\n const envelope: CacheEnvelope = { savedAt: Date.now(), models };\n writeFileSync(file, JSON.stringify(envelope), \"utf8\");\n } catch {\n // Caching is an optimization; ignore write failures.\n }\n}\n\n/**\n * Return cached models for the given API-key fingerprint when present and still\n * fresh, otherwise `undefined`. Never throws on a missing/corrupt cache.\n */\nexport function readModelCache(fingerprint: string): ModelListItem[] | undefined {\n return readCacheFile(cacheFile(fingerprint), ttlMs());\n}\n\n/** Persist the discovered model list (per-key cache + key-independent latest\n * catalog). Best-effort; never throws. */\nexport function writeModelCache(fingerprint: string, models: ModelListItem[]): void {\n writeCacheFile(cacheFile(fingerprint), models);\n writeCacheFile(latestCacheFile(), models);\n}\n\n/**\n * Return the most recently discovered catalog regardless of API key, when\n * present and within {@link LATEST_TTL_MS}. Used by the keyless `config` hook to\n * seed the picker with the real catalog after a prior authed load.\n */\nexport function readLatestModelCache(): ModelListItem[] | undefined {\n return readCacheFile(latestCacheFile(), LATEST_TTL_MS);\n}\n","import type { ModelListItem } from \"@cursor/sdk\";\n\n/**\n * A small static snapshot of well-known Cursor models, used only when live\n * discovery is unavailable (no API key, offline, or an SDK error). The live\n * `Cursor.models.list()` result always takes precedence; this just lets the\n * provider appear in opencode with sensible defaults so the user can reach the\n * login flow. Refresh the real catalog with the `cursor_refresh_models` tool.\n */\nexport const FALLBACK_MODELS: ModelListItem[] = [\n {\n id: \"composer-2.5\",\n displayName: \"Composer 2.5\",\n description: \"Cursor's default agent model (fallback entry).\",\n parameters: [\n { id: \"thinking\", displayName: \"Thinking\", values: [{ value: \"off\" }, { value: \"on\" }] },\n ],\n },\n { id: \"claude-opus-4-8\", displayName: \"Claude Opus 4.8 (via Cursor)\" },\n { id: \"claude-sonnet-4-6\", displayName: \"Claude Sonnet 4.6 (via Cursor)\" },\n { id: \"gpt-5.5\", displayName: \"GPT-5.5 (via Cursor)\" },\n];\n","import type { ModelListItem } from \"@cursor/sdk\";\n\n/**\n * A Cursor model \"variant\" as opencode stores it: an options object that, when\n * the variant is selected, is merged into `providerOptions.cursor` and read back\n * by {@link resolveControls}.\n */\nexport interface CursorVariant {\n params?: Record<string, string>;\n mode?: \"agent\" | \"plan\";\n}\n\nconst REASONING_PARAM = /think|reason|effort/i;\nconst BOOLEAN_VALUES = new Set([\"true\", \"false\"]);\n\nfunction paramValues(param: NonNullable<ModelListItem[\"parameters\"]>[number]): string[] {\n return (param.values ?? []).map((v) => v.value);\n}\n\nfunction isBooleanParam(values: string[]): boolean {\n return values.length > 0 && values.every((v) => BOOLEAN_VALUES.has(v));\n}\n\n/**\n * Params opencode must send by DEFAULT for this model — i.e. when the user has\n * NOT picked a variant. Non-reasoning boolean toggles (notably Cursor's `fast`)\n * are pinned OFF here so the provider never silently inherits Cursor's\n * server-side default, which is `fast: true` for several models (composer-*,\n * gpt-*-codex). The user opts back IN via the matching picker variant.\n *\n * Seeded into each model's opencode `options.params` (see `toOpencodeModels` /\n * `buildModelV2Map`); {@link resolveControls} merges it into the request.\n */\nexport function defaultModelParams(item: ModelListItem): Record<string, string> {\n const out: Record<string, string> = {};\n for (const param of item.parameters ?? []) {\n if (REASONING_PARAM.test(param.id)) continue;\n if (isBooleanParam(paramValues(param))) out[param.id] = \"false\";\n }\n return out;\n}\n\n/**\n * Derive opencode model variants from a Cursor model's parameters so the\n * variant picker can expose thinking/reasoning levels plus the `fast` toggle.\n * Each variant's object is exactly what {@link resolveControls} consumes. Plan\n * mode is NOT a variant: opencode's plan agent (Tab) is mapped to Cursor's plan\n * mode by the plugin's `chat.params` hook.\n *\n * Every variant for a fast-capable model carries an explicit `fast` value\n * (reasoning variants pin it OFF via {@link defaultModelParams}; the `fast`\n * variant turns it ON) so a selection never depends on Cursor's server-side\n * default for an omitted param.\n */\n/** Slugify an SDK variant displayName for the opencode variant picker key. */\nfunction variantKey(displayName: string): string {\n return displayName.toLowerCase().replace(/[^a-z0-9]+/g, \"-\").replace(/(^-|-$)/g, \"\") || \"variant\";\n}\n\nexport function buildModelVariants(item: ModelListItem): Record<string, CursorVariant> {\n // Non-reasoning boolean defaults (e.g. { fast: \"false\" }), pinned into every\n // reasoning variant so picking a reasoning level never re-enables fast.\n const defaults = defaultModelParams(item);\n\n const sdkVariants = item.variants ?? [];\n const nonDefault = sdkVariants.filter((v) => v.isDefault !== true);\n // Some models return every variant with the SAME displayName (the model's own\n // name), differing only in params — e.g. grok-4.5 returns six \"Cursor Grok\n // 4.5\" presets that vary only in `effort`/`fast`. Those presets carry no\n // distinguishing label, so keying off the displayName yields meaningless\n // numbered collisions (\"cursor-grok-4-5\", \"cursor-grok-4-5-2\", …) that surface\n // in the picker as bogus \"thinking levels\". Detect that case (multiple presets\n // collapsing to one displayName) and fall through to deriving variants from\n // the model's parameters (effort enum + `fast` toggle) instead. Presets with\n // genuinely distinct labels — even ones that slugify alike (\"Deep Think\" vs\n // \"Deep-Think\") — are still honored via the collision counter below.\n const unlabeledPresets =\n nonDefault.length > 1 &&\n new Set(nonDefault.map((v) => v.displayName)).size === 1;\n if (nonDefault.length > 0 && !unlabeledPresets) {\n // Cursor-authoritative presets win: displayName + isDefault are curated\n // upstream; we only pin the non-reasoning boolean floors underneath.\n const out: Record<string, CursorVariant> = {};\n for (const v of nonDefault) {\n const params: Record<string, string> = { ...defaults };\n for (const p of v.params ?? []) params[p.id] = p.value;\n const key = variantKey(v.displayName);\n let candidate = key;\n for (let n = 2; out[candidate] !== undefined; n++) candidate = `${key}-${n}`;\n out[candidate] = { params };\n }\n return out;\n }\n\n const out: Record<string, CursorVariant> = {};\n\n // Pre-pass: does any reasoning param expose a non-boolean effort enum (e.g.\n // [\"low\",\"medium\",\"high\",\"xhigh\",\"max\"])? When it does, a coexisting boolean\n // reasoning toggle (Cursor's `thinking=[\"false\",\"true\"]` on claude-* models)\n // is redundant — selecting any effort level already enables reasoning — and\n // surfacing it would add a stray `thinking` variant the standard opencode\n // providers don't show. Suppress the boolean variant for parity. Order-\n // independent: the enum may be declared before or after the boolean.\n const hasEffortEnum = (item.parameters ?? []).some(\n (p) => REASONING_PARAM.test(p.id) && !isBooleanParam(paramValues(p)) && paramValues(p).length > 0,\n );\n\n for (const param of item.parameters ?? []) {\n const values = paramValues(param);\n if (values.length === 0) continue;\n const boolean = isBooleanParam(values);\n\n if (REASONING_PARAM.test(param.id)) {\n if (boolean) {\n // Boolean toggle (e.g. thinking=[\"false\",\"true\"]). Literal true/false\n // variant names are meaningless in the picker — surface a single\n // variant named after the param that switches it on. \"Off\" is the\n // model's default (no variant selected). Skipped entirely when an\n // effort enum coexists (see hasEffortEnum above).\n if (!hasEffortEnum && values.includes(\"true\")) {\n out[param.id.toLowerCase()] = { params: { ...defaults, [param.id]: \"true\" } };\n }\n continue;\n }\n\n for (const value of values) {\n // `none` means reasoning OFF — the model's default when no variant is\n // selected. Surfacing it as a selectable variant is meaningless (you\n // get it by picking nothing), so skip it. Standard providers\n // (models.dev) include `none` in their effort values, but the\n // no-variant-selected state already represents it.\n if (value === \"none\") continue;\n // Cursor labels the top reasoning tier \"extra-high\"; the opencode\n // standard (models.dev) calls it \"xhigh\". Use the standard label for\n // the variant key so the cycler is consistent across providers, but\n // keep the Cursor wire-format value (\"extra-high\") in the params sent\n // to the API.\n const displayKey = value === \"extra-high\" ? \"xhigh\" : value;\n const key = out[displayKey] === undefined ? displayKey : `${param.id}-${displayKey}`;\n out[key] = { params: { ...defaults, [param.id]: value } };\n }\n continue;\n }\n\n // Non-reasoning boolean toggle (e.g. Cursor's `fast`). Default is OFF (see\n // defaultModelParams); expose a single opt-in variant that turns it ON.\n if (boolean && values.includes(\"true\")) {\n out[param.id.toLowerCase()] = { params: { ...defaults, [param.id]: \"true\" } };\n }\n // Non-reasoning enum params (e.g. `context`) remain unsupported in the picker.\n }\n\n return out;\n}\n","import type { ModelListItem } from \"@cursor/sdk\";\nimport type { Config } from \"@opencode-ai/plugin\";\nimport { fingerprintApiKey, resolveCursorApiKey } from \"./api-key.js\";\nimport {\n NO_AUTO_COMPACTION_INPUT_LIMIT,\n resolveContextLimit,\n resolveCost,\n resolveOutputLimit,\n} from \"./model-limits.js\";\nimport { readLatestModelCache, readModelCache, writeModelCache } from \"./model-cache.js\";\nimport { FALLBACK_MODELS } from \"./fallback-models.js\";\nimport { loadCursorSdk } from \"./cursor-runtime.js\";\nimport { buildModelVariants, defaultModelParams, type CursorVariant } from \"./model-variants.js\";\n\nexport type ModelSource = \"live\" | \"cache\" | \"fallback\";\n\nexport interface DiscoveryResult {\n models: ModelListItem[];\n source: ModelSource;\n /** Human-readable note when discovery degraded (e.g. missing key, error). */\n warning?: string;\n}\n\nexport interface DiscoverOptions {\n /** Explicit key; falls back to CURSOR_API_KEY. */\n apiKey?: string;\n /** Bypass the on-disk cache and force a live `Cursor.models.list()`. */\n forceRefresh?: boolean;\n}\n\n/**\n * Discover the Cursor model catalog. Tries (in order): on-disk cache (unless\n * forced), live `Cursor.models.list()`, then the static fallback snapshot.\n * Always resolves — failures degrade to the fallback with a `warning`.\n */\nexport async function discoverModels(options: DiscoverOptions = {}): Promise<DiscoveryResult> {\n const apiKey = resolveCursorApiKey(options.apiKey);\n if (!apiKey) {\n // No key here (e.g. the keyless `config` hook). Prefer the real catalog a\n // prior authed load cached, so opencode's picker shows the full list rather\n // than only the static snapshot.\n const latest = readLatestModelCache();\n if (latest && latest.length > 0) return { models: latest, source: \"cache\" };\n return {\n models: FALLBACK_MODELS,\n source: \"fallback\",\n warning:\n \"No Cursor API key found. Run `opencode auth login` and choose Cursor, or set CURSOR_API_KEY. Showing fallback models.\",\n };\n }\n\n const fingerprint = fingerprintApiKey(apiKey);\n\n if (!options.forceRefresh) {\n const cached = readModelCache(fingerprint);\n if (cached && cached.length > 0) {\n return { models: cached, source: \"cache\" };\n }\n }\n\n try {\n const { Cursor } = await loadCursorSdk();\n const models = await Cursor.models.list({ apiKey });\n if (models.length > 0) {\n writeModelCache(fingerprint, models);\n return { models, source: \"live\" };\n }\n return {\n models: FALLBACK_MODELS,\n source: \"fallback\",\n warning: \"Cursor.models.list() returned no models; showing fallback models.\",\n };\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n // A stale cache is better than nothing on a transient failure.\n const stale = readModelCache(fingerprint);\n if (stale && stale.length > 0) {\n return { models: stale, source: \"cache\", warning: `Live discovery failed (${detail}); using cached models.` };\n }\n return {\n models: FALLBACK_MODELS,\n source: \"fallback\",\n warning: `Live discovery failed (${detail}); showing fallback models.`,\n };\n }\n}\n\n/** True when a model exposes a thinking/reasoning parameter. */\nexport function modelSupportsReasoning(item: ModelListItem): boolean {\n return (item.parameters ?? []).some((p) => /think|reason/i.test(p.id));\n}\n\n/** Shape of a single entry in opencode's `provider.<id>.models` config map. */\nexport interface OpencodeModelConfigEntry {\n id: string;\n name: string;\n attachment: boolean;\n reasoning: boolean;\n temperature: boolean;\n tool_call: boolean;\n /**\n * opencode model variants (thinking levels + plan mode). They MUST be seeded\n * here: opencode discards the plugin `provider.models()` hook for providers\n * absent from its models.dev catalog, so this config map is the only channel\n * through which cursor model variants reach the picker.\n */\n variants: Record<string, CursorVariant>;\n /**\n * Default `providerOptions.cursor` for the model, merged into every request\n * unless a variant overrides it. Carries the non-reasoning boolean defaults\n * (e.g. `{ params: { fast: \"false\" } }`) so the provider never silently runs\n * Cursor's server-side `fast` default. See {@link defaultModelParams}.\n */\n options: { params?: Record<string, string> };\n /**\n * Per-model context/output window. opencode's config channel is the only\n * one that reaches the model registry for providers absent from\n * models.dev, so the TUI session header's context-window percentage\n * depends on this being present. `context` and `output` are required by\n * the schema.\n *\n * `input` is an undocumented-but-runtime-honored field used only as\n * opencode's auto-compaction threshold. We emit\n * {@link NO_AUTO_COMPACTION_INPUT_LIMIT} to suppress auto-compaction while\n * keeping `context` honest so the TUI gauge still works. The published\n * `@opencode-ai/sdk` config types omit it, so it is excluded from\n * `_limitKeyGuard` below.\n */\n limit: { context: number; input?: number; output: number };\n /**\n * Per-model API pricing, USD per million tokens. Note the FLAT snake_case\n * cache keys — the config schema (`ProviderConfig` in\n * `@opencode-ai/sdk`) uses `cache_read`/`cache_write`, unlike the\n * `ModelV2` shape's nested `cache: { read, write }`.\n */\n cost: { input: number; output: number; cache_read: number; cache_write: number };\n}\n\n/**\n * Compile-time guard: the entries we write into\n * `config.provider.cursor.models` must satisfy the shape opencode's config\n * schema accepts. If opencode changes the schema (or we drift, e.g. by\n * using `cache: { read, write }` instead of `cache_read`/`cache_write`),\n * `npm run typecheck` fails here rather than silently producing a config\n * opencode discards.\n */\ntype AcceptedModelConfig = NonNullable<\n NonNullable<NonNullable<Config[\"provider\"]>[string]>[\"models\"]\n>[string];\nconst _entryShapeGuard: AcceptedModelConfig = {} as OpencodeModelConfigEntry;\nvoid _entryShapeGuard;\n\n/**\n * Assignability alone is too weak for `cost`/`limit`. Excess-property checking\n * only applies to fresh object literals, and the schema's cache keys are\n * optional — so a drifted `cost: { input, output, cache: { read, write } }`\n * assigns cleanly to the accepted shape (verified: it typechecks) while\n * opencode would read `cache_read`/`cache_write` as absent. These guards\n * assert every key we emit is a key the schema actually declares.\n *\n * `never` means \"no excess keys\"; anything else collapses `_KeysAccepted` to\n * `never` and the `true` initializer below fails to compile.\n */\ntype _KeysAccepted<Ours, Accepted> = Exclude<keyof Ours, keyof Accepted> extends never ? true : never;\nconst _costKeyGuard: _KeysAccepted<\n OpencodeModelConfigEntry[\"cost\"],\n NonNullable<AcceptedModelConfig[\"cost\"]>\n> = true;\nvoid _costKeyGuard;\n// `input` is deliberately excluded: opencode's runtime reads it (verified in\n// the 1.18.11 binary and end-to-end via `Provider.list()`), but the published\n// config types don't declare it. The guard still protects `context`/`output`.\nconst _limitKeyGuard: _KeysAccepted<\n Omit<OpencodeModelConfigEntry[\"limit\"], \"input\">,\n NonNullable<AcceptedModelConfig[\"limit\"]>\n> = true;\nvoid _limitKeyGuard;\n\n/**\n * Map discovered Cursor models to opencode's provider config `models` map. The\n * Cursor SDK runs an agent (it calls tools itself), so every model is marked\n * `tool_call: true` and `temperature: false`.\n */\nexport function toOpencodeModels(\n items: ModelListItem[],\n opts: { autoCompaction?: boolean } = {},\n): Record<string, OpencodeModelConfigEntry> {\n const out: Record<string, OpencodeModelConfigEntry> = {};\n for (const item of items) {\n const params = defaultModelParams(item);\n const cost = resolveCost(item.id);\n out[item.id] = {\n id: item.id,\n name: item.displayName || item.id,\n attachment: true,\n reasoning: modelSupportsReasoning(item),\n temperature: false,\n tool_call: true,\n variants: buildModelVariants(item),\n options: Object.keys(params).length > 0 ? { params } : {},\n limit: {\n context: resolveContextLimit(item.id),\n // Suppress opencode's auto-compaction unless the user opts in: the\n // Cursor agent self-compacts, and opencode's compaction mints a\n // fresh agentId per cycle, permanently leaking guarded SQLite fds.\n ...(opts.autoCompaction\n ? {}\n : { input: NO_AUTO_COMPACTION_INPUT_LIMIT }),\n output: resolveOutputLimit(item.id),\n },\n cost: {\n input: cost.input,\n output: cost.output,\n cache_read: cost.cacheRead,\n cache_write: cost.cacheWrite,\n },\n };\n }\n return out;\n}\n","import type { Model as ModelV2 } from \"@opencode-ai/sdk/v2\";\nimport type { ModelListItem } from \"@cursor/sdk\";\nimport { modelSupportsReasoning } from \"../model-discovery.js\";\nimport {\n NO_AUTO_COMPACTION_INPUT_LIMIT,\n resolveContextLimit,\n resolveCost,\n resolveOutputLimit,\n} from \"../model-limits.js\";\nimport { buildModelVariants, defaultModelParams } from \"../model-variants.js\";\n\nexport const PROVIDER_ID = \"cursor\";\nexport const NPM_PACKAGE = \"@stablekernel/opencode-cursor\";\n\n/**\n * The npm specifier opencode uses to load the provider SDK. Defaults to the\n * published package name; can be overridden with a `file://...` URL (which\n * opencode imports directly, skipping a registry install) via\n * `OPENCODE_CURSOR_PROVIDER_NPM` — useful for local development and CI before\n * the package is published.\n */\nexport function providerNpm(): string {\n return process.env.OPENCODE_CURSOR_PROVIDER_NPM?.trim() || NPM_PACKAGE;\n}\n\n/**\n * Build opencode's rich runtime `Model` objects from discovered Cursor models.\n * Used by the auth-aware `provider.models()` hook. Cost and context/output\n * limits are resolved per model from the shared maps in `../model-limits.js`,\n * falling back to $0 / 200K context / 32K output for models absent from them.\n */\nexport function buildModelV2Map(\n items: ModelListItem[],\n opts: { autoCompaction?: boolean } = {},\n): Record<string, ModelV2> {\n const out: Record<string, ModelV2> = {};\n for (const item of items) {\n const params = defaultModelParams(item);\n out[item.id] = {\n id: item.id,\n providerID: PROVIDER_ID,\n api: { id: item.id, url: \"\", npm: providerNpm() },\n name: item.displayName || item.id,\n capabilities: {\n temperature: false,\n reasoning: modelSupportsReasoning(item),\n attachment: true,\n toolcall: true,\n input: { text: true, audio: false, image: true, video: false, pdf: false },\n output: { text: true, audio: false, image: false, video: false, pdf: false },\n interleaved: false,\n },\n cost: (() => {\n const c = resolveCost(item.id);\n return { input: c.input, output: c.output, cache: { read: c.cacheRead, write: c.cacheWrite } };\n })(),\n limit: {\n context: resolveContextLimit(item.id),\n ...(opts.autoCompaction\n ? {}\n : { input: NO_AUTO_COMPACTION_INPUT_LIMIT }),\n output: resolveOutputLimit(item.id),\n },\n status: \"active\",\n options: Object.keys(params).length > 0 ? { params } : {},\n headers: {},\n release_date: \"\",\n variants: buildModelVariants(item) as ModelV2[\"variants\"],\n };\n }\n return out;\n}\n","import type { Config } from \"@opencode-ai/plugin\";\nimport type { McpServerConfig } from \"@cursor/sdk\";\n\n/** The value type of opencode's `config.mcp` map. */\ntype OpencodeMcp = NonNullable<Config[\"mcp\"]>;\ntype OpencodeMcpEntry = OpencodeMcp[string];\n\n/**\n * Live MCP server status, keyed by server name, as reported by opencode's\n * `client.mcp.status()`. Only the `status` field is consumed; `\"connected\"`\n * means the server is currently usable. Mirrors the SDK's `McpStatus` union\n * without importing it (keeps this module dependency-light).\n */\nexport type McpStatusMap = Record<string, { status?: string } | undefined>;\n\n/** opencode runtime statuses that mean a server still needs OAuth to connect. */\nconst NEEDS_AUTH_STATUS = new Set([\"needs_auth\", \"needs_client_registration\"]);\n\n/** The OAuth client registration on a remote entry, or undefined when none. */\nfunction oauthConfig(\n\tentry: OpencodeMcpEntry,\n): { clientId?: string; clientSecret?: string; scope?: string } | undefined {\n\tif (entry.type !== \"remote\") return undefined;\n\t// `oauth` is `McpOAuthConfig | false | undefined`; both false and undefined\n\t// are falsy, so a truthy value is the client-registration object.\n\treturn entry.oauth ? entry.oauth : undefined;\n}\n\n/**\n * Map opencode's OAuth client registration to the Cursor SDK's `auth` block so\n * the Cursor agent can run its own OAuth flow. Returns undefined when there is\n * no `clientId` to share (e.g. RFC 7591 dynamic registration) — opencode's\n * access token itself never reaches `config.mcp`, so a bare URL would fail.\n */\nfunction toCursorAuth(\n\toauth:\n\t\t| { clientId?: string; clientSecret?: string; scope?: string }\n\t\t| undefined,\n):\n\t| { CLIENT_ID: string; CLIENT_SECRET?: string; scopes?: string[] }\n\t| undefined {\n\tif (!oauth?.clientId) return undefined;\n\tconst scopes = oauth.scope?.split(/\\s+/).filter(Boolean);\n\treturn {\n\t\tCLIENT_ID: oauth.clientId,\n\t\t...(oauth.clientSecret ? { CLIENT_SECRET: oauth.clientSecret } : {}),\n\t\t...(scopes && scopes.length > 0 ? { scopes } : {}),\n\t};\n}\n\n/**\n * Names of remote servers that require OAuth but cannot be forwarded to the\n * Cursor agent because no shareable client registration exists (dynamic\n * registration, or a `needs_auth` runtime status with no configured\n * `clientId`). The plugin surfaces these to the user instead of silently\n * forwarding a spec that would 401.\n */\nexport function findUnshareableOAuthServers(\n\tmcp: Config[\"mcp\"],\n\tstatus?: McpStatusMap,\n): string[] {\n\tconst names: string[] = [];\n\tif (!mcp) return names;\n\tfor (const [name, entry] of Object.entries(mcp) as Array<\n\t\t[string, OpencodeMcpEntry]\n\t>) {\n\t\tif (!entry || entry.type !== \"remote\") continue;\n\t\tif (!status && entry.enabled === false) continue;\n\t\tconst s = status?.[name]?.status;\n\t\tif (status && s !== \"connected\" && !NEEDS_AUTH_STATUS.has(s ?? \"\"))\n\t\t\tcontinue;\n\t\tconst oauth = oauthConfig(entry);\n\t\tconst needsOAuth = Boolean(oauth) || NEEDS_AUTH_STATUS.has(s ?? \"\");\n\t\tif (needsOAuth && !toCursorAuth(oauth)) names.push(name);\n\t}\n\treturn names;\n}\n\n/**\n * Translate opencode's configured MCP servers (`config.mcp`) into the Cursor\n * SDK's `McpServerConfig` shape so the same servers can be handed\n * to the Cursor agent via `Agent.create({ mcpServers })`.\n *\n * MCP servers are independent processes addressed by a launch spec, so opencode\n * and the Cursor agent can each connect to the same server. Disabled entries\n * (`enabled: false`) are skipped. The `timeout` field is dropped (no Cursor\n * equivalent). OAuth is mapped where possible: a remote server's `oauth` client\n * registration becomes Cursor's `auth` block so the agent runs its own OAuth\n * flow; servers needing OAuth with no shareable `clientId` are skipped (the\n * plugin reports them via {@link findUnshareableOAuthServers}).\n */\nexport function translateMcpServers(\n\tmcp: Config[\"mcp\"],\n\tstatus?: McpStatusMap,\n): Record<string, McpServerConfig> {\n\tconst out: Record<string, McpServerConfig> = {};\n\tif (!mcp) return out;\n\n\tfor (const [name, entry] of Object.entries(mcp) as Array<\n\t\t[string, OpencodeMcpEntry]\n\t>) {\n\t\tif (!entry) continue;\n\n\t\t// When a live status map is supplied (per-turn dynamic forwarding), it is\n\t\t// the source of truth: forward only servers opencode has currently\n\t\t// connected, so mid-session enable/disable propagates to the Cursor agent.\n\t\t// Without it (the startup config snapshot), fall back to the static\n\t\t// `enabled` flag.\n\t\tif (status) {\n\t\t\tif (status[name]?.status !== \"connected\") continue;\n\t\t} else if (entry.enabled === false) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.type === \"local\") {\n\t\t\tconst [command, ...args] = entry.command ?? [];\n\t\t\tif (!command) continue;\n\t\t\tout[name] = {\n\t\t\t\ttype: \"stdio\",\n\t\t\t\tcommand,\n\t\t\t\t...(args.length > 0 ? { args } : {}),\n\t\t\t\t...(entry.environment && Object.keys(entry.environment).length > 0\n\t\t\t\t\t? { env: entry.environment }\n\t\t\t\t\t: {}),\n\t\t\t};\n\t\t} else if (entry.type === \"remote\") {\n\t\t\tif (!entry.url) continue;\n\t\t\tconst oauth = oauthConfig(entry);\n\t\t\tconst auth = toCursorAuth(oauth);\n\t\t\t// OAuth server with no shareable client registration: opencode holds the\n\t\t\t// token and it never lands in config.mcp, so skip rather than forward a\n\t\t\t// bare URL that would 401. The plugin notifies the user (see\n\t\t\t// findUnshareableOAuthServers).\n\t\t\tif (oauth && !auth) continue;\n\t\t\tout[name] = {\n\t\t\t\ttype: \"http\",\n\t\t\t\turl: entry.url,\n\t\t\t\t...(entry.headers && Object.keys(entry.headers).length > 0\n\t\t\t\t\t? { headers: entry.headers }\n\t\t\t\t\t: {}),\n\t\t\t\t...(auth ? { auth } : {}),\n\t\t\t};\n\t\t}\n\t}\n\n\treturn out;\n}\n","import { tool, type ToolContext, type ToolDefinition } from \"@opencode-ai/plugin\";\nimport { runCloudAgent } from \"../provider/cloud-agent.js\";\nimport { runDelegate } from \"../provider/delegate.js\";\nimport { linkDelegateSession } from \"../provider/subagent-bridge.js\";\n\nconst s = tool.schema;\n\nexport interface CursorToolDeps {\n /**\n * Resolve the Cursor API key (from opencode auth, captured by the plugin's\n * auth loader, or the CURSOR_API_KEY env var). Returns undefined when no key\n * is available so the tool can return a clear \"needs auth\" message.\n */\n resolveApiKey: () => string | undefined;\n /** Default working directory for local delegation (the session worktree/cwd). */\n defaultCwd: () => string;\n}\n\nconst NEEDS_AUTH =\n \"No Cursor API key available. Run `opencode auth login` and choose Cursor, or set CURSOR_API_KEY.\";\n\n/**\n * Request approval for a sensitive Cursor invocation. `context.ask` is the\n * opencode mechanism a custom tool uses to gate itself; it honors the user's\n * `permission` config (allow resolves silently, ask prompts, deny rejects).\n *\n * Returns `{ ok: true }` when approved, or `{ ok: false, reason }` when the\n * request was rejected. We deliberately do not claim the rejection was a policy\n * \"deny\" — `context.ask` rejects on both an explicit deny and an internal\n * failure, and conflating them produces misleading messages. The gate is\n * fail-closed: any rejection (including a host that doesn't provide `ask`)\n * blocks the call rather than silently allowing it.\n */\nasync function requestApproval(\n context: ToolContext,\n permission: string,\n patterns: string[],\n metadata: Record<string, unknown>,\n): Promise<{ ok: boolean; reason?: string }> {\n try {\n await context.ask({ permission, patterns, always: patterns, metadata });\n return { ok: true };\n } catch (err) {\n return { ok: false, reason: err instanceof Error ? err.message : String(err) };\n }\n}\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Build the Cursor delegation tools that complement the native provider:\n * - `cursor_cloud_agent`: run a background agent on a remote repo (optionally\n * opening a PR) — work that maps poorly onto the synchronous provider path.\n * - `cursor_delegate`: run a single local Cursor turn as a permission-gated,\n * auditable tool call (for users who want Cursor as a delegate rather than\n * as their primary model).\n *\n * Both are gated via `context.ask`, so a user `permission` policy controls them.\n */\nexport function buildCursorTools(deps: CursorToolDeps): Record<string, ToolDefinition> {\n return {\n cursor_cloud_agent: tool({\n description:\n \"Launch a Cursor background ('cloud') agent on a remote repository. Runs autonomously \" +\n \"(may take minutes) and can open a pull request. Returns the cloud agent id, final \" +\n \"status, result, and PR url when available.\",\n args: {\n prompt: s.string().describe(\"The task/instruction for the background agent.\"),\n repoUrl: s\n .string()\n .describe(\"Target repository URL, e.g. https://github.com/owner/repo.\"),\n startingRef: s\n .string()\n .optional()\n .describe(\"Branch or ref to start from (defaults to the repo default branch).\"),\n model: s.string().optional().describe(\"Cursor model id (optional for cloud).\"),\n mode: s.enum([\"agent\", \"plan\"]).optional().describe(\"Conversation mode.\"),\n thinking: s.string().optional().describe(\"Thinking level, e.g. 'high'.\"),\n autoCreatePR: s\n .boolean()\n .optional()\n .describe(\"Open a pull request automatically when finished.\"),\n workOnCurrentBranch: s\n .boolean()\n .optional()\n .describe(\"Operate on the current branch instead of creating a new one.\"),\n },\n execute: async (args, context) => {\n const apiKey = deps.resolveApiKey();\n if (!apiKey) return NEEDS_AUTH;\n\n const approval = await requestApproval(\n context,\n \"cursor_cloud_agent\",\n [args.repoUrl],\n { repoUrl: args.repoUrl, autoCreatePR: args.autoCreatePR ?? false },\n );\n if (!approval.ok) {\n return `Cloud agent not approved for ${args.repoUrl}${approval.reason ? `: ${approval.reason}` : \".\"}`;\n }\n\n let result;\n try {\n result = await runCloudAgent({\n apiKey,\n prompt: args.prompt,\n repoUrl: args.repoUrl,\n ...(args.startingRef ? { startingRef: args.startingRef } : {}),\n ...(args.model ? { model: args.model } : {}),\n ...(args.mode ? { mode: args.mode } : {}),\n ...(args.thinking ? { thinking: args.thinking } : {}),\n ...(args.autoCreatePR !== undefined ? { autoCreatePR: args.autoCreatePR } : {}),\n ...(args.workOnCurrentBranch !== undefined\n ? { workOnCurrentBranch: args.workOnCurrentBranch }\n : {}),\n abortSignal: context.abort,\n });\n } catch (err) {\n return `Cloud agent failed: ${errorMessage(err)}`;\n }\n\n const lines = [\n `Cloud agent ${result.agentId} — ${result.status}`,\n ...(result.prUrl ? [`PR: ${result.prUrl}`] : []),\n ...(result.branches.length > 0\n ? [`Branches: ${result.branches.map((b) => b.branch ?? b.repoUrl).join(\", \")}`]\n : []),\n ...(result.result ? [\"\", result.result] : []),\n ...(result.progress.length > 0 ? [\"\", \"Progress:\", ...result.progress] : []),\n ];\n\n return {\n title: `Cursor cloud agent (${result.status})`,\n output: lines.join(\"\\n\"),\n metadata: {\n agentId: result.agentId,\n status: result.status,\n prUrl: result.prUrl ?? null,\n durationMs: result.durationMs ?? null,\n },\n };\n },\n }),\n\n cursor_delegate: tool({\n description:\n \"Delegate a single subtask to a local Cursor agent and return its result. Use to hand \" +\n \"off discrete work to Cursor while keeping your primary model in control. Permission-gated.\",\n args: {\n prompt: s.string().describe(\"The subtask to delegate to Cursor.\"),\n model: s.string().describe(\"Cursor model id to run the delegation on.\"),\n mode: s.enum([\"agent\", \"plan\"]).optional().describe(\"Conversation mode.\"),\n thinking: s.string().optional().describe(\"Thinking level, e.g. 'high'.\"),\n cwd: s\n .string()\n .optional()\n .describe(\"Working directory (defaults to the session directory).\"),\n additionalCwds: s\n .array(s.string())\n .optional()\n .describe(\"Extra workspace roots; combined with cwd into a multi-root agent workspace.\"),\n sandbox: s.boolean().optional().describe(\"Run the agent's tools in Cursor's sandbox.\"),\n agentId: s\n .string()\n .optional()\n .describe(\"Resume a specific Cursor agent id instead of starting fresh.\"),\n },\n execute: async (args, context) => {\n const apiKey = deps.resolveApiKey();\n if (!apiKey) return NEEDS_AUTH;\n\n const approval = await requestApproval(context, \"cursor_delegate\", [args.model], {\n model: args.model,\n prompt: args.prompt,\n });\n if (!approval.ok) {\n return `Delegation to ${args.model} not approved${approval.reason ? `: ${approval.reason}` : \".\"}`;\n }\n\n let result;\n try {\n const baseCwd = args.cwd ?? context.directory ?? deps.defaultCwd();\n result = await runDelegate({\n apiKey,\n prompt: args.prompt,\n model: args.model,\n cwd: args.additionalCwds?.length ? [baseCwd, ...args.additionalCwds] : baseCwd,\n // Honour the user's project settings layer so delegated turns pick\n // up `.cursor/skills/` from the delegate's cwd. The delegate defaults\n // to [\"project\"] on its own, so this is only needed when the user\n // explicitly configured settingSources on the provider.\n settingSources: [\"project\"],\n ...(args.mode ? { mode: args.mode } : {}),\n ...(args.thinking ? { thinking: args.thinking } : {}),\n ...(args.sandbox !== undefined ? { sandbox: args.sandbox } : {}),\n ...(args.agentId ? { agentId: args.agentId } : {}),\n abortSignal: context.abort,\n });\n } catch (err) {\n return `Delegation failed: ${errorMessage(err)}`;\n }\n\n const toolNote =\n result.toolActivity.length > 0\n ? `\\n\\n(${result.toolActivity.length} tool call(s)` +\n `${result.toolActivity.some((t) => t.isError) ? \", some failed\" : \"\"})`\n : \"\";\n\n // Surface the delegate's work in a child session so it's discoverable\n // in the TUI's subagent panel. Best-effort: a failed link never breaks\n // the turn. The result card itself stays a tool block (a custom tool\n // can't render a navigable `task` part), so the child session is\n // reached via the subagent panel, not by clicking the result.\n if (context.sessionID) {\n const transcript = [\n result.text || \"(no text output)\",\n ...(result.reasoning ? [`\\n> ${result.reasoning}`] : []),\n ...(result.toolActivity.length > 0\n ? [`\\n(${result.toolActivity.length} tool call(s))`]\n : []),\n ].join(\"\\n\");\n await linkDelegateSession({\n parentSessionID: context.sessionID,\n title: `Cursor delegate (${args.model})`,\n prompt: args.prompt,\n transcript,\n });\n }\n\n return {\n title: `Cursor delegate (${args.model})`,\n output: (result.text || \"(no text output)\") + toolNote,\n metadata: {\n agentId: result.agentId,\n model: args.model,\n toolCalls: result.toolActivity.length,\n usage: result.usage ?? null,\n },\n };\n },\n }),\n };\n}\n","import type { AgentModeOption, ConversationStep, InteractionUpdate } from \"@cursor/sdk\";\nimport { loadCursorSdk } from \"../cursor-runtime.js\";\nimport { buildModelSelection } from \"./controls.js\";\n\n/**\n * A target repository for a cloud agent. Cursor's cloud runtime accepts an\n * array of repos; the tool surface exposes the common single-repo case.\n */\nexport interface CloudRepoTarget {\n url: string;\n startingRef?: string;\n}\n\nexport interface CloudAgentParams {\n apiKey: string;\n /** The instruction/task for the background agent. */\n prompt: string;\n /** Target repository URL (e.g. https://github.com/owner/repo). */\n repoUrl: string;\n /** Branch/ref to start from. Defaults to the repo's default branch. */\n startingRef?: string;\n /** Cursor model id. Optional for cloud (server picks a default otherwise). */\n model?: string;\n /** Conversation mode; defaults to \"agent\". */\n mode?: AgentModeOption;\n /** Convenience for the Cursor `thinking` model param (e.g. \"high\"). */\n thinking?: string;\n /** When true, open a PR automatically once the agent finishes. */\n autoCreatePR?: boolean;\n /** Operate on the current branch instead of creating a new one. */\n workOnCurrentBranch?: boolean;\n /** Cancels the run when aborted (wired to the tool's abort signal). */\n abortSignal?: AbortSignal;\n}\n\nexport interface CloudAgentBranch {\n repoUrl: string;\n branch?: string;\n prUrl?: string;\n}\n\nexport interface CloudAgentResult {\n agentId: string;\n /** Terminal run status: \"finished\" | \"error\" | \"cancelled\". */\n status: string;\n /** The agent's final textual result, when present. */\n result?: string;\n /** First PR url found across result branches (when `autoCreatePR`). */\n prUrl?: string;\n /** Per-repo branch/PR info reported by the run. */\n branches: CloudAgentBranch[];\n durationMs?: number;\n /** Human-readable progress lines captured from status/step/summary updates. */\n progress: string[];\n}\n\n/**\n * Run a Cursor background (\"cloud\") agent against a remote repository and wait\n * for it to finish, returning the final status, result text, and any PR url.\n *\n * A cloud agent can run for minutes and produce a PR rather than a chat reply,\n * which maps poorly onto the synchronous provider `doStream` path — so this is\n * exposed as an opencode tool instead (see plugin/index.ts). Progress is\n * collected into `progress[]` (opencode custom tools return a single result\n * rather than a live stream) and the lifecycle is bridged through the same\n * `loadCursorSdk` plumbing the provider uses.\n */\nexport async function runCloudAgent(params: CloudAgentParams): Promise<CloudAgentResult> {\n const { Agent } = await loadCursorSdk();\n const modelSelection = params.model\n ? buildModelSelection(params.model, params.thinking ? { thinking: params.thinking } : undefined)\n : undefined;\n const mode: AgentModeOption = params.mode ?? \"agent\";\n\n const createOptions = {\n apiKey: params.apiKey,\n ...(modelSelection ? { model: modelSelection } : {}),\n mode,\n cloud: {\n repos: [\n {\n url: params.repoUrl,\n ...(params.startingRef ? { startingRef: params.startingRef } : {}),\n },\n ],\n ...(params.autoCreatePR !== undefined ? { autoCreatePR: params.autoCreatePR } : {}),\n ...(params.workOnCurrentBranch !== undefined\n ? { workOnCurrentBranch: params.workOnCurrentBranch }\n : {}),\n },\n };\n\n const progress: string[] = [];\n const agent = await Agent.create(createOptions);\n\n // `onDelta` carries fine-grained updates; for a cloud (background) run the\n // higher-signal progress arrives via `onStep` (whole conversation steps) and\n // `run.onDidChangeStatus`. We capture all three — whichever the runtime emits.\n const onDelta = ({ update }: { update: InteractionUpdate }) => {\n if (update.type === \"summary\") progress.push(`summary: ${update.summary}`);\n };\n\n const onStep = ({ step }: { step: ConversationStep }) => {\n progress.push(`step: ${describeStep(step)}`);\n };\n\n try {\n const run = await agent.send(params.prompt, { mode, onDelta, onStep });\n\n const off = run.onDidChangeStatus?.((status: string) => {\n progress.push(`status: ${status}`);\n });\n const onAbort = () => {\n run.cancel().catch(() => {});\n };\n params.abortSignal?.addEventListener(\"abort\", onAbort);\n\n try {\n const result = await run.wait();\n const branches: CloudAgentBranch[] = (result.git?.branches ?? []).map((b) => ({\n repoUrl: b.repoUrl,\n ...(b.branch ? { branch: b.branch } : {}),\n ...(b.prUrl ? { prUrl: b.prUrl } : {}),\n }));\n const prUrl = branches.find((b) => b.prUrl)?.prUrl;\n return {\n agentId: agent.agentId,\n status: result.status,\n ...(result.result !== undefined ? { result: result.result } : {}),\n ...(prUrl ? { prUrl } : {}),\n branches,\n ...(result.durationMs !== undefined ? { durationMs: result.durationMs } : {}),\n progress,\n };\n } finally {\n off?.();\n params.abortSignal?.removeEventListener(\"abort\", onAbort);\n }\n } finally {\n try {\n agent.close();\n } catch {\n // best effort; cloud agents persist server-side regardless.\n }\n }\n}\n\n/** A short, log-friendly description of a conversation step for progress output. */\nfunction describeStep(step: ConversationStep): string {\n if (step.type === \"toolCall\") return `toolCall:${step.message.type}`;\n return step.type;\n}\n","import type { AgentModeOption, SettingSource } from \"@cursor/sdk\";\nimport type { CursorUsage } from \"./agent-events.js\";\nimport { streamAgentTurn } from \"./agent-events.js\";\nimport { resolveControls } from \"./controls.js\";\nimport { acquireAgent } from \"./session-pool.js\";\n\nexport interface DelegateParams {\n\tapiKey: string;\n\t/** The subtask to delegate to the Cursor agent. */\n\tprompt: string;\n\t/** Cursor model id to run the delegation on. */\n\tmodel: string;\n\t/** Conversation mode; defaults to \"agent\". */\n\tmode?: AgentModeOption;\n\t/** Convenience for the Cursor `thinking` model param (e.g. \"high\"). */\n\tthinking?: string;\n\t/**\n\t * Working directory the local agent operates in. An array supplies\n\t * additional workspace roots, giving the agent multi-root access.\n\t */\n\tcwd: string | string[];\n\t/** Run the agent's tools inside Cursor's sandbox. */\n\tsandbox?: boolean;\n\t/** Resume a specific Cursor agent by id instead of creating a fresh one. */\n\tagentId?: string;\n\t/** Cancels the run when aborted (wired to the tool's abort signal). */\n\tabortSignal?: AbortSignal;\n\t/**\n\t * Cursor settings layers to load from disk. When omitted, defaults to\n\t * `[\"project\"]` so the delegate picks up `.cursor/skills/` and other\n\t * project-level config from its cwd. Pass an explicit array to override.\n\t */\n\tsettingSources?: SettingSource[];\n}\n\nexport interface DelegateToolActivity {\n\tname: string;\n\tisError: boolean;\n}\n\nexport interface DelegateResult {\n\tagentId: string;\n\ttext: string;\n\treasoning: string;\n\ttoolActivity: DelegateToolActivity[];\n\tusage?: CursorUsage;\n}\n\n/**\n * Run a single delegated turn on a fresh (or explicitly resumed) local Cursor\n * agent and aggregate the outcome into a plain result. This backs the opt-in\n * `cursor_delegate` tool, which gives users a permission-gated boundary around\n * Cursor (the provider path runs Cursor's own loop without per-call gating).\n *\n * Reuses the provider's `acquireAgent` + `streamAgentTurn` plumbing; the turn\n * is consumed eagerly here because a tool returns a single result rather than a\n * live stream.\n */\nexport async function runDelegate(\n\tparams: DelegateParams,\n): Promise<DelegateResult> {\n\tconst { mode, modelSelection } = resolveControls(\n\t\tparams.model,\n\t\t{\n\t\t\tmode: params.mode ?? \"agent\",\n\t\t\t...(params.thinking ? { params: { thinking: params.thinking } } : {}),\n\t\t},\n\t\tundefined,\n\t);\n\n\tconst acquired = await acquireAgent({\n\t\tapiKey: params.apiKey,\n\t\tmodelSelection,\n\t\tmode,\n\t\tcwd: params.cwd,\n\t\t// Default to the \"project\" settings layer so the delegate picks up\n\t\t// `.cursor/skills/` and other project-level config from its cwd. An\n\t\t// explicit `settingSources` from the caller overrides this default.\n\t\tsettingSources: params.settingSources ?? [\"project\"],\n\t\t...(params.sandbox !== undefined ? { sandbox: params.sandbox } : {}),\n\t\t...(params.agentId ? { resumeAgentId: params.agentId } : {}),\n\t});\n\n\tconst text: string[] = [];\n\tconst reasoning: string[] = [];\n\tconst toolActivity: DelegateToolActivity[] = [];\n\tlet usage: CursorUsage | undefined;\n\n\ttry {\n\t\tfor await (const event of streamAgentTurn(\n\t\t\tacquired.agent,\n\t\t\t{ text: params.prompt },\n\t\t\t{\n\t\t\t\tmode,\n\t\t\t\t...(params.abortSignal ? { abortSignal: params.abortSignal } : {}),\n\t\t\t},\n\t\t)) {\n\t\t\tswitch (event.type) {\n\t\t\t\tcase \"text-delta\":\n\t\t\t\t\ttext.push(event.text);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"reasoning-delta\":\n\t\t\t\t\treasoning.push(event.text);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"tool-input-partial\":\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"tool-call\":\n\t\t\t\t\ttoolActivity.push({ name: event.name, isError: false });\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"tool-result\":\n\t\t\t\t\tif (event.isError)\n\t\t\t\t\t\ttoolActivity.push({ name: event.name, isError: true });\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"usage\":\n\t\t\t\t\tusage = event.usage;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"reasoning-complete\":\n\t\t\t\tcase \"compaction\":\n\t\t\t\tcase \"subagent-event\":\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"finish\":\n\t\t\t\t\t// The aggregated result text; prefer it when deltas were absent.\n\t\t\t\t\tif (event.text && text.length === 0) text.push(event.text);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t} finally {\n\t\tacquired.release();\n\t}\n\n\treturn {\n\t\tagentId: acquired.agent.agentId,\n\t\ttext: text.join(\"\"),\n\t\treasoning: reasoning.join(\"\"),\n\t\ttoolActivity,\n\t\t...(usage ? { usage } : {}),\n\t};\n}\n","import { createRequire } from \"node:module\";\nimport { homedir, tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { get } from \"node:https\";\nimport { mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport semver from \"semver\";\n\n/**\n * Inlined by tsup's `define` option in the published bundle (see\n * tsup.config.ts). In the bundle, a relative require of `../package.json`\n * would resolve inside `dist/` where no package.json exists, so the version\n * must be baked in at build time. When running un-bundled (tests against\n * `src/`), this stays undefined and `getLocalVersion` falls back to reading\n * package.json.\n */\ndeclare const __PKG_VERSION__: string | undefined;\n\nconst PACKAGE_NAME = \"@stablekernel/opencode-cursor\";\nconst REGISTRY_URL = `https://registry.npmjs.org/${encodeURIComponent(PACKAGE_NAME)}/latest`;\n\n/**\n * The path where opencode caches the installed plugin package.\n * Used by `warnIfStale` (to build the removal command) and by the\n * `cursor_update_plugin` tool (to actually clear the cache) — single source\n * of truth so both stay in sync.\n */\nexport const PLUGIN_CACHE_PATH =\n\tprocess.platform === \"win32\"\n\t\t? join(\n\t\t\t\tprocess.env.LocalAppData ?? join(homedir(), \"AppData\", \"Local\"),\n\t\t\t\t\"opencode\",\n\t\t\t\t\"cache\",\n\t\t\t\t\"packages\",\n\t\t\t\t\"@stablekernel\",\n\t\t\t\t\"opencode-cursor@latest\",\n\t\t )\n\t\t: join(homedir(), \".cache\", \"opencode\", \"packages\", `${PACKAGE_NAME}@latest`);\nconst CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;\n// Failed fetches are retried sooner than successful ones so a transient\n// network error doesn't suppress the check for a full day.\nconst FAILURE_TTL_MS = 60 * 60 * 1000;\nconst REQUEST_TIMEOUT_MS = 5000;\n\ninterface VersionCheckCache {\n\tcheckedAt: number;\n\tlatest: string | undefined;\n}\n\nfunction cacheDir(): string {\n\tconst base =\n\t\tprocess.env.XDG_CACHE_HOME?.trim() ||\n\t\t(homedir() ? join(homedir(), \".cache\") : tmpdir());\n\treturn join(base, \"opencode-cursor\");\n}\n\nfunction cacheFile(): string {\n\treturn join(cacheDir(), \"version-check.json\");\n}\n\nfunction readCache(): VersionCheckCache | undefined {\n\ttry {\n\t\tconst parsed = JSON.parse(readFileSync(cacheFile(), \"utf8\")) as VersionCheckCache;\n\t\tif (typeof parsed.checkedAt === \"number\") return parsed;\n\t} catch {\n\t\t// ignore\n\t}\n\treturn undefined;\n}\n\nfunction writeCache(latest: string | undefined): void {\n\ttry {\n\t\tmkdirSync(cacheDir(), { recursive: true });\n\t\twriteFileSync(\n\t\t\tcacheFile(),\n\t\t\tJSON.stringify({ checkedAt: Date.now(), latest }),\n\t\t\t\"utf8\",\n\t\t);\n\t} catch {\n\t\t// Best-effort; never block plugin init.\n\t}\n}\n\n/** Remove the on-disk version-check cache so the next startup re-fetches from npm. */\nexport function clearVersionCache(): void {\n\ttry {\n\t\trmSync(cacheFile(), { force: true });\n\t} catch {\n\t\t// Best-effort.\n\t}\n}\n\nexport function getLocalVersion(): string | undefined {\n\t// Build-time inlined version (published bundle path).\n\tif (typeof __PKG_VERSION__ === \"string\") return __PKG_VERSION__;\n\t// Un-bundled fallback: resolve package.json relative to this source file.\n\ttry {\n\t\tconst require = createRequire(import.meta.url);\n\t\tconst pkg = require(\"../package.json\") as { version: string };\n\t\treturn pkg.version;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction fetchLatestVersion(): Promise<string | undefined> {\n\treturn new Promise((resolve) => {\n\t\tconst req = get(\n\t\t\tREGISTRY_URL,\n\t\t\t{ headers: { Accept: \"application/json\", Connection: \"close\" } },\n\t\t\t(res) => {\n\t\t\t\tif (res.statusCode !== 200) {\n\t\t\t\t\tres.resume();\n\t\t\t\t\tresolve(undefined);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tlet body = \"\";\n\t\t\t\tres.setEncoding(\"utf8\");\n\t\t\t\tres.on(\"data\", (chunk: string) => {\n\t\t\t\t\tbody += chunk;\n\t\t\t\t});\n\t\t\t\tres.on(\"end\", () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst parsed = JSON.parse(body) as { version?: string };\n\t\t\t\t\t\tresolve(parsed.version);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tresolve(undefined);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tres.on(\"error\", () => resolve(undefined));\n\t\t\t},\n\t\t);\n\t\treq.setTimeout(REQUEST_TIMEOUT_MS, () => {\n\t\t\treq.destroy();\n\t\t\tresolve(undefined);\n\t\t});\n\t\treq.on(\"error\", () => resolve(undefined));\n\t});\n}\n\n/** Return the cached latest version if fresh, else fetch from npm. */\nexport async function getLatestVersion(): Promise<string | undefined> {\n\tconst cached = readCache();\n\tif (cached) {\n\t\t// Successful lookups are trusted for 24h; failures only briefly.\n\t\tconst ttl = cached.latest ? CHECK_INTERVAL_MS : FAILURE_TTL_MS;\n\t\tif (Date.now() - cached.checkedAt < ttl) return cached.latest;\n\t}\n\tconst latest = await fetchLatestVersion();\n\twriteCache(latest);\n\treturn latest;\n}\n\n/**\n * Check whether this installed plugin is older than the registry's `latest`\n * tag. opencode resolves `@latest` once and then never reinstalls the plugin,\n * so users can silently stay on old versions.\n *\n * The registry fetch is throttled to once per 24h via an on-disk cache.\n * Staleness is surfaced via the UI toast (plugin/index.ts); no terminal output\n * is emitted. Set CI or NO_UPDATE_NOTIFIER to skip the check entirely.\n *\n * @param prefetchedLatest - Optional already-resolved latest version string.\n * Pass this when the caller has already awaited `getLatestVersion()` so the\n * registry is only fetched once per startup rather than twice.\n */\nexport async function warnIfStale(prefetchedLatest?: string): Promise<void> {\n\tif (process.env.CI || process.env.NO_UPDATE_NOTIFIER) return;\n\n\tconst local = getLocalVersion();\n\tif (!local || !semver.valid(local)) return;\n\tconst latest = prefetchedLatest ?? (await getLatestVersion());\n\tif (!latest || !semver.valid(latest)) return;\n\tif (!semver.gt(latest, local)) return;\n\n\t// Update notice is surfaced via the UI toast (plugin/index.ts); no\n\t// terminal output needed.\n}\n","import {\n\tmkdirSync,\n\twriteFileSync,\n\treadFileSync,\n\texistsSync,\n\trmSync,\n\treaddirSync,\n\tstatSync,\n\tcopyFileSync,\n\trealpathSync,\n} from \"node:fs\";\nimport type { Dirent } from \"node:fs\";\nimport { join, relative, dirname } from \"node:path\";\nimport { entryKind } from \"../plugin/skill-discovery.js\";\nimport type { DiscoveredSkill } from \"../plugin/skill-discovery.js\";\n\n/** Location of the generated mirror, relative to the agent's cwd. */\nconst SKILLS_DIR = join(\".cursor\", \"skills\");\nconst IGNORE_FILE = \".gitignore\";\n\n/**\n * Frontmatter sentinel marking mirrored skills as generated by this plugin.\n * Only directories whose `SKILL.md` carries it are ever overwritten or\n * deleted, so a user-owned `.cursor/skills/<id>` is never clobbered.\n */\nconst SENTINEL = \"generated: opencode-cursor\";\n\n/** Max bytes per individual supporting file (skip oversized files, not the whole skill). */\nconst MAX_FILE_BYTES = 1_048_576; // 1 MB\n\n/** Max total mirrored bytes across all skills. */\nconst MAX_TOTAL_BYTES = 10_485_760; // 10 MB\n\n/** Outcome of a {@link writeSkillMirror} attempt. */\nexport type SkillMirrorWrite =\n\t/** One or more skill directories were created or updated. */\n\t| \"written\"\n\t/** All skills already mirrored with identical content; no writes. */\n\t| \"unchanged\"\n\t/** No skills to mirror. */\n\t| \"empty\"\n\t/** A write error occurred; some skills may be unavailable. */\n\t| \"partial\";\n\n/** True when the file carries the generated-by sentinel in its frontmatter. */\nfunction isGenerated(content: string): boolean {\n\tif (!content.startsWith(\"---\")) return false;\n\tconst end = content.indexOf(\"\\n---\", 3);\n\tconst frontmatter = end === -1 ? content : content.slice(0, end);\n\treturn frontmatter.split(/\\r?\\n/).includes(SENTINEL);\n}\n\n/** Inject the sentinel into frontmatter, preserving name and description. */\nfunction stampSentinel(content: string): string {\n\tif (!content.startsWith(\"---\")) {\n\t\t// No frontmatter at all — shouldn't happen (discovery requires it),\n\t\t// but handle gracefully by wrapping the whole content.\n\t\treturn `---\\n${SENTINEL}\\n---\\n\\n${content}`;\n\t}\n\tconst end = content.indexOf(\"\\n---\", 3);\n\tif (end === -1) {\n\t\treturn `---\\n${SENTINEL}\\n${content.slice(3)}`;\n\t}\n\tconst frontmatter = content.slice(3, end);\n\t// Already has the sentinel — return as-is.\n\tif (frontmatter.includes(SENTINEL)) return content;\n\t// Insert sentinel before the closing ---.\n\treturn `---${frontmatter}\\n${SENTINEL}\\n${content.slice(end)}`;\n}\n\n/** Recursively copy a file tree, skipping oversized files. Returns bytes copied + skipped files. */\nfunction copyTree(\n\tsrcDir: string,\n\tdestDir: string,\n\tskillId: string,\n\tmaxBytes: number,\n\twarn: (message: string) => void,\n): { bytes: number; skipped: string[] } {\n\tlet bytes = 0;\n\tconst skipped: string[] = [];\n\t// Following symlinked directories admits cycles; track resolved paths.\n\tconst visited = new Set<string>();\n\n\tfunction walk(src: string, dest: string) {\n\t\tlet realSrc: string;\n\t\ttry {\n\t\t\trealSrc = realpathSync(src);\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tif (visited.has(realSrc)) return;\n\t\tvisited.add(realSrc);\n\t\tlet entries;\n\t\ttry {\n\t\t\tentries = readdirSync(src, { withFileTypes: true });\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tfor (const entry of entries) {\n\t\t\tconst srcPath = join(src, entry.name);\n\t\t\tconst destPath = join(dest, entry.name);\n\t\t\tconst kind = entryKind(entry, srcPath);\n\t\t\tif (kind === \"dir\") {\n\t\t\t\twalk(srcPath, destPath);\n\t\t\t} else if (kind === \"file\") {\n\t\t\t\t// Never copy SKILL.md here — it's already written with the\n\t\t\t\t// sentinel stamped by the caller. Copying the original would\n\t\t\t\t// overwrite the stamped version.\n\t\t\t\tif (entry.name === \"SKILL.md\") continue;\n\t\t\t\ttry {\n\t\t\t\t\tconst size = statSync(srcPath).size;\n\t\t\t\t\tif (size > MAX_FILE_BYTES) {\n\t\t\t\t\t\tskipped.push(relative(srcDir, srcPath));\n\t\t\t\t\t\twarn(\n\t\t\t\t\t\t\t`Skill \"${skillId}\": skipped oversized file \"${relative(srcDir, srcPath)}\" (${size} bytes > ${MAX_FILE_BYTES} limit). The rest of the skill is still mirrored.`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (bytes + size > maxBytes) {\n\t\t\t\t\t\tskipped.push(relative(srcDir, srcPath));\n\t\t\t\t\t\twarn(\n\t\t\t\t\t\t\t`Skill \"${skillId}\": skipped file \"${relative(srcDir, srcPath)}\" because the mirror's ${MAX_TOTAL_BYTES}-byte total size limit would be exceeded.`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tmkdirSync(dirname(destPath), { recursive: true });\n\t\t\t\t\tcopyFileSync(srcPath, destPath);\n\t\t\t\t\tbytes += size;\n\t\t\t\t} catch {\n\t\t\t\t\t// Best effort — skip unreadable files.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\twalk(srcDir, destDir);\n\treturn { bytes, skipped };\n}\n\n/** Compare file contents and skip the write when unchanged. */\nfunction writeIfChanged(path: string, content: string): boolean {\n\tconst existing = existsSync(path) ? readFileSync(path, \"utf8\") : undefined;\n\tif (existing === content) return false;\n\tmkdirSync(dirname(path), { recursive: true });\n\twriteFileSync(path, content, \"utf8\");\n\treturn true;\n}\n\n/**\n * Keep the generated skills out of git via `.cursor/skills/.gitignore` (which\n * also ignores itself so it doesn't pollute `git status`). Lists each mirrored\n * skill directory by id plus the `.gitignore` itself.\n */\nfunction ensureGitIgnored(dir: string, skillIds: string[]): void {\n\tconst path = join(dir, IGNORE_FILE);\n\tconst existing = existsSync(path) ? readFileSync(path, \"utf8\") : \"\";\n\tconst lines = existing.split(/\\r?\\n/);\n\tconst missing = [...skillIds, IGNORE_FILE].filter(\n\t\t(entry) => !lines.includes(entry),\n\t);\n\tif (missing.length === 0) return;\n\tconst prefix =\n\t\texisting && !existing.endsWith(\"\\n\") ? `${existing}\\n` : existing;\n\twriteFileSync(path, `${prefix}${missing.join(\"\\n\")}\\n`, \"utf8\");\n}\n\n/**\n * Materialise a git-ignored mirror of opencode's resolved skills into\n * `<cwd>/.cursor/skills/`, which Cursor discovers natively when the `project`\n * settings layer is loaded.\n *\n * Each skill is written to `<cwd>/.cursor/skills/<id>/SKILL.md` with a\n * `generated: opencode-cursor` sentinel stamped into the frontmatter.\n * Supporting files are copied alongside, preserving relative paths. A\n * user-owned skill directory (sentinel-less `SKILL.md`) is never overwritten —\n * it's skipped with a warning. Oversized individual files are skipped (not the\n * whole skill). Writes are idempotent (content compared before writing).\n * Sentinel-bearing directories for skills that no longer resolve are pruned.\n *\n * Never throws. Every failure degrades to \"this skill is unavailable this\n * turn\" plus a warning, mirroring how `resolveSystemDelivery` falls back.\n */\nexport function writeSkillMirror(\n\tcwd: string,\n\tskills: DiscoveredSkill[],\n\twarn: (message: string) => void,\n): SkillMirrorWrite {\n\tif (skills.length === 0) {\n\t\t// Still prune stale sentinel-bearing dirs.\n\t\ttry {\n\t\t\tpruneStale(cwd, new Set(), warn);\n\t\t} catch {\n\t\t\t// best effort\n\t\t}\n\t\treturn \"empty\";\n\t}\n\n\tconst dir = join(cwd, SKILLS_DIR);\n\tlet totalBytes = 0;\n\tlet wroteAny = false;\n\tlet partial = false;\n\tconst mirroredIds: string[] = [];\n\n\tfor (const skill of skills) {\n\t\tif (totalBytes >= MAX_TOTAL_BYTES) {\n\t\t\twarn(\n\t\t\t\t`Skill mirror: total size limit (${MAX_TOTAL_BYTES} bytes) reached; skipping remaining skills: ${skills\n\t\t\t\t\t.filter((s) => !mirroredIds.includes(s.id))\n\t\t\t\t\t.map((s) => s.id)\n\t\t\t\t\t.join(\", \")}`,\n\t\t\t);\n\t\t\tbreak;\n\t\t}\n\n\t\tconst skillDir = join(dir, skill.id);\n\t\tconst skillMdPath = join(skillDir, \"SKILL.md\");\n\n\t\t// Check for user-owned skill (sentinel-less SKILL.md).\n\t\tif (existsSync(skillMdPath)) {\n\t\t\ttry {\n\t\t\t\tconst existing = readFileSync(skillMdPath, \"utf8\");\n\t\t\t\tif (!isGenerated(existing)) {\n\t\t\t\t\twarn(\n\t\t\t\t\t\t`.cursor/skills/${skill.id}/SKILL.md exists but was not generated by opencode-cursor; leaving it untouched and skipping this skill.`,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Can't read — assume it's ours and proceed.\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\t// Read and stamp the SKILL.md.\n\t\t\tconst sourceContent = readFileSync(\n\t\t\t\tjoin(skill.sourceDir, \"SKILL.md\"),\n\t\t\t\t\"utf8\",\n\t\t\t);\n\t\t\tconst stamped = stampSentinel(sourceContent);\n\t\t\tconst skillMdBytes = Buffer.byteLength(stamped);\n\t\t\tif (totalBytes + skillMdBytes > MAX_TOTAL_BYTES) {\n\t\t\t\twarn(\n\t\t\t\t\t`Skill mirror: total size limit (${MAX_TOTAL_BYTES} bytes) reached; skipping skill \"${skill.id}\" and all remaining skills.`,\n\t\t\t\t);\n\t\t\t\tpartial = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst changed = writeIfChanged(skillMdPath, stamped);\n\t\t\tif (changed) wroteAny = true;\n\t\t\ttotalBytes += skillMdBytes;\n\n\t\t\t// Copy supporting files.\n\t\t\tconst { bytes, skipped } = copyTree(\n\t\t\t\t\tskill.sourceDir,\n\t\t\t\t\tskillDir,\n\t\t\t\t\tskill.id,\n\t\t\t\t\tMAX_TOTAL_BYTES - totalBytes,\n\t\t\t\t\twarn,\n\t\t\t\t);\n\t\t\ttotalBytes += bytes;\n\t\t\tif (skipped.length > 0) partial = true;\n\n\t\t\tmirroredIds.push(skill.id);\n\t\t} catch (error) {\n\t\t\tpartial = true;\n\t\t\twarn(\n\t\t\t\t`Failed to mirror skill \"${skill.id}\": ${error instanceof Error ? error.message : String(error)}. This skill will be unavailable this turn.`,\n\t\t\t);\n\t\t}\n\t}\n\n\t// Prune stale sentinel-bearing dirs for skills no longer in the set.\n\ttry {\n\t\tconst staleRemoved = pruneStale(\n\t\t\tcwd,\n\t\t\tnew Set(mirroredIds),\n\t\t\twarn,\n\t\t);\n\t\tif (staleRemoved) wroteAny = true;\n\t} catch {\n\t\t// best effort\n\t}\n\n\t// Update .gitignore with the current set of mirrored skill ids.\n\tif (mirroredIds.length > 0) {\n\t\ttry {\n\t\t\tensureGitIgnored(dir, mirroredIds);\n\t\t} catch {\n\t\t\t// non-fatal\n\t\t}\n\t}\n\n\tif (partial) return \"partial\";\n\treturn wroteAny ? \"written\" : \"unchanged\";\n}\n\n/**\n * Remove sentinel-bearing skill directories for skills that no longer resolve.\n * User-owned directories (no sentinel) are left in place. Returns true if any\n * directories were removed.\n */\nfunction pruneStale(\n\tcwd: string,\n\tcurrentIds: Set<string>,\n\twarn: (message: string) => void,\n): boolean {\n\tconst dir = join(cwd, SKILLS_DIR);\n\tif (!existsSync(dir)) return false;\n\tlet removed = false;\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = readdirSync(dir, { withFileTypes: true });\n\t} catch {\n\t\treturn false;\n\t}\n\tfor (const entry of entries) {\n\t\tif (!entry.isDirectory()) continue;\n\t\tif (entry.name === \".\" || entry.name === \"..\") continue;\n\t\tif (currentIds.has(entry.name)) continue;\n\t\tconst skillMdPath = join(dir, entry.name, \"SKILL.md\");\n\t\tif (!existsSync(skillMdPath)) continue;\n\t\ttry {\n\t\t\tconst content = readFileSync(skillMdPath, \"utf8\");\n\t\t\tif (isGenerated(content)) {\n\t\t\t\trmSync(join(dir, entry.name), { recursive: true, force: true });\n\t\t\t\tremoved = true;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Can't read — leave it alone.\n\t\t}\n\t}\n\treturn removed;\n}\n\n/**\n * Remove the entire generated skill mirror (best-effort); used on plugin\n * dispose. Only deletes directories whose `SKILL.md` carries the sentinel —\n * user-owned `.cursor/skills/<id>` directories are left in place. Also\n * removes the `.gitignore` if it was generated (contains the sentinel pattern).\n */\nexport function removeSkillMirror(cwd: string): void {\n\tconst dir = join(cwd, SKILLS_DIR);\n\tif (!existsSync(dir)) return;\n\ttry {\n\t\tconst entries = readdirSync(dir, { withFileTypes: true });\n\t\tfor (const entry of entries) {\n\t\t\tif (!entry.isDirectory()) continue;\n\t\t\tconst skillMdPath = join(dir, entry.name, \"SKILL.md\");\n\t\t\tif (!existsSync(skillMdPath)) continue;\n\t\t\ttry {\n\t\t\t\tif (isGenerated(readFileSync(skillMdPath, \"utf8\"))) {\n\t\t\t\t\trmSync(join(dir, entry.name), { recursive: true, force: true });\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// best effort\n\t\t\t}\n\t\t}\n\t\t// Remove the .gitignore if all sentinel dirs are gone.\n\t\tconst remaining = readdirSync(dir, { withFileTypes: true });\n\t\tconst hasSentinelDir = remaining.some((e) => {\n\t\t\tif (!e.isDirectory()) return false;\n\t\t\tconst p = join(dir, e.name, \"SKILL.md\");\n\t\t\tif (!existsSync(p)) return false;\n\t\t\ttry {\n\t\t\t\treturn isGenerated(readFileSync(p, \"utf8\"));\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t});\n\t\tif (!hasSentinelDir) {\n\t\t\tconst ignorePath = join(dir, IGNORE_FILE);\n\t\t\tif (existsSync(ignorePath)) {\n\t\t\t\ttry {\n\t\t\t\t\trmSync(ignorePath);\n\t\t\t\t} catch {\n\t\t\t\t\t// best effort\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// best effort — already gone or unreadable\n\t}\n}\n\n/**\n * Build the `<available_skills>` catalogue text for the system rule. Lists each\n * skill's id and description, plus a one-line instruction to load the matching\n * skill file before starting relevant work. Returns undefined when no skills\n * are mirrored (so the caller can skip appending an empty section).\n */\nexport function buildSkillsCatalogue(\n\tskills: DiscoveredSkill[],\n): string | undefined {\n\tif (skills.length === 0) return undefined;\n\tconst lines = skills.map(\n\t\t(s) => `- **${s.id}**: ${s.description}`,\n\t);\n\treturn [\n\t\t\"<available_skills>\",\n\t\t\"The following skills are available. Load the matching skill from `.cursor/skills/<id>/SKILL.md` before starting relevant work:\",\n\t\t\"\",\n\t\t...lines,\n\t\t\"</available_skills>\",\n\t].join(\"\\n\");\n}\n","import {\n\treaddirSync,\n\treadFileSync,\n\tstatSync,\n\texistsSync,\n\trealpathSync,\n} from \"node:fs\";\nimport type { Dirent } from \"node:fs\";\nimport { join, relative, dirname, resolve as resolvePath, isAbsolute } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { execSync } from \"node:child_process\";\nimport type { Config } from \"@opencode-ai/plugin\";\n\n/** A skill discovered from the filesystem, ready for permission filtering. */\nexport interface DiscoveredSkill {\n\t/** Skill id (the `name` field from frontmatter, also the directory name). */\n\tid: string;\n\t/** Human-readable name from frontmatter. */\n\tname: string;\n\t/** Description from frontmatter — used by Cursor for on-demand loading. */\n\tdescription: string;\n\t/** Absolute path to the skill's source directory (containing SKILL.md). */\n\tsourceDir: string;\n\t/** Relative paths of supporting files alongside SKILL.md (not SKILL.md itself). */\n\tfiles: string[];\n}\n\n/** Outcome of discovery + permission filtering. */\nexport interface ResolvedSkills {\n\t/** Skills permitted to mirror. */\n\tskills: DiscoveredSkill[];\n\t/** Skills withheld and why (for logging / user notification). */\n\twithheld: Array<{ id: string; reason: string }>;\n}\n\n/** Manual include/exclude override from plugin options. */\nexport interface SkillFilterOptions {\n\tinclude?: string[];\n\texclude?: string[];\n}\n\n// --- Frontmatter parsing ---\n\n/** Parse the small recognised frontmatter field set (name, description). */\nfunction parseFrontmatter(\n\tcontent: string,\n): { name?: string; description?: string } {\n\tif (!content.startsWith(\"---\")) return {};\n\tconst end = content.indexOf(\"\\n---\", 3);\n\tif (end === -1) return {};\n\tconst frontmatter = content.slice(3, end);\n\tconst result: { name?: string; description?: string } = {};\n\tfor (const line of frontmatter.split(/\\r?\\n/)) {\n\t\tconst trimmed = line.trim();\n\t\tif (!trimmed || trimmed.startsWith(\"#\")) continue;\n\t\tconst colon = trimmed.indexOf(\":\");\n\t\tif (colon === -1) continue;\n\t\tconst key = trimmed.slice(0, colon).trim();\n\t\tlet value = trimmed.slice(colon + 1).trim();\n\t\t// Strip surrounding quotes if present.\n\t\tif (\n\t\t\t(value.startsWith('\"') && value.endsWith('\"')) ||\n\t\t\t(value.startsWith(\"'\") && value.endsWith(\"'\"))\n\t\t) {\n\t\t\tvalue = value.slice(1, -1);\n\t\t}\n\t\tif (key === \"name\") result.name = value;\n\t\telse if (key === \"description\") result.description = value;\n\t}\n\treturn result;\n}\n\n// --- Filesystem walk ---\n\n/** Directory names under each config root that may contain skills. */\nconst SKILL_DIR_NAMES = [\"skill\", \"skills\"];\n\n/** External (non-opencode) config roots that contain a `skills/` subdir. */\nconst EXTERNAL_DIR_NAMES = [\".claude\", \".agents\"];\n\n/**\n * Find the git worktree root by walking up from `cwd`. Falls back to `cwd`\n * itself when not in a git repo (so a non-git project still discovers skills\n * in its own `.opencode/skills/`).\n */\nfunction worktreeRoot(cwd: string): string {\n\ttry {\n\t\tconst root = execSync(\"git rev-parse --show-toplevel\", {\n\t\t\tcwd,\n\t\t\tencoding: \"utf8\",\n\t\t\tstdio: [\"pipe\", \"pipe\", \"pipe\"],\n\t\t\ttimeout: 3000,\n\t\t}).trim();\n\t\treturn root || cwd;\n\t} catch {\n\t\treturn cwd;\n\t}\n}\n\n/** Walk up from `start` to `stop` (inclusive), yielding each directory. */\nfunction* walkUp(\n\tstart: string,\n\tstop: string,\n): Generator<string> {\n\tlet current = start;\n\twhile (current) {\n\t\tyield current;\n\t\tif (current === stop) break;\n\t\tconst parent = dirname(current);\n\t\tif (parent === current) break;\n\t\tcurrent = parent;\n\t}\n}\n\n/** List immediate subdirectories of `dir` that contain a `SKILL.md`. */\nfunction scanSkillDir(\n\tdir: string,\n): Array<{ id: string; sourceDir: string }> {\n\tif (!existsSync(dir)) return [];\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = readdirSync(dir, { withFileTypes: true });\n\t} catch {\n\t\treturn [];\n\t}\n\tconst found: Array<{ id: string; sourceDir: string }> = [];\n\tfor (const entry of entries) {\n\t\t// Symlinks are admitted here rather than filtered: `Dirent.isDirectory()`\n\t\t// is false for a symlink pointing at a directory, which would silently\n\t\t// drop skills linked in from a shared checkout. The `SKILL.md` check\n\t\t// below follows symlinks, so it rejects broken links and links to files.\n\t\tif (!entry.isDirectory() && !entry.isSymbolicLink()) continue;\n\t\tconst skillDir = join(dir, entry.name);\n\t\tif (!existsSync(join(skillDir, \"SKILL.md\"))) continue;\n\t\tfound.push({ id: entry.name, sourceDir: skillDir });\n\t}\n\treturn found;\n}\n\n/**\n * Classify a directory entry, following symlinks. `Dirent` reports a symlink\n * as neither file nor directory, so symlinked supporting files would be lost\n * without this. Broken symlinks and non-regular targets resolve to \"other\".\n */\nexport function entryKind(\n\tentry: Dirent,\n\tfullPath: string,\n): \"dir\" | \"file\" | \"other\" {\n\tif (entry.isDirectory()) return \"dir\";\n\tif (entry.isFile()) return \"file\";\n\tif (!entry.isSymbolicLink()) return \"other\";\n\ttry {\n\t\tconst target = statSync(fullPath);\n\t\tif (target.isDirectory()) return \"dir\";\n\t\tif (target.isFile()) return \"file\";\n\t} catch {\n\t\t// Broken symlink.\n\t}\n\treturn \"other\";\n}\n\n/** Collect supporting files (relative paths) alongside SKILL.md in a skill dir. */\nfunction collectFiles(sourceDir: string): string[] {\n\tconst files: string[] = [];\n\t// Following symlinked directories admits cycles; track resolved paths.\n\tconst visited = new Set<string>();\n\tfunction walk(dir: string, base: string) {\n\t\tlet realDir: string;\n\t\ttry {\n\t\t\trealDir = realpathSync(dir);\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tif (visited.has(realDir)) return;\n\t\tvisited.add(realDir);\n\t\tlet entries;\n\t\ttry {\n\t\t\tentries = readdirSync(dir, { withFileTypes: true });\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tfor (const entry of entries) {\n\t\t\tconst fullPath = join(dir, entry.name);\n\t\t\tconst relPath = relative(base, fullPath);\n\t\t\tif (entry.name === \"SKILL.md\") continue;\n\t\t\tconst kind = entryKind(entry, fullPath);\n\t\t\tif (kind === \"dir\") {\n\t\t\t\twalk(fullPath, base);\n\t\t\t} else if (kind === \"file\") {\n\t\t\t\tfiles.push(relPath);\n\t\t\t}\n\t\t}\n\t}\n\twalk(sourceDir, sourceDir);\n\treturn files;\n}\n\n/** Load and parse a single skill from its source directory. */\nfunction loadSkill(\n\tid: string,\n\tsourceDir: string,\n): DiscoveredSkill | undefined {\n\tconst skillMdPath = join(sourceDir, \"SKILL.md\");\n\tlet content: string;\n\ttry {\n\t\tcontent = readFileSync(skillMdPath, \"utf8\");\n\t} catch {\n\t\treturn undefined;\n\t}\n\tconst fm = parseFrontmatter(content);\n\t// Both name and description are required for the mirror — Cursor matches\n\t// skills by description, and the id must match the name for consistency.\n\tif (!fm.name || !fm.description) return undefined;\n\treturn {\n\t\tid,\n\t\tname: fm.name,\n\t\tdescription: fm.description,\n\t\tsourceDir,\n\t\tfiles: collectFiles(sourceDir),\n\t};\n}\n\n/**\n * Expand a path from `skills.paths` the way opencode does: `~/` prefix →\n * home, relative paths → resolved against the project directory, absolute\n * paths used as-is. Returns undefined for empty input.\n */\nfunction expandSkillPath(raw: string, cwd: string, home: string): string | undefined {\n\tconst trimmed = raw.trim();\n\tif (!trimmed) return undefined;\n\tif (trimmed.startsWith(\"~/\")) return join(home, trimmed.slice(2));\n\tif (isAbsolute(trimmed)) return trimmed;\n\treturn resolvePath(cwd, trimmed);\n}\n\n/**\n * Discover skills from the filesystem, using a deterministic resolution\n * order that prioritises specificity: project beats global, nearer beats\n * farther, `.opencode` beats `.claude`/`.agents`.\n *\n * Scan order (first wins on duplicate id — a skill already seen is kept,\n * later duplicates are skipped):\n * 1. Project `.opencode/skill/`, `.opencode/skills/` walk-up (near→far)\n * 2. Project `.claude/skills/`, `.agents/skills/` walk-up (near→far)\n * 3. Global `~/.config/opencode/skill/`, `~/.config/opencode/skills/`\n * 4. Global `~/.claude/skills/`, `~/.agents/skills/`\n * 5. `~/.opencode/skill/`, `~/.opencode/skills/` (if `~/.opencode` exists)\n * 6. Extra paths from `config.skills.paths` (lowest priority, first-wins)\n *\n * This differs from opencode's own resolution, which loads concurrently with\n * unbounded concurrency (making \"last wins\" non-deterministic). We use\n * first-wins for a deterministic, specificity-ordered mirror.\n *\n * `extraPaths` corresponds to opencode's `config.skills.paths` — additional\n * directories to scan for skills. Paths are expanded: `~/` → home, relative\n * → resolved against `cwd`, absolute used as-is. Non-existent directories\n * are silently skipped (matching opencode's behaviour).\n */\nexport function discoverSkills(\n\tcwd: string,\n\textraPaths?: string[],\n): DiscoveredSkill[] {\n\tconst home = homedir();\n\tconst xdgConfig =\n\t\tprocess.env[\"XDG_CONFIG_HOME\"] || join(home, \".config\");\n\tconst stop = worktreeRoot(cwd);\n\n\t// Build the scan list in specificity order (first wins).\n\tconst scanRoots: string[] = [];\n\n\t// 1. Project .opencode walk-up (near→far)\n\tfor (const ancestor of walkUp(cwd, stop)) {\n\t\tfor (const sub of SKILL_DIR_NAMES) {\n\t\t\tscanRoots.push(join(ancestor, \".opencode\", sub));\n\t\t}\n\t}\n\n\t// 2. Project external walk-up (near→far)\n\tfor (const ancestor of walkUp(cwd, stop)) {\n\t\tfor (const ext of EXTERNAL_DIR_NAMES) {\n\t\t\tscanRoots.push(join(ancestor, ext, \"skills\"));\n\t\t}\n\t}\n\n\t// 3. Global opencode\n\tfor (const sub of SKILL_DIR_NAMES) {\n\t\tscanRoots.push(join(xdgConfig, \"opencode\", sub));\n\t}\n\n\t// 4. Global external\n\tfor (const ext of EXTERNAL_DIR_NAMES) {\n\t\tscanRoots.push(join(home, ext, \"skills\"));\n\t}\n\n\t// 5. ~/.opencode (if it exists)\n\tconst tildeOpencode = join(home, \".opencode\");\n\tif (existsSync(tildeOpencode)) {\n\t\tfor (const sub of SKILL_DIR_NAMES) {\n\t\t\tscanRoots.push(join(tildeOpencode, sub));\n\t\t}\n\t}\n\n\t// 6. Extra paths from config.skills.paths (lowest priority)\n\tif (extraPaths) {\n\t\tfor (const raw of extraPaths) {\n\t\t\tconst expanded = expandSkillPath(raw, cwd, home);\n\t\t\tif (!expanded) continue;\n\t\t\tif (!existsSync(expanded)) continue;\n\t\t\tscanRoots.push(expanded);\n\t\t}\n\t}\n\n\t// Scan in order, first wins on duplicate id (skip if already seen).\n\tconst byId = new Map<string, DiscoveredSkill>();\n\tfor (const dir of scanRoots) {\n\t\tconst found = scanSkillDir(dir);\n\t\tfor (const { id, sourceDir } of found) {\n\t\t\tif (byId.has(id)) continue;\n\t\t\tconst skill = loadSkill(id, sourceDir);\n\t\t\tif (skill) byId.set(id, skill);\n\t\t}\n\t}\n\n\treturn Array.from(byId.values());\n}\n\n// --- Permission filtering ---\n\n/** Wildcard pattern match supporting `*` (any sequence) and literal text. */\nfunction wildcardMatch(pattern: string, value: string): boolean {\n\tif (pattern === \"*\") return true;\n\tif (!pattern.includes(\"*\")) return pattern === value;\n\t// Convert glob to regex: escape everything except *, replace * with .*\n\tconst regex = pattern.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\").replace(/\\*/g, \".*\");\n\treturn new RegExp(`^${regex}$`).test(value);\n}\n\n/** Action for a skill under the map-form permission config. */\ntype SkillAction = \"allow\" | \"deny\" | \"ask\";\n\n/** Resolve the action for a skill id from the map-form `skill` permission rule. */\nfunction resolveMapPermission(\n\tskillPerm: unknown,\n\tskillId: string,\n): SkillAction | undefined {\n\tif (typeof skillPerm === \"string\") {\n\t\treturn skillPerm as SkillAction;\n\t}\n\tif (typeof skillPerm !== \"object\" || skillPerm === null) return undefined;\n\tconst map = skillPerm as Record<string, string>;\n\t// Last-matching-pattern wins (iterate in insertion order).\n\tlet action: SkillAction | undefined;\n\tfor (const [pattern, value] of Object.entries(map)) {\n\t\tif (wildcardMatch(pattern, skillId)) {\n\t\t\taction = value as SkillAction;\n\t\t}\n\t}\n\treturn action;\n}\n\n/** Resolve the action for a skill id from the rule-array permission config. */\nfunction resolveRuleArrayPermission(\n\trules: Array<{ permission: string; pattern: string; action: string }>,\n\tskillId: string,\n): SkillAction | undefined {\n\t// Last-matching-rule wins.\n\tlet action: SkillAction | undefined;\n\tfor (const rule of rules) {\n\t\tif (rule.permission !== \"skill\") continue;\n\t\tif (wildcardMatch(rule.pattern, skillId)) {\n\t\t\taction = rule.action as SkillAction;\n\t\t}\n\t}\n\treturn action;\n}\n\n/**\n * Filter discovered skills through opencode's live permission config and the\n * plugin's manual include/exclude override.\n *\n * - `deny` → excluded entirely.\n * - `ask` → excluded (the ask prompt can't be enforced across the Cursor\n * boundary). Logged as withheld.\n * - `allow` → included.\n * - No permission config for skills → all included (default allow).\n *\n * Manual `include`/`exclude` from plugin options takes precedence over\n * permission config: `exclude` always drops, `include` always keeps (even if\n * permission says deny — the user explicitly asked for it).\n */\nexport function filterSkills(\n\tskills: DiscoveredSkill[],\n\tconfig: Config | undefined,\n\toptions?: SkillFilterOptions,\n): ResolvedSkills {\n\tconst include = options?.include ?? [];\n\tconst exclude = options?.exclude ?? [];\n\tconst matchesAny = (patterns: string[], id: string) =>\n\t\tpatterns.some((pattern) => wildcardMatch(pattern, id));\n\n\t// Extract skill permission config from both forms.\n\tconst permission = config?.permission as Record<string, unknown> | undefined;\n\tconst mapSkillPerm = permission?.[\"skill\"];\n\tconst ruleArray = Array.isArray(permission?.[\"permission\"])\n\t\t? (permission![\"permission\"] as Array<{\n\t\t\t\tpermission: string;\n\t\t\t\tpattern: string;\n\t\t\t\taction: string;\n\t\t }>)\n\t\t: undefined;\n\n\t// Also check the V2 PermissionRuleset form (config.permission as array).\n\tconst v2Ruleset = Array.isArray(config?.permission)\n\t\t? (config!.permission as Array<{\n\t\t\t\tpermission: string;\n\t\t\t\tpattern: string;\n\t\t\t\taction: string;\n\t\t }>)\n\t\t: undefined;\n\n\tconst permitted: DiscoveredSkill[] = [];\n\tconst withheld: Array<{ id: string; reason: string }> = [];\n\n\tfor (const skill of skills) {\n\t\t// Manual exclude always wins.\n\t\tif (matchesAny(exclude, skill.id)) {\n\t\t\twithheld.push({ id: skill.id, reason: \"excluded by plugin options\" });\n\t\t\tcontinue;\n\t\t}\n\t\t// Manual include always wins.\n\t\tif (include.length > 0 && matchesAny(include, skill.id)) {\n\t\t\tpermitted.push(skill);\n\t\t\tcontinue;\n\t\t}\n\t\t// If include list is specified and this skill isn't on it, skip.\n\t\tif (include.length > 0 && !matchesAny(include, skill.id)) {\n\t\t\twithheld.push({\n\t\t\t\tid: skill.id,\n\t\t\t\treason: \"not in plugin include list\",\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Resolve permission action.\n\t\tlet action: SkillAction | undefined;\n\t\tif (v2Ruleset) {\n\t\t\taction = resolveRuleArrayPermission(v2Ruleset, skill.id);\n\t\t}\n\t\tif (action === undefined && ruleArray) {\n\t\t\taction = resolveRuleArrayPermission(ruleArray, skill.id);\n\t\t}\n\t\tif (action === undefined && mapSkillPerm !== undefined) {\n\t\t\taction = resolveMapPermission(mapSkillPerm, skill.id);\n\t\t}\n\n\t\t// Default to allow when no permission config touches this skill.\n\t\tif (action === undefined || action === \"allow\") {\n\t\t\tpermitted.push(skill);\n\t\t} else if (action === \"deny\") {\n\t\t\twithheld.push({ id: skill.id, reason: \"denied by permission config\" });\n\t\t} else if (action === \"ask\") {\n\t\t\twithheld.push({\n\t\t\t\tid: skill.id,\n\t\t\t\treason:\n\t\t\t\t\t\"ask-permissioned skills are withheld (the ask prompt can't cross the Cursor boundary)\",\n\t\t\t});\n\t\t}\n\t}\n\n\treturn { skills: permitted, withheld };\n}\n\n/**\n * Discover and filter skills in one call. This is the main entry point for the\n * plugin's config and chat.params hooks. Never throws — fs errors degrade to\n * an empty skill list.\n *\n * `config.skills.paths` is extracted and passed to {@link discoverSkills} as\n * `extraPaths`, so skills configured via the `skills.paths` config option are\n * included in the mirror (lowest priority, first-wins).\n */\nexport function resolveSkills(\n\tcwd: string,\n\tconfig?: Config,\n\toptions?: SkillFilterOptions,\n): ResolvedSkills {\n\t// Extract skills.paths from the config (untyped — the V1 Config type\n\t// doesn't include the `skills` field, but the live config returned by\n\t// client.config.get() does).\n\tconst skillsConfig = config as unknown as\n\t\t| { skills?: { paths?: string[] } }\n\t\t| undefined;\n\tconst extraPaths = skillsConfig?.skills?.paths;\n\n\tlet discovered: DiscoveredSkill[];\n\ttry {\n\t\tdiscovered = discoverSkills(cwd, extraPaths);\n\t} catch {\n\t\tdiscovered = [];\n\t}\n\treturn filterSkills(discovered, config, options);\n}\n\n/**\n * A stable hash of the resolved skill set, used to skip re-materialisation\n * when nothing changed between turns. Based on skill ids + source dirs + file\n * mtimes so content changes are detected.\n */\nexport function skillSetHash(skills: DiscoveredSkill[]): string {\n\tconst parts = skills.map((s) => {\n\t\tconst files = [\"SKILL.md\", ...s.files].map((file) => {\n\t\t\ttry {\n\t\t\t\tconst stat = statSync(join(s.sourceDir, file));\n\t\t\t\treturn `${file}:${stat.mtimeMs}:${stat.size}`;\n\t\t\t} catch {\n\t\t\t\treturn `${file}:missing`;\n\t\t\t}\n\t\t});\n\t\tfiles.sort();\n\t\treturn `${s.id}:${s.sourceDir}:${files.join(\",\")}`;\n\t});\n\tparts.sort();\n\treturn parts.join(\"|\");\n}\n"],"mappings":"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","names":["rmSync","semver","out","s","homedir","tmpdir","join","mkdirSync","readFileSync","writeFileSync","cacheDir","cacheFile","require","mkdirSync","writeFileSync","readFileSync","existsSync","rmSync","readdirSync","statSync","realpathSync","join","relative","dirname","readFileSync","join","homedir","s","join","realpathSync","readdirSync","statSync","relative","mkdirSync","dirname","existsSync","readFileSync","writeFileSync","s","rmSync","semver","input","rmSync"]}
|
|
1
|
+
{"version":3,"sources":["../../src/plugin/index.ts","../../src/model-limits.ts","../../src/model-cache.ts","../../src/fallback-models.ts","../../src/model-variants.ts","../../src/model-discovery.ts","../../src/plugin/model-v2.ts","../../src/plugin/mcp-config.ts","../../src/plugin/cursor-tools.ts","../../src/provider/cloud-agent.ts","../../src/provider/delegate.ts","../../src/version-check.ts","../../src/provider/skill-mirror.ts","../../src/plugin/skill-discovery.ts","../../src/plugin/plugin-tool-registry.ts","../../src/plugin/plugin-tools-bridge.ts"],"sourcesContent":["import type { Config, Plugin, ToolContext } from \"@opencode-ai/plugin\";\nimport type { Auth } from \"@opencode-ai/sdk/v2\";\nimport type { McpServerConfig } from \"@cursor/sdk\";\nimport { rmSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport semver from \"semver\";\nimport { resolveCursorApiKey } from \"../api-key.js\";\nimport { discoverModels, toOpencodeModels } from \"../model-discovery.js\";\nimport { defaultModelParams } from \"../model-variants.js\";\nimport { buildModelV2Map, PROVIDER_ID, providerNpm } from \"./model-v2.js\";\nimport {\n\tfindUnshareableOAuthServers,\n\ttype McpStatusMap,\n\ttranslateMcpServers,\n} from \"./mcp-config.js\";\nimport { buildCursorTools } from \"./cursor-tools.js\";\nimport {\n\tgetLocalVersion,\n\tgetLatestVersion,\n\tclearVersionCache,\n\tPLUGIN_CACHE_PATH,\n} from \"../version-check.js\";\nimport { removeSystemRule } from \"../provider/system-rule.js\";\nimport {\n\tclearLogBridge,\n\tpluginLog,\n\tsetLogBridge,\n} from \"../provider/log-bridge.js\";\nimport {\n\twriteSkillMirror,\n\tremoveSkillMirror,\n\tbuildSkillsCatalogue,\n} from \"../provider/skill-mirror.js\";\nimport {\n\tresolveSkills,\n\tresolvePluginSkillSources,\n\tskillSetHash,\n\ttype LiveSkill,\n\ttype SkillFilterOptions,\n} from \"../plugin/skill-discovery.js\";\nimport {\n\tclearSubagentBridge,\n\tsetSubagentBridge,\n\tsubagentCallChildId,\n\tstampTaskPartSessionId,\n} from \"../provider/subagent-bridge.js\";\nimport {\n\tmirrorPluginTools,\n\ttype MirroredTool,\n} from \"./plugin-tool-registry.js\";\nimport {\n\tstartPluginToolsBridge,\n\ttype PluginToolsBridge,\n} from \"./plugin-tools-bridge.js\";\n\nfunction apiKeyFromAuth(auth: Auth | undefined): string | undefined {\n\treturn auth?.type === \"api\" ? auth.key : undefined;\n}\n\n/**\n * Fetch opencode's live skill inventory. The instance route is `GET /skill`\n * (OpenApi identifier `app.skills`); newer SDK clients expose it as\n * `client.app.skills(...)`, but the V1 SDK typings this repo builds against\n * (1.18.18) predate it, so fall back to a raw `client.get` (hey-api).\n *\n * SAFETY: both casts widen typed surfaces to probe for methods that may not\n * exist at runtime — the probe is optional-chained and the caller catches, so\n * a host without the route degrades to the filesystem scan.\n */\nasync function fetchLiveSkills(\n\tclient: unknown,\n\tquery?: { query?: { directory?: string } },\n): Promise<{ data?: unknown } | undefined> {\n\tconst app = (client as { app?: unknown }).app as\n\t\t| { skills?: (params?: unknown) => Promise<{ data?: unknown } | undefined> }\n\t\t| undefined;\n\tif (typeof app?.skills === \"function\") {\n\t\treturn app.skills(query);\n\t}\n\t// Fallback: the typed group predates the route, so reach the hey-api core\n\t// client underneath (`_client`) and hit the route by URL. Verified against\n\t// SDK 1.18.18: `_client.get({ url: \"/skill\" })` returns `{ data: Skill[] }`.\n\tconst inner = (client as { _client?: unknown })._client as\n\t\t| {\n\t\t\t\tget?: (opts?: {\n\t\t\t\t\turl?: string;\n\t\t\t\t\tquery?: unknown;\n\t\t\t\t}) => Promise<{ data?: unknown } | undefined>;\n\t\t }\n\t\t| undefined;\n\treturn inner?.get?.({\n\t\turl: \"/skill\",\n\t\t...(query?.query ? { query: query.query } : {}),\n\t});\n}\n\n/**\n * opencode plugin that adds a \"Cursor\" provider backed by the official Cursor\n * SDK (`@cursor/sdk`).\n *\n * - `auth`: registers an API-key login for Cursor and a `loader` that feeds the\n * key into the AI-SDK provider factory. The key is validated on first use\n * (model discovery / first call), not at login — see the note on `methods`.\n * - `config`: registers the provider (npm package + discovered/fallback models)\n * so it shows up in opencode immediately.\n * - `provider.models`: auth-aware live model discovery via `Cursor.models.list`.\n * - `tool.cursor_refresh_models`: force-refresh the model catalog.\n */\nexport const CursorPlugin: Plugin = async (input) => {\n\t// Single registry fetch shared by both the console warning and the UI\n\t// version-check paths. Throttled to once per 24h via an on-disk cache.\n\t// Fire-and-forget: never block or fail plugin init.\n\tconst _latestVersionPromise: Promise<string | undefined> = (async () => {\n\t\ttry {\n\t\t\tif (process.env.CI || process.env.NO_UPDATE_NOTIFIER) return undefined;\n\t\t\treturn await getLatestVersion();\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t})();\n\n\t// Surfaces the update notice in the UI (toast). Resolved once per plugin\n\t// instance using the shared fetch above.\n\tconst _versionCheckPromise: Promise<{ local: string; latest: string } | null> =\n\t\t(async () => {\n\t\t\ttry {\n\t\t\t\tif (process.env.CI || process.env.NO_UPDATE_NOTIFIER) return null;\n\t\t\t\tconst local = getLocalVersion();\n\t\t\t\tconst latest = await _latestVersionPromise;\n\t\t\t\tif (!local || !latest || !semver.gt(latest, local)) return null;\n\t\t\t\treturn { local, latest };\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t})();\n\tlet _toastShown = false;\n\n\t// The Cursor API key resolved by opencode's auth loader, captured so the\n\t// delegation tools (which don't receive auth directly) can reuse it. Falls\n\t// back to the CURSOR_API_KEY env var when the loader hasn't run.\n\tlet capturedApiKey: string | undefined;\n\n\t// opencode client + MCP-forwarding settings captured at config time so the\n\t// per-turn chat.params hook can re-forward the *live* MCP server set\n\t// (reflecting mid-session enable/disable) rather than the startup snapshot.\n\tconst client = input?.client;\n\n\t// Show a version update toast shortly after startup so it surfaces before\n\t// the user sends their first message. The 2s delay gives the TUI time to\n\t// initialize before we call showToast; it runs AFTER the version fetch\n\t// resolves so a slow network never suspends into the user's first prompt.\n\tvoid _versionCheckPromise\n\t\t.then(async (result) => {\n\t\t\tif (_toastShown || !result || !client) return;\n\t\t\t_toastShown = true;\n\t\t\tawait new Promise<void>((r) => setTimeout(r, 2000));\n\t\t\tconst message = `@stablekernel/opencode-cursor v${result.latest} is available (you have v${result.local}). Use the cursor_update_plugin tool to update, then restart opencode.`;\n\t\t\tvoid client.tui\n\t\t\t\t.showToast({\n\t\t\t\t\tbody: {\n\t\t\t\t\t\ttitle: \"Cursor plugin update available\",\n\t\t\t\t\t\tmessage,\n\t\t\t\t\t\tvariant: \"warning\",\n\t\t\t\t\t\tduration: 15000,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\t.catch(() => {});\n\t\t})\n\t\t.catch(() => {});\n\n\tconst directory = input?.directory;\n\t// Publish the opencode client + directory so the provider stream layer can\n\t// create a real child session for each Cursor subagent (making its `task`\n\t// card clickable / `ctrl+x`-navigable). Same-process handoff via a globalThis\n\t// registry; the provider degrades gracefully when it's absent.\n\tif (client) {\n\t\tsetSubagentBridge({ client, directory });\n\t\tsetLogBridge({ client, directory });\n\t}\n\t// Canonical working directory for the generated system-prompt rule: the\n\t// provider writes `.cursor/rules/opencode.mdc` under this path and dispose\n\t// cleans it up from the same path. The config hook threads it into the\n\t// provider options (respecting a user-configured `cwd` option) so write and\n\t// cleanup can never diverge.\n\tlet resolvedCwd = directory ?? process.cwd();\n\t// Skills bundled inside installed opencode plugins ship under the plugin\n\t// package cache, and optionally alongside file-based plugins — resolve the\n\t// plugin-side sources here so the skill mirror includes them. Set to\n\t// undefined when any source throws so the mirror falls back to its default\n\t// filesystem scan instead of mirroring nothing.\n\tlet pluginSkillSources:\n\t\t| { cacheRoot?: string; filePluginRoots?: string[] }\n\t\t| undefined;\n\ttry {\n\t\tpluginSkillSources = resolvePluginSkillSources();\n\t} catch (error) {\n\t\tpluginLog(\"warn\", \"plugin skill source discovery failed\", {\n\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\timpact: \"plugin-bundled skills unavailable to the Cursor agent\",\n\t\t});\n\t\tpluginSkillSources = undefined;\n\t}\n\tlet forwardMcp = true;\n\tlet userMcp: Record<string, McpServerConfig> = {};\n\t// Whether to let opencode drive auto-compaction. Default false: the Cursor\n\t// agent self-compacts (preCompact hook, trigger:\"auto\"), so opencode's\n\t// compaction is redundant and is what mints a fresh agentId per compaction.\n\t// Opt in with `provider.cursor.options.autoCompaction: true`.\n\tlet autoCompaction = false;\n\t// Skill forwarding state, mirroring the MCP forwarding pattern.\n\tlet forwardSkills = true;\n\tlet skillFilterOptions: SkillFilterOptions | undefined;\n\tlet lastSkillHash = \"\";\n\tlet currentSkillsCatalogue = \"\";\n\t// OAuth servers we've already warned about, so the toast fires once per\n\t// server rather than on every turn.\n\tconst warnedOAuth = new Set<string>();\n\n\t// Plugin-tool bridge state: mirrored tools from other opencode plugins,\n\t// exposed to the Cursor agent via a local stdio MCP server. Populated by\n\t// the config hook; re-checked in chat.params so plugins added mid-session\n\t// are picked up on the next turn.\n\tlet forwardPluginTools = true;\n\tlet pluginToolOptions: { include?: string[]; exclude?: string[] } | undefined;\n\tlet mirroredTools: MirroredTool[] = [];\n\tlet pluginToolsBridge: PluginToolsBridge | undefined;\n\tlet lastPermissionKey = \"\";\n\tlet pluginToolsMcpServer: McpServerConfig | undefined;\n\tlet pluginToolsWarned = false;\n\n\t/**\n\t * Mirror other plugins' tool maps and (re)start the bridge. Returns the\n\t * MCP server config to merge into the Cursor agent's `mcpServers`, or\n\t * undefined when nothing is mirrored. Never throws.\n\t */\n\tasync function syncPluginTools(\n\t\tconfig?: Config,\n\t): Promise<McpServerConfig | undefined> {\n\t\tif (!forwardPluginTools) return undefined;\n\t\ttry {\n\t\t\tconst result = await mirrorPluginTools(config, input, pluginToolOptions);\n\t\t\tif (Object.keys(result.failed).length > 0 && !pluginToolsWarned) {\n\t\t\t\tpluginToolsWarned = true;\n\t\t\t\tpluginLog(\"warn\", \"plugin tool mirror skipped some plugins\", result.failed);\n\t\t\t}\n\t\t\tif (result.tools.length === 0) {\n\t\t\t\tif (!Array.isArray(config?.plugin) && pluginToolsMcpServer) {\n\t\t\t\t\treturn pluginToolsMcpServer;\n\t\t\t\t}\n\t\t\t\tawait pluginToolsBridge?.close();\n\t\t\t\tpluginToolsBridge = undefined;\n\t\t\t\tmirroredTools = [];\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\t// Restart the bridge when the tool set OR the permission config\n\t\t\t// changed (the ask gate closes over the config snapshot).\n\t\t\tconst ids = result.tools\n\t\t\t\t.map((t) => t.id)\n\t\t\t\t.sort()\n\t\t\t\t.join(\"|\");\n\t\t\t// Permission objects are plain config JSON; JSON.stringify on the\n\t\t\t// raw value is a stable-enough identity for change detection\n\t\t\t// (config order is stable within a session).\n\t\t\tconst permKey = JSON.stringify(config?.permission ?? null);\n\t\t\tconst currentIds = mirroredTools\n\t\t\t\t.map((t) => t.id)\n\t\t\t\t.sort()\n\t\t\t\t.join(\"|\");\n\t\t\tif (\n\t\t\t\tids !== currentIds ||\n\t\t\t\tpermKey !== lastPermissionKey ||\n\t\t\t\t!pluginToolsBridge\n\t\t\t) {\n\t\t\t\tawait pluginToolsBridge?.close();\n\t\t\t\tpluginToolsBridge = await startPluginToolsBridge({\n\t\t\t\t\ttools: result.tools,\n\t\t\t\t\tdirectory: input?.directory ?? process.cwd(),\n\t\t\t\t\taskGate: makeAskGate(config?.permission),\n\t\t\t\t});\n\t\t\t\tmirroredTools = result.tools;\n\t\t\t\tlastPermissionKey = permKey;\n\t\t\t}\n\t\t\tpluginToolsMcpServer = pluginToolsBridge?.mcpServer as\n\t\t\t\t| McpServerConfig\n\t\t\t\t| undefined;\n\t\t\treturn pluginToolsMcpServer;\n\t\t} catch (error) {\n\t\t\tpluginLog(\"warn\", \"plugin tool mirror failed\", {\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t});\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Permission gate for mirrored tool execution, evaluated against the\n\t * user's opencode `permission` config exactly like a native tool's\n\t * `context.ask`:\n\t *\n\t * - `allow` → resolve silently.\n\t * - `deny` → reject (the call fails closed).\n\t * - `ask` → reject. The interactive prompt is anchored to an opencode\n\t * session/TUI; a Cursor-originated call has no way to surface it, so —\n\t * like ask-permissioned skills — it is withheld rather than run\n\t * unattended. Users who want a tool available to Cursor set it to\n\t * `allow` (optionally scoped with a pattern).\n\t */\n\tfunction makeAskGate(\n\t\tpermissionConfig: unknown,\n\t): ToolContext[\"ask\"] | undefined {\n\t\treturn async (req) => {\n\t\t\t// Mirror opencode's Permission.ask loop: evaluate every requested\n\t\t\t// pattern (default \"*\"). Any deny → reject immediately; all allow →\n\t\t\t// run; anything else is \"ask\", which can't be prompted from Cursor.\n\t\t\tconst patterns =\n\t\t\t\tArray.isArray(req.patterns) && req.patterns.length > 0\n\t\t\t\t\t? req.patterns\n\t\t\t\t\t: [\"*\"];\n\t\t\tlet needsAsk = false;\n\t\t\tfor (const pattern of patterns) {\n\t\t\t\tconst action = evaluatePermissionAction(\n\t\t\t\t\tpermissionConfig,\n\t\t\t\t\treq.permission,\n\t\t\t\t\tpattern,\n\t\t\t\t);\n\t\t\t\tif (action === \"deny\") {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`permission denied for \"${req.permission}\" (pattern \"${pattern}\")`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (action !== \"allow\") needsAsk = true;\n\t\t\t}\n\t\t\tif (!needsAsk) return;\n\t\t\tthrow new Error(\n\t\t\t\t`permission for \"${req.permission}\" is set to \"ask\", which can't be prompted from the Cursor agent — set it to \"allow\" to use this tool`,\n\t\t\t);\n\t\t};\n\t}\n\n\t/**\n\t * Resolve a permission action from the live opencode permission config,\n\t * matching opencode's own `Permission.evaluate` semantics: rules are\n\t * flattened in config order and the LAST rule whose permission wildcard\n\t * matches `permission` AND whose pattern wildcard matches `pattern` wins;\n\t * no match → \"ask\". Supported shapes:\n\t *\n\t * - rule array (V2 ruleset): `[{ permission, pattern, action }, ...]`\n\t * - map form: `{ \"pty_*\": \"allow\" }` or `{ \"pty_*\": { \"*\": \"allow\" } }`\n\t * (the nested map keys are pattern wildcards)\n\t */\n\tfunction evaluatePermissionAction(\n\t\tpermissionConfig: unknown,\n\t\tpermission: string,\n\t\tpattern: string,\n\t): \"allow\" | \"deny\" | \"ask\" {\n\t\tconst wildcardMatch = (pattern: string, value: string): boolean => {\n\t\t\tif (pattern === \"*\") return true;\n\t\t\tif (!pattern.includes(\"*\")) return pattern === value;\n\t\t\tconst regex = pattern\n\t\t\t\t.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n\t\t\t\t.replace(/\\*/g, \".*\");\n\t\t\treturn new RegExp(`^${regex}$`).test(value);\n\t\t};\n\t\tconst normalize = (value: unknown): \"allow\" | \"deny\" | \"ask\" | undefined =>\n\t\t\tvalue === \"allow\" || value === \"deny\" || value === \"ask\" ? value : undefined;\n\t\t// Mirror opencode's `expand` for pattern wildcards (permission/index.ts):\n\t\t// `~`, `~/...`, and `$HOME...` expand against the current user's home.\n\t\tconst home = process.env[\"HOME\"] || homedir();\n\t\tconst expandPattern = (pattern: string): string => {\n\t\t\tif (pattern === \"~\") return home;\n\t\t\tif (pattern.startsWith(\"~/\")) return home + pattern.slice(1);\n\t\t\tif (pattern.startsWith(\"$HOME/\")) return home + pattern.slice(5);\n\t\t\tif (pattern.startsWith(\"$HOME\")) return home + pattern.slice(5);\n\t\t\treturn pattern;\n\t\t};\n\n\t\t// Flatten the config into (permission-wildcard, pattern-wildcard, action)\n\t\t// triples in config order, then take the last match — same as opencode.\n\t\tconst rules: Array<{\n\t\t\tpermission: string;\n\t\t\tpattern: string;\n\t\t\taction: \"allow\" | \"deny\" | \"ask\";\n\t\t}> = [];\n\t\tconst pushRule = (perm: unknown, pattern: unknown, action: unknown): void => {\n\t\t\tconst normalized = normalize(action);\n\t\t\tif (typeof perm !== \"string\" || normalized === undefined) return;\n\t\t\trules.push({\n\t\t\t\tpermission: perm,\n\t\t\t\tpattern: typeof pattern === \"string\" ? expandPattern(pattern) : \"*\",\n\t\t\t\taction: normalized,\n\t\t\t});\n\t\t};\n\n\t\tif (Array.isArray(permissionConfig)) {\n\t\t\tfor (const rule of permissionConfig) {\n\t\t\t\tif (rule && typeof rule === \"object\") {\n\t\t\t\t\tconst r = rule as {\n\t\t\t\t\t\tpermission?: unknown;\n\t\t\t\t\t\tpattern?: unknown;\n\t\t\t\t\t\taction?: unknown;\n\t\t\t\t\t};\n\t\t\t\t\tpushRule(r.permission, r.pattern, r.action);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (permissionConfig && typeof permissionConfig === \"object\") {\n\t\t\tfor (const [perm, value] of Object.entries(\n\t\t\t\tpermissionConfig as Record<string, unknown>,\n\t\t\t)) {\n\t\t\t\tconst direct = normalize(value);\n\t\t\t\tif (direct) {\n\t\t\t\t\tpushRule(perm, \"*\", value);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (value && typeof value === \"object\" && !Array.isArray(value)) {\n\t\t\t\t\tfor (const [pattern, action] of Object.entries(\n\t\t\t\t\t\tvalue as Record<string, unknown>,\n\t\t\t\t\t)) {\n\t\t\t\t\t\tpushRule(perm, pattern, action);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (let i = rules.length - 1; i >= 0; i--) {\n\t\t\tconst rule = rules[i]!;\n\t\t\tif (\n\t\t\t\twildcardMatch(rule.permission, permission) &&\n\t\t\t\twildcardMatch(rule.pattern, pattern)\n\t\t\t) {\n\t\t\t\treturn rule.action;\n\t\t\t}\n\t\t}\n\t\treturn \"ask\";\n\t}\n\n\treturn {\n\t\tauth: {\n\t\t\tprovider: PROVIDER_ID,\n\t\t\tloader: async (getAuth) => {\n\t\t\t\tconst apiKey = resolveCursorApiKey(\n\t\t\t\t\tapiKeyFromAuth(await getAuth().catch(() => undefined)),\n\t\t\t\t);\n\t\t\t\tif (apiKey) {\n\t\t\t\t\tcapturedApiKey = apiKey;\n\t\t\t\t\t// The `config` hook (which seeds opencode's model picker) runs without\n\t\t\t\t\t// a key. Warm the catalog cache here — the loader is the hook that\n\t\t\t\t\t// reliably has the key — so the next launch seeds the full live\n\t\t\t\t\t// catalog instead of the static fallback.\n\t\t\t\t\t//\n\t\t\t\t\t// `forceRefresh: true` bypasses the 24h on-disk cache so a live\n\t\t\t\t\t// `Cursor.models.list()` runs on every opencode startup. This is the\n\t\t\t\t\t// stale-while-revalidate write side: the `config` and\n\t\t\t\t\t// `provider.models` hooks still serve the current cache instantly (no\n\t\t\t\t\t// startup latency), while this refreshes it in the background so newly\n\t\t\t\t\t// released Cursor models surface on the next launch instead of waiting\n\t\t\t\t\t// up to 24h for the cache to expire. Fire-and-forget: discovery never\n\t\t\t\t\t// throws and must not block auth/provider load.\n\t\t\t\t\tvoid discoverModels({ apiKey, forceRefresh: true });\n\t\t\t\t}\n\t\t\t\treturn apiKey ? { apiKey } : {};\n\t\t\t},\n\t\t\t// A single API-key method. opencode always shows its built-in \"Enter your\n\t\t\t// API key\" prompt for `type: \"api\"`, so we intentionally do NOT declare\n\t\t\t// custom `prompts` (that asks for the key a second time) or an `authorize`\n\t\t\t// callback. opencode only passes `authorize` the *custom-prompt* inputs —\n\t\t\t// never the built-in key — so validating the key in `authorize` would\n\t\t\t// force that redundant extra prompt. Instead the key is validated on first\n\t\t\t// use (model discovery / the first call both surface a bad key clearly).\n\t\t\tmethods: [{ type: \"api\", label: \"Cursor API Key\" }],\n\t\t},\n\n\t\tconfig: async (config) => {\n\t\t\tconst { models } = await discoverModels({});\n\t\t\tconfig.provider ??= {};\n\t\t\tconst existing = config.provider[PROVIDER_ID] ?? {};\n\t\t\tconst existingOptions = (existing.options ?? {}) as Record<string, unknown>;\n\n\t\t\t// Forward opencode's configured MCP servers to the Cursor\n\t\t\t// agent so it can use the same servers. Opt out via\n\t\t\t// `provider.cursor.options.forwardMcp: false`.\n\t\t\tautoCompaction = existingOptions[\"autoCompaction\"] === true;\n\t\t\tforwardMcp = existingOptions[\"forwardMcp\"] !== false;\n\t\t\tuserMcp = (existingOptions[\"mcpServers\"] ?? {}) as Record<\n\t\t\t\tstring,\n\t\t\t\tMcpServerConfig\n\t\t\t>;\n\t\t\tconst baseMcpServers = forwardMcp\n\t\t\t\t? { ...userMcp, ...translateMcpServers(config.mcp) }\n\t\t\t\t: userMcp;\n\n\t\t\t// Bridge other plugins' custom tools to the Cursor agent via a\n\t\t\t// local stdio MCP server. Opt out with\n\t\t\t// `provider.cursor.options.forwardPluginTools: false`; filter with\n\t\t\t// `provider.cursor.options.pluginTools: { include, exclude }`.\n\t\t\tforwardPluginTools = existingOptions[\"forwardPluginTools\"] !== false;\n\t\t\tpluginToolOptions = existingOptions[\"pluginTools\"] as\n\t\t\t\t| { include?: string[]; exclude?: string[] }\n\t\t\t\t| undefined;\n\t\t\tconst pluginToolsServer = await syncPluginTools(\n\t\t\t\tconfig as Config | undefined,\n\t\t\t);\n\t\t\tconst mcpServers = pluginToolsServer\n\t\t\t\t? { ...baseMcpServers, \"opencode-plugin-tools\": pluginToolsServer }\n\t\t\t\t: baseMcpServers;\n\n\t\t\t// opencode forwards a model's own options.params on the normal chat\n\t\t\t// path, but a subagent inheriting its parent's model reaches the provider\n\t\t\t// with them dropped — letting Cursor's server-side `fast: true` apply.\n\t\t\t// Thread the defaults through provider options (per-provider, survives\n\t\t\t// the drop) so the provider can re-apply them as a floor.\n\t\t\tconst modelParamDefaults: Record<string, Record<string, string>> = {};\n\t\t\tfor (const item of models) {\n\t\t\t\tconst params = defaultModelParams(item);\n\t\t\t\tif (Object.keys(params).length > 0) modelParamDefaults[item.id] = params;\n\t\t\t}\n\n\t\t\t// One canonical cwd for the provider's rule write and our dispose\n\t\t\t// cleanup: an explicit user option wins, else the plugin directory.\n\t\t\tconst optionCwd = existingOptions[\"cwd\"];\n\t\t\tresolvedCwd =\n\t\t\t\t(typeof optionCwd === \"string\" ? optionCwd : undefined) ??\n\t\t\t\tdirectory ??\n\t\t\t\tprocess.cwd();\n\n\t\t\t// Forward opencode's resolved skills (both project and global scope) to\n\t\t\t// the Cursor agent by mirroring them into `<cwd>/.cursor/skills/`. Cursor\n\t\t\t// discovers these natively when the `project` settings layer is loaded.\n\t\t\t// Opt out via `provider.cursor.options.forwardSkills: false`. Manual\n\t\t\t// include/exclude override via `provider.cursor.options.skills`.\n\t\t\tforwardSkills = existingOptions[\"forwardSkills\"] !== false;\n\t\t\tconst skillsOpt = existingOptions[\"skills\"] as\n\t\t\t\t| { include?: string[]; exclude?: string[] }\n\t\t\t\t| undefined;\n\t\t\tskillFilterOptions = skillsOpt;\n\n\t\t\tif (forwardSkills) {\n\t\t\t\ttry {\n\t\t\t\t\tconst resolved = resolveSkills(\n\t\t\t\t\t\tresolvedCwd,\n\t\t\t\t\t\tconfig as Config | undefined,\n\t\t\t\t\t\tskillFilterOptions,\n\t\t\t\t\t\tpluginSkillSources,\n\t\t\t\t\t);\n\t\t\t\t\twriteSkillMirror(resolvedCwd, resolved.skills, (msg) =>\n\t\t\t\t\t\tpluginLog(\"warn\", msg),\n\t\t\t\t\t);\n\t\t\t\t\tcurrentSkillsCatalogue = buildSkillsCatalogue(resolved.skills) ?? \"\";\n\t\t\t\t\tlastSkillHash = skillSetHash(resolved.skills);\n\t\t\t\t\tif (resolved.withheld.length > 0) {\n\t\t\t\t\t\tpluginLog(\"warn\", \"skills withheld from mirror\", {\n\t\t\t\t\t\t\twithheld: resolved.withheld.map((w) => ({\n\t\t\t\t\t\t\t\tid: w.id,\n\t\t\t\t\t\t\t\treason: w.reason,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tpluginLog(\"warn\", \"skill mirror failed\", {\n\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t\timpact: \"skills unavailable to the Cursor agent this session\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconfig.provider[PROVIDER_ID] = {\n\t\t\t\tname: \"Cursor\",\n\t\t\t\tnpm: providerNpm(),\n\t\t\t\t...existing,\n\t\t\t\toptions: {\n\t\t\t\t\t...existingOptions,\n\t\t\t\t\tcwd: resolvedCwd,\n\t\t\t\t\t...(Object.keys(mcpServers).length > 0 ? { mcpServers } : {}),\n\t\t\t\t\t...(Object.keys(modelParamDefaults).length > 0\n\t\t\t\t\t\t? { modelParamDefaults }\n\t\t\t\t\t\t: {}),\n\t\t\t\t\t...(currentSkillsCatalogue\n\t\t\t\t\t\t? { skillsCatalogue: currentSkillsCatalogue }\n\t\t\t\t\t\t: {}),\n\t\t\t\t},\n\t\t\t\tmodels: {\n\t\t\t\t\t...toOpencodeModels(models, { autoCompaction }),\n\t\t\t\t\t...(existing.models ?? {}),\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\n\t\tprovider: {\n\t\t\tid: PROVIDER_ID,\n\t\t\tmodels: async (_provider, ctx) => {\n\t\t\t\tconst apiKey = apiKeyFromAuth(ctx.auth);\n\t\t\t\tconst { models } = await discoverModels({ apiKey });\n\t\t\t\treturn buildModelV2Map(models, { autoCompaction });\n\t\t\t},\n\t\t},\n\n\t\t// Bridge opencode's session id to the provider: it lands in\n\t\t// providerOptions.cursor.sessionID, which the provider reads to pool/resume a\n\t\t// Cursor agent per session (when the `session` option is enabled).\n\t\t//\n\t\t// Also map opencode's plan AGENT to Cursor's plan mode. This hook fires\n\t\t// after opencode merges the selected variant into `output.options`, so an\n\t\t// explicit mode from the `plan` variant (or model options) wins — the\n\t\t// agent-based default only applies when no mode was set.\n\t\t\"chat.params\": async (input, output) => {\n\t\t\tif (input.model?.providerID !== PROVIDER_ID) return;\n\t\t\toutput.options = {\n\t\t\t\t...(output.options ?? {}),\n\t\t\t\tsessionID: input.sessionID,\n\t\t\t};\n\t\t\tif (input.agent === \"plan\" && output.options[\"mode\"] === undefined) {\n\t\t\t\toutput.options[\"mode\"] = \"plan\";\n\t\t\t}\n\t\t\t// opencode runs its own title-generation call on the same sessionID as\n\t\t\t// a session's real first turn, concurrently, with an unrelated (empty)\n\t\t\t// system prompt. Mark it ephemeral so the provider always treats it as\n\t\t\t// a side-call regardless of whether a pool record exists yet — without\n\t\t\t// this, a race between the two calls' agent-creation round-trips can\n\t\t\t// let the title call's fingerprint win and permanently overwrite the\n\t\t\t// session's pool record (see language-model.ts's `ephemeral` check).\n\t\t\tif (input.agent === \"title\") {\n\t\t\t\toutput.options[\"ephemeral\"] = true;\n\t\t\t}\n\n\t\t\t// Dynamically re-forward MCP servers from opencode's *live* state so\n\t\t\t// mid-session enable/disable reaches the Cursor agent (the config hook\n\t\t\t// only snapshots the set once, at startup). `client.mcp.status()` is the\n\t\t\t// runtime truth (connected/disabled/...) and `client.config.get()`\n\t\t\t// supplies the launch specs. On any failure we leave the static snapshot\n\t\t\t// (already baked into the provider options) in place.\n\t\t\tif (forwardMcp && client) {\n\t\t\t\ttry {\n\t\t\t\t\tconst query = directory ? { query: { directory } } : undefined;\n\t\t\t\t\tconst [cfgRes, statusRes] = await Promise.all([\n\t\t\t\t\t\tclient.config.get(),\n\t\t\t\t\t\tclient.mcp.status(query),\n\t\t\t\t\t]);\n\t\t\t\t\tconst liveConfig = cfgRes?.data as Config | undefined;\n\t\t\t\t\tconst liveMcp = liveConfig?.mcp;\n\t\t\t\t\tconst status = statusRes?.data as McpStatusMap | undefined;\n\t\t\t\t\tif (status) {\n\t\t\t\t\t\t// Re-sync the plugin-tools bridge against the live config too,\n\t\t\t\t\t\t// so plugins added mid-session reach Cursor on the next turn.\n\t\t\t\t\t\t// (Runs here as well as in the dedicated block below so the\n\t\t\t\t\t\t// merged server set always carries the latest bridge config.)\n\t\t\t\t\t\tconst liveToolsServer = await syncPluginTools(liveConfig);\n\t\t\t\t\t\tconst liveServers: Record<string, McpServerConfig> = {\n\t\t\t\t\t\t\t...userMcp,\n\t\t\t\t\t\t\t...translateMcpServers(liveMcp, status),\n\t\t\t\t\t\t};\n\t\t\t\t\t\tif (liveToolsServer) {\n\t\t\t\t\t\t\tliveServers[\"opencode-plugin-tools\"] = liveToolsServer;\n\t\t\t\t\t\t}\n\t\t\t\t\t\toutput.options[\"mcpServers\"] = liveServers;\n\t\t\t\t\t\t// Notify (once) about OAuth servers we can't forward: opencode\n\t\t\t\t\t\t// holds their token and it never reaches config.mcp, so the\n\t\t\t\t\t\t// Cursor agent can't connect. Only those without a shareable\n\t\t\t\t\t\t// client registration are skipped; ones with a clientId are\n\t\t\t\t\t\t// forwarded with an `auth` block for the agent's own OAuth flow.\n\t\t\t\t\t\tconst unshareable = findUnshareableOAuthServers(liveMcp, status).filter(\n\t\t\t\t\t\t\t(name) => !warnedOAuth.has(name),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (unshareable.length > 0) {\n\t\t\t\t\t\t\tfor (const name of unshareable) warnedOAuth.add(name);\n\t\t\t\t\t\t\tconst plural = unshareable.length > 1;\n\t\t\t\t\t\t\tvoid client.tui\n\t\t\t\t\t\t\t\t.showToast({\n\t\t\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\t\t\ttitle: \"Cursor MCP\",\n\t\t\t\t\t\t\t\t\t\tmessage: `Skipped OAuth MCP server${plural ? \"s\" : \"\"}: ${unshareable.join(\", \")}. opencode's token can't be shared with the Cursor agent; configure an OAuth clientId to forward ${plural ? \"them\" : \"it\"}.`,\n\t\t\t\t\t\t\t\t\t\tvariant: \"warning\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t.catch(() => {});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Keep the static snapshot; live forwarding is best-effort.\n\t\t\t\t}\n\t\t\t} else if (client && forwardPluginTools && mirroredTools.length > 0) {\n\t\t\t\t// `forwardMcp: false` still leaves the plugin-tools bridge live\n\t\t\t\t// (it's independent of opencode MCP forwarding), so re-sync it\n\t\t\t\t// against the live config and keep it in the forwarded set.\n\t\t\t\t// Guard on `mirroredTools` so installs with no plugin tools keep\n\t\t\t\t// `mcpServers` entirely absent from the per-turn output.\n\t\t\t\ttry {\n\t\t\t\t\tconst query = directory ? { query: { directory } } : undefined;\n\t\t\t\t\tconst cfgRes = await client.config.get(query);\n\t\t\t\t\tconst liveConfig = cfgRes?.data as Config | undefined;\n\t\t\t\t\tconst liveToolsServer = await syncPluginTools(liveConfig);\n\t\t\t\t\tconst liveServers: Record<string, McpServerConfig> = {\n\t\t\t\t\t\t...userMcp,\n\t\t\t\t\t};\n\t\t\t\t\tif (liveToolsServer) {\n\t\t\t\t\t\tliveServers[\"opencode-plugin-tools\"] = liveToolsServer;\n\t\t\t\t\t}\n\t\t\t\t\tif (Object.keys(liveServers).length > 0) {\n\t\t\t\t\t\toutput.options[\"mcpServers\"] = liveServers;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Keep the static snapshot; live re-sync is best-effort.\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Re-sync the skill mirror from opencode's *live* state so skills\n\t\t\t// added/removed mid-session reach the Cursor agent on the next turn.\n\t\t\t// Hash the resolved skill set and skip the write when unchanged.\n\t\t\tif (forwardSkills) {\n\t\t\t\tif (client) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst query = directory ? { query: { directory } } : undefined;\n\t\t\t\t\t\tconst cfgRes = await client.config.get(query);\n\t\t\t\t\t\tconst liveConfig = cfgRes?.data as Config | undefined;\n\t\t\t\t\t\t// Live skill inventory from opencode (`app.skills`) — covers\n\t\t\t\t\t\t// plugin-bundled skills and any other source the filesystem\n\t\t\t\t\t\t// scan can't see. Merged at lowest priority.\n\t\t\t\t\t\tlet liveSkills: LiveSkill[] | undefined;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tlet skillsRes: { data?: unknown } | undefined;\n\t\t\t\t\t\t\tskillsRes = await fetchLiveSkills(client, query);\n\t\t\t\t\t\t\tliveSkills = skillsRes?.data as LiveSkill[] | undefined;\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// Live inventory is best-effort; the filesystem scan stands.\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst resolved = resolveSkills(\n\t\t\t\t\t\t\tresolvedCwd,\n\t\t\t\t\t\t\tliveConfig,\n\t\t\t\t\t\t\tskillFilterOptions,\n\t\t\t\t\t\t\t{ ...pluginSkillSources, liveSkills },\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst hash = skillSetHash(resolved.skills);\n\t\t\t\t\t\tif (hash !== lastSkillHash) {\n\t\t\t\t\t\t\twriteSkillMirror(resolvedCwd, resolved.skills, (msg) =>\n\t\t\t\t\t\t\t\tpluginLog(\"warn\", msg),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tcurrentSkillsCatalogue = buildSkillsCatalogue(resolved.skills) ?? \"\";\n\t\t\t\t\t\t\tlastSkillHash = hash;\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Keep the existing mirror; live re-sync is best-effort.\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Always override the startup snapshot, including with an empty\n\t\t\t\t// string when all skills were removed or withheld.\n\t\t\t\toutput.options[\"skillsCatalogue\"] = currentSkillsCatalogue;\n\t\t\t}\n\t\t},\n\n\t\t// Stamp the child session id on the RUNNING `task` part. The provider\n\t\t// creates the child session when the Cursor subagent starts and\n\t\t// publishes call→child on the bridge registry; when opencode's\n\t\t// processor lands the task part (`message.part.updated`), patch it\n\t\t// (`part.update`, the native `ctx.metadata` equivalent) so the TUI\n\t\t// card carries `state.metadata.sessionId` from the start — matching\n\t\t// the native task tool, which publishes the id at execute time. The\n\t\t// processor emits a running-state part update for every streamed\n\t\t// tool part, so this fires early; the stamp is idempotent.\n\t\tevent: async (input) => {\n\t\t\tconst evt = input.event;\n\t\t\tif (evt.type !== \"message.part.updated\") return;\n\t\t\tconst part = evt.properties.part;\n\t\t\tif (!part || part.type !== \"tool\" || part.tool !== \"task\") return;\n\t\t\tconst childId = subagentCallChildId(part.callID);\n\t\t\tif (!childId) return;\n\t\t\tvoid stampTaskPartSessionId({\n\t\t\t\tsessionID: part.sessionID,\n\t\t\t\tmessageID: part.messageID,\n\t\t\t\tpartID: part.id,\n\t\t\t\tpart,\n\t\t\t\tchildId,\n\t\t\t});\n\t\t},\n\n\t\ttool: {\n\t\t\tcursor_update_plugin: {\n\t\t\t\tdescription:\n\t\t\t\t\t\"Check if the @stablekernel/opencode-cursor plugin is up to date and update it if not. Call this when the user asks to update, upgrade, or refresh the cursor plugin. Clears the cached install so opencode fetches the latest version on next launch.\",\n\t\t\t\targs: {},\n\t\t\t\texecute: async () => {\n\t\t\t\t\tif (process.env.CI || process.env.NO_UPDATE_NOTIFIER) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttitle: \"cursor plugin (checks disabled)\",\n\t\t\t\t\t\t\toutput: \"Update checks are disabled (CI or NO_UPDATE_NOTIFIER is set).\",\n\t\t\t\t\t\t\tmetadata: {\n\t\t\t\t\t\t\t\tlocal: undefined,\n\t\t\t\t\t\t\t\tlatest: undefined,\n\t\t\t\t\t\t\t\tstatus: \"disabled\" as const,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\tconst local = getLocalVersion();\n\t\t\t\t\tif (!local || !semver.valid(local)) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttitle: \"cursor plugin (unknown version)\",\n\t\t\t\t\t\t\toutput: \"Could not determine the installed plugin version.\",\n\t\t\t\t\t\t\tmetadata: { local, latest: undefined, status: \"failed\" as const },\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\tconst latest = await getLatestVersion();\n\t\t\t\t\tif (!latest || !semver.valid(latest)) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttitle: \"cursor plugin (registry unavailable)\",\n\t\t\t\t\t\t\toutput:\n\t\t\t\t\t\t\t\t\"Could not fetch the latest version from npm. Check your network connection and try again.\",\n\t\t\t\t\t\t\tmetadata: { local, latest, status: \"failed\" as const },\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!semver.gt(latest, local)) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttitle: \"cursor plugin (up to date)\",\n\t\t\t\t\t\t\toutput: `The plugin is up to date (v${local}).`,\n\t\t\t\t\t\t\tmetadata: { local, latest, status: \"up-to-date\" as const },\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\t// Plugin is outdated — clear the opencode plugin cache so it re-fetches on next launch.\n\t\t\t\t\tconst cachePath = PLUGIN_CACHE_PATH;\n\t\t\t\t\tconst removeCommand =\n\t\t\t\t\t\tprocess.platform === \"win32\"\n\t\t\t\t\t\t\t? `rmdir /s /q \"${cachePath}\"`\n\t\t\t\t\t\t\t: `rm -rf ${cachePath}`;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\trmSync(cachePath, { recursive: true, force: true });\n\t\t\t\t\t\tclearVersionCache();\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttitle: \"cursor plugin (updated)\",\n\t\t\t\t\t\t\toutput:\n\t\t\t\t\t\t\t\t`Plugin cache cleared (v${local} → v${latest}).\\n` +\n\t\t\t\t\t\t\t\t`Restart opencode to complete the upgrade — it will fetch v${latest} on next launch.`,\n\t\t\t\t\t\t\tmetadata: { local, latest, status: \"updated\" as const },\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttitle: \"cursor plugin (cache clear failed)\",\n\t\t\t\t\t\t\toutput:\n\t\t\t\t\t\t\t\t`Failed to clear plugin cache: ${message}\\n\\n` +\n\t\t\t\t\t\t\t\t`To update manually, exit opencode and run:\\n\\n` +\n\t\t\t\t\t\t\t\t` ${removeCommand}\\n\\n` +\n\t\t\t\t\t\t\t\t`then restart opencode.`,\n\t\t\t\t\t\t\tmetadata: { local, latest, status: \"failed\" as const },\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\tcursor_refresh_models: {\n\t\t\t\tdescription:\n\t\t\t\t\t\"Refresh the live Cursor model catalog now (bypasses the cache) and report the available models. The catalog also auto-refreshes on every opencode startup; use this to pick up new models mid-session. Note: to update the plugin itself (not just the model list), use the cursor_update_plugin tool.\",\n\t\t\t\targs: {},\n\t\t\t\texecute: async () => {\n\t\t\t\t\tconst result = await discoverModels({ forceRefresh: true });\n\t\t\t\t\tconst lines = result.models.map((m) => `- ${m.id} — ${m.displayName}`);\n\t\t\t\t\tconst header =\n\t\t\t\t\t\tresult.source === \"live\"\n\t\t\t\t\t\t\t? `Refreshed ${result.models.length} Cursor models (live):`\n\t\t\t\t\t\t\t: `Could not fetch live models (${result.source}). ${result.warning ?? \"\"}`.trim();\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttitle: `Cursor models (${result.source})`,\n\t\t\t\t\t\toutput: [header, ...lines].join(\"\\n\"),\n\t\t\t\t\t\tmetadata: { source: result.source, count: result.models.length },\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t},\n\t\t\t// Delegation tools that complement the provider: a cloud/background agent\n\t\t\t// and a permission-gated local delegate. They resolve the Cursor key from\n\t\t\t// the auth loader (captured above) or CURSOR_API_KEY.\n\t\t\t...buildCursorTools({\n\t\t\t\tresolveApiKey: () => resolveCursorApiKey(capturedApiKey),\n\t\t\t\tdefaultCwd: () => input?.directory ?? process.cwd(),\n\t\t\t}),\n\t\t},\n\n\t\tdispose: async () => {\n\t\t\t// Best-effort: drop the generated system-prompt rule and skill mirror\n\t\t\t// so they don't linger in the user's workspace / Cursor IDE after the\n\t\t\t// session ends. Uses the same canonical cwd the provider wrote to;\n\t\t\t// sentinel-guarded, so user-owned files are never deleted.\n\t\t\tremoveSystemRule(resolvedCwd);\n\t\t\tremoveSkillMirror(resolvedCwd);\n\t\t\tawait pluginToolsBridge?.close();\n\t\t\tpluginToolsBridge = undefined;\n\t\t\tclearSubagentBridge();\n\t\t\tclearLogBridge();\n\t\t},\n\t};\n};\n\nexport default CursorPlugin;\n","/**\n * GENERATED FILE — do not edit by hand.\n *\n * Generated by `scripts/sync-model-limits.mjs` from Cursor's published docs:\n * context windows https://cursor.com/docs/account/pricing/request-based-legacy.md\n * pricing https://cursor.com/docs/models-and-pricing.md\n *\n * Data last changed: 2026-08-03\n * (a sync that finds no data change leaves this date alone, so it dates the\n * last change to the generated maps — NOT the last time they were verified.\n * Verification runs on a schedule in CI; see the model-data-drift job.)\n * Regenerate: `npm run sync:model-limits`\n *\n * Only MODEL_CONTEXT_LIMITS and MODEL_COST are derived from the docs.\n * MODEL_OUTPUT_LIMITS further down is hand-maintained, because Cursor's docs\n * publish no output-token column — but it is still emitted from this file's\n * template, so edit it in `scripts/sync-model-limits.mjs`, not here. An edit\n * made here is reverted by the next sync.\n *\n * Pricing is read from the structured Input / Cache write / Cache read /\n * Output columns only. The `Notes` cell is deliberately NOT parsed, even\n * though promotions are announced there in prose (Claude Sonnet 5's row\n * advertises \"$2/M input and $10/M output through August 31, 2026\" while its\n * price columns still read $3 / $15). Extracting money from free text is\n * confidently wrong by construction, promo windows expire, and Cursor's own\n * `agent.getUsage()` -> `chargedCents` is the authoritative source for\n * promotions, discounts, the Cursor Token Fee, and Max Mode multipliers. This\n * map is only the rate card opencode multiplies token counts by.\n */\n\n/**\n * Per-model default context window limits (tokens), keyed by model id prefix.\n * The \"Max context\" column (1M for frontier models) requires Max Mode and is\n * NOT used here — the plugin can't detect Max Mode, so the default window is\n * the honest limit to display.\n *\n * Longest prefix wins: `claude-opus-4-8` (300K) beats `claude-opus-4` (200K).\n */\nconst MODEL_CONTEXT_LIMITS: Record<string, number> = {\n \"auto-smart\": 200_000,\n \"claude-fable-5\": 300_000,\n \"claude-haiku-4-5\": 200_000,\n \"claude-opus-4-5\": 200_000,\n \"claude-opus-4-6\": 200_000,\n \"claude-opus-4-7\": 300_000,\n \"claude-opus-4-8\": 300_000,\n \"claude-opus-5\": 300_000,\n \"claude-sonnet-4\": 200_000,\n \"claude-sonnet-4-5\": 200_000,\n \"claude-sonnet-4-6\": 200_000,\n \"claude-sonnet-5\": 200_000,\n \"composer-2\": 200_000,\n \"composer-2.5\": 200_000,\n \"default\": 200_000,\n \"gemini-2.5-flash\": 200_000,\n \"gemini-3-flash\": 200_000,\n \"gemini-3.1-pro\": 200_000,\n \"gemini-3.5-flash\": 200_000,\n \"gemini-3.6-flash\": 200_000,\n \"glm-5.2\": 200_000,\n \"gpt-5-mini\": 272_000,\n \"gpt-5.1\": 272_000,\n \"gpt-5.2\": 272_000,\n \"gpt-5.3-codex\": 272_000,\n \"gpt-5.4\": 272_000,\n \"gpt-5.4-mini\": 272_000,\n \"gpt-5.4-nano\": 272_000,\n \"gpt-5.5\": 272_000,\n \"gpt-5.6-luna\": 272_000,\n \"gpt-5.6-sol\": 272_000,\n \"gpt-5.6-terra\": 272_000,\n \"grok-4.5\": 256_000,\n};\n\nconst DEFAULT_CONTEXT_LIMIT = 200_000;\n\n/**\n * Sentinel `limit.input` that pushes opencode's auto-compaction threshold out\n * of reach, so auto-compaction never fires. opencode computes the threshold as\n * `limit.input ? limit.input - reserved : limit.context - maxOutput`, so a huge\n * `input` makes it unreachable while `limit.context` stays honest — the TUI\n * context gauge keeps working.\n *\n * Why suppress it: the Cursor agent runtime self-compacts on its own context\n * threshold (`@cursor/sdk` `dist/esm/357.js`, `preCompact` hook with\n * `trigger: \"auto\"`), so opencode-driven compaction is redundant. It is also\n * harmful — each opencode compaction rewrites the transcript, which classifies\n * as `divergence` and mints a fresh Cursor agentId, and every distinct agentId\n * permanently adds a guarded SQLite `store.db`/`-wal`/`-shm` triple that\n * `agent.close()` cannot release.\n *\n * This is NOT a real model capability. Verified against the opencode 1.18.11\n * binary by enumerating the call sites of `Is()` (the threshold function) rather\n * than textual hits on `limit.input`, since consumers reach it transitively:\n * - `vl()` — the proactive auto-compaction trigger. Suppressed here.\n * - `Pd()` — preserve-recent-tokens budget, also used by manual\n * `/compact`. Inert: it is `min(8000, max(2000,\n * floor(Is*0.25)))`, which saturates at 8000 for any\n * `Is >= 32000` — true both before and after the sentinel.\n * Everything else that touches `limit.input` is catalog merge/serialization.\n *\n * Also verified end-to-end (isolated HOME, `opencode models cursor --verbose`)\n * that a config-channel `limit.input` survives validation and reaches\n * `Provider.list()` with `limit.context` intact.\n *\n * Caveat: `Is()` is `max(0, input - reserved)`, so a user setting\n * `compaction.reserved >= this value` would drive the threshold to 0 and make\n * compaction fire every turn. Absurd but user-settable.\n */\nexport const NO_AUTO_COMPACTION_INPUT_LIMIT = 1_000_000_000;\n\n/**\n * Resolve a model's context window by longest-prefix match against\n * {@link MODEL_CONTEXT_LIMITS}. Falls back to 200K for unknown models.\n */\nexport function resolveContextLimit(modelId: string): number {\n let best: number | undefined;\n let bestLen = 0;\n for (const [prefix, limit] of Object.entries(MODEL_CONTEXT_LIMITS)) {\n if (modelId.startsWith(prefix) && prefix.length > bestLen) {\n best = limit;\n bestLen = prefix.length;\n }\n }\n return best ?? DEFAULT_CONTEXT_LIMIT;\n}\n\n/**\n * Per-model API pricing (USD per million tokens), keyed by model id prefix.\n * Cursor Models pool models (Grok 4.5, Composer, Auto) have $0 — they draw\n * from the Cursor Models pool, not the Other Models pool, so there is no\n * per-token API charge and they are absent from the pricing docs entirely.\n *\n * Longest prefix wins: `gpt-5.4-mini` (0.75) beats `gpt-5.4` (2.50).\n */\nconst MODEL_COST: Record<string, { input: number; output: number; cacheRead: number; cacheWrite: number }> = {\n \"auto-smart\": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n \"claude-fable-5\": { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 },\n \"claude-haiku-4-5\": { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 },\n \"claude-opus-4-5\": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },\n \"claude-opus-4-6\": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },\n \"claude-opus-4-7\": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },\n \"claude-opus-4-8\": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },\n \"claude-opus-5\": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },\n \"claude-sonnet-4\": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },\n \"claude-sonnet-4-5\": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },\n \"claude-sonnet-4-6\": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },\n \"claude-sonnet-5\": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },\n \"composer-2\": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n \"composer-2.5\": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n \"default\": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n \"gemini-2.5-flash\": { input: 0.3, output: 2.5, cacheRead: 0.03, cacheWrite: 0 },\n \"gemini-3-flash\": { input: 0.5, output: 3, cacheRead: 0.05, cacheWrite: 0 },\n \"gemini-3.1-pro\": { input: 2, output: 12, cacheRead: 0.2, cacheWrite: 0 },\n \"gemini-3.5-flash\": { input: 1.5, output: 9, cacheRead: 0.15, cacheWrite: 0 },\n \"gemini-3.6-flash\": { input: 1.5, output: 7.5, cacheRead: 0.15, cacheWrite: 0 },\n \"glm-5.2\": { input: 1.4, output: 4.4, cacheRead: 0.26, cacheWrite: 0 },\n \"gpt-5-mini\": { input: 0.25, output: 2, cacheRead: 0.025, cacheWrite: 0 },\n \"gpt-5.1\": { input: 1.25, output: 10, cacheRead: 0.125, cacheWrite: 0 },\n \"gpt-5.2\": { input: 1.75, output: 14, cacheRead: 0.175, cacheWrite: 0 },\n \"gpt-5.3-codex\": { input: 1.75, output: 14, cacheRead: 0.175, cacheWrite: 0 },\n \"gpt-5.4\": { input: 2.5, output: 15, cacheRead: 0.25, cacheWrite: 0 },\n \"gpt-5.4-mini\": { input: 0.75, output: 4.5, cacheRead: 0.075, cacheWrite: 0 },\n \"gpt-5.4-nano\": { input: 0.2, output: 1.25, cacheRead: 0.02, cacheWrite: 0 },\n \"gpt-5.5\": { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 0 },\n \"gpt-5.6-luna\": { input: 0.2, output: 1.2, cacheRead: 0.02, cacheWrite: 0.25 },\n \"gpt-5.6-sol\": { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 6.25 },\n \"gpt-5.6-terra\": { input: 2, output: 12, cacheRead: 0.2, cacheWrite: 2.5 },\n \"grok-4.5\": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n};\n\nconst DEFAULT_COST = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };\n\n/**\n * Resolve a model's per-token cost by longest-prefix match against\n * {@link MODEL_COST}. Falls back to $0 for unknown models (treated as\n * subscription/Cursor Models pool).\n */\nexport function resolveCost(modelId: string): {\n input: number;\n output: number;\n cacheRead: number;\n cacheWrite: number;\n} {\n let best: { input: number; output: number; cacheRead: number; cacheWrite: number } | undefined;\n let bestLen = 0;\n for (const [prefix, cost] of Object.entries(MODEL_COST)) {\n if (modelId.startsWith(prefix) && prefix.length > bestLen) {\n best = cost;\n bestLen = prefix.length;\n }\n }\n return best ?? DEFAULT_COST;\n}\n\n/**\n * NOT DERIVED FROM THE DOCS — hand-maintained in the template inside\n * `scripts/sync-model-limits.mjs`. Cursor's docs publish no output-token\n * column, so there is nothing to generate these from. Editing this map here\n * has no lasting effect; the next sync reverts it.\n *\n * Per-model output token limits, keyed by model id prefix. The Cursor SDK\n * doesn't expose output limits, so these are best-known values. 32K default\n * (the previous hardcoded value); 64K for frontier models known to support\n * higher output. Low priority — the TUI doesn't display output limit.\n */\nconst MODEL_OUTPUT_LIMITS: Record<string, number> = {\n \"claude-opus-4-7\": 64_000,\n \"claude-opus-4-8\": 64_000,\n \"claude-opus-5\": 64_000,\n \"claude-fable-5\": 64_000,\n \"gpt-5.5\": 64_000,\n \"gpt-5.6-sol\": 64_000,\n};\n\nconst DEFAULT_OUTPUT_LIMIT = 32_000;\n\n/**\n * Resolve a model's output limit by longest-prefix match. Falls back to 32K.\n */\nexport function resolveOutputLimit(modelId: string): number {\n let best: number | undefined;\n let bestLen = 0;\n for (const [prefix, limit] of Object.entries(MODEL_OUTPUT_LIMITS)) {\n if (modelId.startsWith(prefix) && prefix.length > bestLen) {\n best = limit;\n bestLen = prefix.length;\n }\n }\n return best ?? DEFAULT_OUTPUT_LIMIT;\n}\n","import { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir, tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { ModelListItem } from \"@cursor/sdk\";\n\n/** Default cache lifetime: 24 hours, overridable via env. */\nconst DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;\n\nfunction ttlMs(): number {\n const raw = process.env.OPENCODE_CURSOR_MODEL_CACHE_TTL_MS;\n const parsed = raw ? Number.parseInt(raw, 10) : NaN;\n return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_TTL_MS;\n}\n\nfunction cacheDir(): string {\n const base =\n process.env.XDG_CACHE_HOME?.trim() ||\n (homedir() ? join(homedir(), \".cache\") : tmpdir());\n return join(base, \"opencode-cursor\");\n}\n\nfunction cacheFile(fingerprint: string): string {\n return join(cacheDir(), `models-${fingerprint}.json`);\n}\n\n/**\n * Key-independent \"latest known catalog\" file. The `config` plugin hook runs\n * without access to the stored API key, so it can't read the per-key cache.\n * This file lets a keyless caller (the config hook) seed opencode's model\n * picker with the real catalog that a previous *authed* load discovered.\n */\nfunction latestCacheFile(): string {\n return join(cacheDir(), \"models-latest.json\");\n}\n\n/** The latest-catalog seed is kept longer than the per-key cache: the catalog\n * is stable and this only feeds pre-auth UI seeding. */\nconst LATEST_TTL_MS = 30 * 24 * 60 * 60 * 1000;\n\ninterface CacheEnvelope {\n savedAt: number;\n models: ModelListItem[];\n}\n\nfunction readCacheFile(file: string, maxAgeMs: number): ModelListItem[] | undefined {\n try {\n const parsed = JSON.parse(readFileSync(file, \"utf8\")) as CacheEnvelope;\n if (!parsed?.savedAt || !Array.isArray(parsed.models)) return undefined;\n if (Date.now() - parsed.savedAt > maxAgeMs) return undefined;\n return parsed.models;\n } catch {\n return undefined;\n }\n}\n\nfunction writeCacheFile(file: string, models: ModelListItem[]): void {\n try {\n mkdirSync(cacheDir(), { recursive: true });\n const envelope: CacheEnvelope = { savedAt: Date.now(), models };\n writeFileSync(file, JSON.stringify(envelope), \"utf8\");\n } catch {\n // Caching is an optimization; ignore write failures.\n }\n}\n\n/**\n * Return cached models for the given API-key fingerprint when present and still\n * fresh, otherwise `undefined`. Never throws on a missing/corrupt cache.\n */\nexport function readModelCache(fingerprint: string): ModelListItem[] | undefined {\n return readCacheFile(cacheFile(fingerprint), ttlMs());\n}\n\n/** Persist the discovered model list (per-key cache + key-independent latest\n * catalog). Best-effort; never throws. */\nexport function writeModelCache(fingerprint: string, models: ModelListItem[]): void {\n writeCacheFile(cacheFile(fingerprint), models);\n writeCacheFile(latestCacheFile(), models);\n}\n\n/**\n * Return the most recently discovered catalog regardless of API key, when\n * present and within {@link LATEST_TTL_MS}. Used by the keyless `config` hook to\n * seed the picker with the real catalog after a prior authed load.\n */\nexport function readLatestModelCache(): ModelListItem[] | undefined {\n return readCacheFile(latestCacheFile(), LATEST_TTL_MS);\n}\n","import type { ModelListItem } from \"@cursor/sdk\";\n\n/**\n * A small static snapshot of well-known Cursor models, used only when live\n * discovery is unavailable (no API key, offline, or an SDK error). The live\n * `Cursor.models.list()` result always takes precedence; this just lets the\n * provider appear in opencode with sensible defaults so the user can reach the\n * login flow. Refresh the real catalog with the `cursor_refresh_models` tool.\n */\nexport const FALLBACK_MODELS: ModelListItem[] = [\n {\n id: \"composer-2.5\",\n displayName: \"Composer 2.5\",\n description: \"Cursor's default agent model (fallback entry).\",\n parameters: [\n { id: \"thinking\", displayName: \"Thinking\", values: [{ value: \"off\" }, { value: \"on\" }] },\n ],\n },\n { id: \"claude-opus-4-8\", displayName: \"Claude Opus 4.8 (via Cursor)\" },\n { id: \"claude-sonnet-4-6\", displayName: \"Claude Sonnet 4.6 (via Cursor)\" },\n { id: \"gpt-5.5\", displayName: \"GPT-5.5 (via Cursor)\" },\n];\n","import type { ModelListItem } from \"@cursor/sdk\";\n\n/**\n * A Cursor model \"variant\" as opencode stores it: an options object that, when\n * the variant is selected, is merged into `providerOptions.cursor` and read back\n * by {@link resolveControls}.\n */\nexport interface CursorVariant {\n params?: Record<string, string>;\n mode?: \"agent\" | \"plan\";\n}\n\nconst REASONING_PARAM = /think|reason|effort/i;\nconst BOOLEAN_VALUES = new Set([\"true\", \"false\"]);\n\nfunction paramValues(param: NonNullable<ModelListItem[\"parameters\"]>[number]): string[] {\n return (param.values ?? []).map((v) => v.value);\n}\n\nfunction isBooleanParam(values: string[]): boolean {\n return values.length > 0 && values.every((v) => BOOLEAN_VALUES.has(v));\n}\n\n/**\n * Params opencode must send by DEFAULT for this model — i.e. when the user has\n * NOT picked a variant. Non-reasoning boolean toggles (notably Cursor's `fast`)\n * are pinned OFF here so the provider never silently inherits Cursor's\n * server-side default, which is `fast: true` for several models (composer-*,\n * gpt-*-codex). The user opts back IN via the matching picker variant.\n *\n * Seeded into each model's opencode `options.params` (see `toOpencodeModels` /\n * `buildModelV2Map`); {@link resolveControls} merges it into the request.\n */\nexport function defaultModelParams(item: ModelListItem): Record<string, string> {\n const out: Record<string, string> = {};\n for (const param of item.parameters ?? []) {\n if (REASONING_PARAM.test(param.id)) continue;\n if (isBooleanParam(paramValues(param))) out[param.id] = \"false\";\n }\n return out;\n}\n\n/**\n * Derive opencode model variants from a Cursor model's parameters so the\n * variant picker can expose thinking/reasoning levels plus the `fast` toggle.\n * Each variant's object is exactly what {@link resolveControls} consumes. Plan\n * mode is NOT a variant: opencode's plan agent (Tab) is mapped to Cursor's plan\n * mode by the plugin's `chat.params` hook.\n *\n * Every variant for a fast-capable model carries an explicit `fast` value\n * (reasoning variants pin it OFF via {@link defaultModelParams}; the `fast`\n * variant turns it ON) so a selection never depends on Cursor's server-side\n * default for an omitted param.\n */\n/** Slugify an SDK variant displayName for the opencode variant picker key. */\nfunction variantKey(displayName: string): string {\n return displayName.toLowerCase().replace(/[^a-z0-9]+/g, \"-\").replace(/(^-|-$)/g, \"\") || \"variant\";\n}\n\nexport function buildModelVariants(item: ModelListItem): Record<string, CursorVariant> {\n // Non-reasoning boolean defaults (e.g. { fast: \"false\" }), pinned into every\n // reasoning variant so picking a reasoning level never re-enables fast.\n const defaults = defaultModelParams(item);\n\n const sdkVariants = item.variants ?? [];\n const nonDefault = sdkVariants.filter((v) => v.isDefault !== true);\n // Some models return every variant with the SAME displayName (the model's own\n // name), differing only in params — e.g. grok-4.5 returns six \"Cursor Grok\n // 4.5\" presets that vary only in `effort`/`fast`. Those presets carry no\n // distinguishing label, so keying off the displayName yields meaningless\n // numbered collisions (\"cursor-grok-4-5\", \"cursor-grok-4-5-2\", …) that surface\n // in the picker as bogus \"thinking levels\". Detect that case (multiple presets\n // collapsing to one displayName) and fall through to deriving variants from\n // the model's parameters (effort enum + `fast` toggle) instead. Presets with\n // genuinely distinct labels — even ones that slugify alike (\"Deep Think\" vs\n // \"Deep-Think\") — are still honored via the collision counter below.\n const unlabeledPresets =\n nonDefault.length > 1 &&\n new Set(nonDefault.map((v) => v.displayName)).size === 1;\n if (nonDefault.length > 0 && !unlabeledPresets) {\n // Cursor-authoritative presets win: displayName + isDefault are curated\n // upstream; we only pin the non-reasoning boolean floors underneath.\n const out: Record<string, CursorVariant> = {};\n for (const v of nonDefault) {\n const params: Record<string, string> = { ...defaults };\n for (const p of v.params ?? []) params[p.id] = p.value;\n const key = variantKey(v.displayName);\n let candidate = key;\n for (let n = 2; out[candidate] !== undefined; n++) candidate = `${key}-${n}`;\n out[candidate] = { params };\n }\n return out;\n }\n\n const out: Record<string, CursorVariant> = {};\n\n // Pre-pass: does any reasoning param expose a non-boolean effort enum (e.g.\n // [\"low\",\"medium\",\"high\",\"xhigh\",\"max\"])? When it does, a coexisting boolean\n // reasoning toggle (Cursor's `thinking=[\"false\",\"true\"]` on claude-* models)\n // is redundant — selecting any effort level already enables reasoning — and\n // surfacing it would add a stray `thinking` variant the standard opencode\n // providers don't show. Suppress the boolean variant for parity. Order-\n // independent: the enum may be declared before or after the boolean.\n const hasEffortEnum = (item.parameters ?? []).some(\n (p) => REASONING_PARAM.test(p.id) && !isBooleanParam(paramValues(p)) && paramValues(p).length > 0,\n );\n\n for (const param of item.parameters ?? []) {\n const values = paramValues(param);\n if (values.length === 0) continue;\n const boolean = isBooleanParam(values);\n\n if (REASONING_PARAM.test(param.id)) {\n if (boolean) {\n // Boolean toggle (e.g. thinking=[\"false\",\"true\"]). Literal true/false\n // variant names are meaningless in the picker — surface a single\n // variant named after the param that switches it on. \"Off\" is the\n // model's default (no variant selected). Skipped entirely when an\n // effort enum coexists (see hasEffortEnum above).\n if (!hasEffortEnum && values.includes(\"true\")) {\n out[param.id.toLowerCase()] = { params: { ...defaults, [param.id]: \"true\" } };\n }\n continue;\n }\n\n for (const value of values) {\n // `none` means reasoning OFF — the model's default when no variant is\n // selected. Surfacing it as a selectable variant is meaningless (you\n // get it by picking nothing), so skip it. Standard providers\n // (models.dev) include `none` in their effort values, but the\n // no-variant-selected state already represents it.\n if (value === \"none\") continue;\n // Cursor labels the top reasoning tier \"extra-high\"; the opencode\n // standard (models.dev) calls it \"xhigh\". Use the standard label for\n // the variant key so the cycler is consistent across providers, but\n // keep the Cursor wire-format value (\"extra-high\") in the params sent\n // to the API.\n const displayKey = value === \"extra-high\" ? \"xhigh\" : value;\n const key = out[displayKey] === undefined ? displayKey : `${param.id}-${displayKey}`;\n out[key] = { params: { ...defaults, [param.id]: value } };\n }\n continue;\n }\n\n // Non-reasoning boolean toggle (e.g. Cursor's `fast`). Default is OFF (see\n // defaultModelParams); expose a single opt-in variant that turns it ON.\n if (boolean && values.includes(\"true\")) {\n out[param.id.toLowerCase()] = { params: { ...defaults, [param.id]: \"true\" } };\n }\n // Non-reasoning enum params (e.g. `context`) remain unsupported in the picker.\n }\n\n return out;\n}\n","import type { ModelListItem } from \"@cursor/sdk\";\nimport type { Config } from \"@opencode-ai/plugin\";\nimport { fingerprintApiKey, resolveCursorApiKey } from \"./api-key.js\";\nimport {\n NO_AUTO_COMPACTION_INPUT_LIMIT,\n resolveContextLimit,\n resolveCost,\n resolveOutputLimit,\n} from \"./model-limits.js\";\nimport { readLatestModelCache, readModelCache, writeModelCache } from \"./model-cache.js\";\nimport { FALLBACK_MODELS } from \"./fallback-models.js\";\nimport { loadCursorSdk } from \"./cursor-runtime.js\";\nimport { buildModelVariants, defaultModelParams, type CursorVariant } from \"./model-variants.js\";\n\nexport type ModelSource = \"live\" | \"cache\" | \"fallback\";\n\nexport interface DiscoveryResult {\n models: ModelListItem[];\n source: ModelSource;\n /** Human-readable note when discovery degraded (e.g. missing key, error). */\n warning?: string;\n}\n\nexport interface DiscoverOptions {\n /** Explicit key; falls back to CURSOR_API_KEY. */\n apiKey?: string;\n /** Bypass the on-disk cache and force a live `Cursor.models.list()`. */\n forceRefresh?: boolean;\n}\n\n/**\n * Discover the Cursor model catalog. Tries (in order): on-disk cache (unless\n * forced), live `Cursor.models.list()`, then the static fallback snapshot.\n * Always resolves — failures degrade to the fallback with a `warning`.\n */\nexport async function discoverModels(options: DiscoverOptions = {}): Promise<DiscoveryResult> {\n const apiKey = resolveCursorApiKey(options.apiKey);\n if (!apiKey) {\n // No key here (e.g. the keyless `config` hook). Prefer the real catalog a\n // prior authed load cached, so opencode's picker shows the full list rather\n // than only the static snapshot.\n const latest = readLatestModelCache();\n if (latest && latest.length > 0) return { models: latest, source: \"cache\" };\n return {\n models: FALLBACK_MODELS,\n source: \"fallback\",\n warning:\n \"No Cursor API key found. Run `opencode auth login` and choose Cursor, or set CURSOR_API_KEY. Showing fallback models.\",\n };\n }\n\n const fingerprint = fingerprintApiKey(apiKey);\n\n if (!options.forceRefresh) {\n const cached = readModelCache(fingerprint);\n if (cached && cached.length > 0) {\n return { models: cached, source: \"cache\" };\n }\n }\n\n try {\n const { Cursor } = await loadCursorSdk();\n const models = await Cursor.models.list({ apiKey });\n if (models.length > 0) {\n writeModelCache(fingerprint, models);\n return { models, source: \"live\" };\n }\n return {\n models: FALLBACK_MODELS,\n source: \"fallback\",\n warning: \"Cursor.models.list() returned no models; showing fallback models.\",\n };\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n // A stale cache is better than nothing on a transient failure.\n const stale = readModelCache(fingerprint);\n if (stale && stale.length > 0) {\n return { models: stale, source: \"cache\", warning: `Live discovery failed (${detail}); using cached models.` };\n }\n return {\n models: FALLBACK_MODELS,\n source: \"fallback\",\n warning: `Live discovery failed (${detail}); showing fallback models.`,\n };\n }\n}\n\n/** True when a model exposes a thinking/reasoning parameter. */\nexport function modelSupportsReasoning(item: ModelListItem): boolean {\n return (item.parameters ?? []).some((p) => /think|reason/i.test(p.id));\n}\n\n/** Shape of a single entry in opencode's `provider.<id>.models` config map. */\nexport interface OpencodeModelConfigEntry {\n id: string;\n name: string;\n attachment: boolean;\n reasoning: boolean;\n temperature: boolean;\n tool_call: boolean;\n /**\n * opencode model variants (thinking levels + plan mode). They MUST be seeded\n * here: opencode discards the plugin `provider.models()` hook for providers\n * absent from its models.dev catalog, so this config map is the only channel\n * through which cursor model variants reach the picker.\n */\n variants: Record<string, CursorVariant>;\n /**\n * Default `providerOptions.cursor` for the model, merged into every request\n * unless a variant overrides it. Carries the non-reasoning boolean defaults\n * (e.g. `{ params: { fast: \"false\" } }`) so the provider never silently runs\n * Cursor's server-side `fast` default. See {@link defaultModelParams}.\n */\n options: { params?: Record<string, string> };\n /**\n * Per-model context/output window. opencode's config channel is the only\n * one that reaches the model registry for providers absent from\n * models.dev, so the TUI session header's context-window percentage\n * depends on this being present. `context` and `output` are required by\n * the schema.\n *\n * `input` is an undocumented-but-runtime-honored field used only as\n * opencode's auto-compaction threshold. We emit\n * {@link NO_AUTO_COMPACTION_INPUT_LIMIT} to suppress auto-compaction while\n * keeping `context` honest so the TUI gauge still works. The published\n * `@opencode-ai/sdk` config types omit it, so it is excluded from\n * `_limitKeyGuard` below.\n */\n limit: { context: number; input?: number; output: number };\n /**\n * Per-model API pricing, USD per million tokens. Note the FLAT snake_case\n * cache keys — the config schema (`ProviderConfig` in\n * `@opencode-ai/sdk`) uses `cache_read`/`cache_write`, unlike the\n * `ModelV2` shape's nested `cache: { read, write }`.\n */\n cost: { input: number; output: number; cache_read: number; cache_write: number };\n}\n\n/**\n * Compile-time guard: the entries we write into\n * `config.provider.cursor.models` must satisfy the shape opencode's config\n * schema accepts. If opencode changes the schema (or we drift, e.g. by\n * using `cache: { read, write }` instead of `cache_read`/`cache_write`),\n * `npm run typecheck` fails here rather than silently producing a config\n * opencode discards.\n */\ntype AcceptedModelConfig = NonNullable<\n NonNullable<NonNullable<Config[\"provider\"]>[string]>[\"models\"]\n>[string];\nconst _entryShapeGuard: AcceptedModelConfig = {} as OpencodeModelConfigEntry;\nvoid _entryShapeGuard;\n\n/**\n * Assignability alone is too weak for `cost`/`limit`. Excess-property checking\n * only applies to fresh object literals, and the schema's cache keys are\n * optional — so a drifted `cost: { input, output, cache: { read, write } }`\n * assigns cleanly to the accepted shape (verified: it typechecks) while\n * opencode would read `cache_read`/`cache_write` as absent. These guards\n * assert every key we emit is a key the schema actually declares.\n *\n * `never` means \"no excess keys\"; anything else collapses `_KeysAccepted` to\n * `never` and the `true` initializer below fails to compile.\n */\ntype _KeysAccepted<Ours, Accepted> = Exclude<keyof Ours, keyof Accepted> extends never ? true : never;\nconst _costKeyGuard: _KeysAccepted<\n OpencodeModelConfigEntry[\"cost\"],\n NonNullable<AcceptedModelConfig[\"cost\"]>\n> = true;\nvoid _costKeyGuard;\n// `input` is deliberately excluded: opencode's runtime reads it (verified in\n// the 1.18.11 binary and end-to-end via `Provider.list()`), but the published\n// config types don't declare it. The guard still protects `context`/`output`.\nconst _limitKeyGuard: _KeysAccepted<\n Omit<OpencodeModelConfigEntry[\"limit\"], \"input\">,\n NonNullable<AcceptedModelConfig[\"limit\"]>\n> = true;\nvoid _limitKeyGuard;\n\n/**\n * Map discovered Cursor models to opencode's provider config `models` map. The\n * Cursor SDK runs an agent (it calls tools itself), so every model is marked\n * `tool_call: true` and `temperature: false`.\n */\nexport function toOpencodeModels(\n items: ModelListItem[],\n opts: { autoCompaction?: boolean } = {},\n): Record<string, OpencodeModelConfigEntry> {\n const out: Record<string, OpencodeModelConfigEntry> = {};\n for (const item of items) {\n const params = defaultModelParams(item);\n const cost = resolveCost(item.id);\n out[item.id] = {\n id: item.id,\n name: item.displayName || item.id,\n attachment: true,\n reasoning: modelSupportsReasoning(item),\n temperature: false,\n tool_call: true,\n variants: buildModelVariants(item),\n options: Object.keys(params).length > 0 ? { params } : {},\n limit: {\n context: resolveContextLimit(item.id),\n // Suppress opencode's auto-compaction unless the user opts in: the\n // Cursor agent self-compacts, and opencode's compaction mints a\n // fresh agentId per cycle, permanently leaking guarded SQLite fds.\n ...(opts.autoCompaction\n ? {}\n : { input: NO_AUTO_COMPACTION_INPUT_LIMIT }),\n output: resolveOutputLimit(item.id),\n },\n cost: {\n input: cost.input,\n output: cost.output,\n cache_read: cost.cacheRead,\n cache_write: cost.cacheWrite,\n },\n };\n }\n return out;\n}\n","import type { Model as ModelV2 } from \"@opencode-ai/sdk/v2\";\nimport type { ModelListItem } from \"@cursor/sdk\";\nimport { modelSupportsReasoning } from \"../model-discovery.js\";\nimport {\n NO_AUTO_COMPACTION_INPUT_LIMIT,\n resolveContextLimit,\n resolveCost,\n resolveOutputLimit,\n} from \"../model-limits.js\";\nimport { buildModelVariants, defaultModelParams } from \"../model-variants.js\";\n\nexport const PROVIDER_ID = \"cursor\";\nexport const NPM_PACKAGE = \"@stablekernel/opencode-cursor\";\n\n/**\n * The npm specifier opencode uses to load the provider SDK. Defaults to the\n * published package name; can be overridden with a `file://...` URL (which\n * opencode imports directly, skipping a registry install) via\n * `OPENCODE_CURSOR_PROVIDER_NPM` — useful for local development and CI before\n * the package is published.\n */\nexport function providerNpm(): string {\n return process.env.OPENCODE_CURSOR_PROVIDER_NPM?.trim() || NPM_PACKAGE;\n}\n\n/**\n * Build opencode's rich runtime `Model` objects from discovered Cursor models.\n * Used by the auth-aware `provider.models()` hook. Cost and context/output\n * limits are resolved per model from the shared maps in `../model-limits.js`,\n * falling back to $0 / 200K context / 32K output for models absent from them.\n */\nexport function buildModelV2Map(\n items: ModelListItem[],\n opts: { autoCompaction?: boolean } = {},\n): Record<string, ModelV2> {\n const out: Record<string, ModelV2> = {};\n for (const item of items) {\n const params = defaultModelParams(item);\n out[item.id] = {\n id: item.id,\n providerID: PROVIDER_ID,\n api: { id: item.id, url: \"\", npm: providerNpm() },\n name: item.displayName || item.id,\n capabilities: {\n temperature: false,\n reasoning: modelSupportsReasoning(item),\n attachment: true,\n toolcall: true,\n input: { text: true, audio: false, image: true, video: false, pdf: false },\n output: { text: true, audio: false, image: false, video: false, pdf: false },\n interleaved: false,\n },\n cost: (() => {\n const c = resolveCost(item.id);\n return { input: c.input, output: c.output, cache: { read: c.cacheRead, write: c.cacheWrite } };\n })(),\n limit: {\n context: resolveContextLimit(item.id),\n ...(opts.autoCompaction\n ? {}\n : { input: NO_AUTO_COMPACTION_INPUT_LIMIT }),\n output: resolveOutputLimit(item.id),\n },\n status: \"active\",\n options: Object.keys(params).length > 0 ? { params } : {},\n headers: {},\n release_date: \"\",\n variants: buildModelVariants(item) as ModelV2[\"variants\"],\n };\n }\n return out;\n}\n","import type { Config } from \"@opencode-ai/plugin\";\nimport type { McpServerConfig } from \"@cursor/sdk\";\n\n/** The value type of opencode's `config.mcp` map. */\ntype OpencodeMcp = NonNullable<Config[\"mcp\"]>;\ntype OpencodeMcpEntry = OpencodeMcp[string];\n\n/**\n * Live MCP server status, keyed by server name, as reported by opencode's\n * `client.mcp.status()`. Only the `status` field is consumed; `\"connected\"`\n * means the server is currently usable. Mirrors the SDK's `McpStatus` union\n * without importing it (keeps this module dependency-light).\n */\nexport type McpStatusMap = Record<string, { status?: string } | undefined>;\n\n/** opencode runtime statuses that mean a server still needs OAuth to connect. */\nconst NEEDS_AUTH_STATUS = new Set([\"needs_auth\", \"needs_client_registration\"]);\n\n/** The OAuth client registration on a remote entry, or undefined when none. */\nfunction oauthConfig(\n\tentry: OpencodeMcpEntry,\n): { clientId?: string; clientSecret?: string; scope?: string } | undefined {\n\tif (entry.type !== \"remote\") return undefined;\n\t// `oauth` is `McpOAuthConfig | false | undefined`; both false and undefined\n\t// are falsy, so a truthy value is the client-registration object.\n\treturn entry.oauth ? entry.oauth : undefined;\n}\n\n/**\n * Map opencode's OAuth client registration to the Cursor SDK's `auth` block so\n * the Cursor agent can run its own OAuth flow. Returns undefined when there is\n * no `clientId` to share (e.g. RFC 7591 dynamic registration) — opencode's\n * access token itself never reaches `config.mcp`, so a bare URL would fail.\n */\nfunction toCursorAuth(\n\toauth:\n\t\t| { clientId?: string; clientSecret?: string; scope?: string }\n\t\t| undefined,\n):\n\t| { CLIENT_ID: string; CLIENT_SECRET?: string; scopes?: string[] }\n\t| undefined {\n\tif (!oauth?.clientId) return undefined;\n\tconst scopes = oauth.scope?.split(/\\s+/).filter(Boolean);\n\treturn {\n\t\tCLIENT_ID: oauth.clientId,\n\t\t...(oauth.clientSecret ? { CLIENT_SECRET: oauth.clientSecret } : {}),\n\t\t...(scopes && scopes.length > 0 ? { scopes } : {}),\n\t};\n}\n\n/**\n * Names of remote servers that require OAuth but cannot be forwarded to the\n * Cursor agent because no shareable client registration exists (dynamic\n * registration, or a `needs_auth` runtime status with no configured\n * `clientId`). The plugin surfaces these to the user instead of silently\n * forwarding a spec that would 401.\n */\nexport function findUnshareableOAuthServers(\n\tmcp: Config[\"mcp\"],\n\tstatus?: McpStatusMap,\n): string[] {\n\tconst names: string[] = [];\n\tif (!mcp) return names;\n\tfor (const [name, entry] of Object.entries(mcp) as Array<\n\t\t[string, OpencodeMcpEntry]\n\t>) {\n\t\tif (!entry || entry.type !== \"remote\") continue;\n\t\tif (!status && entry.enabled === false) continue;\n\t\tconst s = status?.[name]?.status;\n\t\tif (status && s !== \"connected\" && !NEEDS_AUTH_STATUS.has(s ?? \"\"))\n\t\t\tcontinue;\n\t\tconst oauth = oauthConfig(entry);\n\t\tconst needsOAuth = Boolean(oauth) || NEEDS_AUTH_STATUS.has(s ?? \"\");\n\t\tif (needsOAuth && !toCursorAuth(oauth)) names.push(name);\n\t}\n\treturn names;\n}\n\n/**\n * Translate opencode's configured MCP servers (`config.mcp`) into the Cursor\n * SDK's `McpServerConfig` shape so the same servers can be handed\n * to the Cursor agent via `Agent.create({ mcpServers })`.\n *\n * MCP servers are independent processes addressed by a launch spec, so opencode\n * and the Cursor agent can each connect to the same server. Disabled entries\n * (`enabled: false`) are skipped. The `timeout` field is dropped (no Cursor\n * equivalent). OAuth is mapped where possible: a remote server's `oauth` client\n * registration becomes Cursor's `auth` block so the agent runs its own OAuth\n * flow; servers needing OAuth with no shareable `clientId` are skipped (the\n * plugin reports them via {@link findUnshareableOAuthServers}).\n */\nexport function translateMcpServers(\n\tmcp: Config[\"mcp\"],\n\tstatus?: McpStatusMap,\n): Record<string, McpServerConfig> {\n\tconst out: Record<string, McpServerConfig> = {};\n\tif (!mcp) return out;\n\n\tfor (const [name, entry] of Object.entries(mcp) as Array<\n\t\t[string, OpencodeMcpEntry]\n\t>) {\n\t\tif (!entry) continue;\n\n\t\t// When a live status map is supplied (per-turn dynamic forwarding), it is\n\t\t// the source of truth: forward only servers opencode has currently\n\t\t// connected, so mid-session enable/disable propagates to the Cursor agent.\n\t\t// Without it (the startup config snapshot), fall back to the static\n\t\t// `enabled` flag.\n\t\tif (status) {\n\t\t\tif (status[name]?.status !== \"connected\") continue;\n\t\t} else if (entry.enabled === false) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.type === \"local\") {\n\t\t\tconst [command, ...args] = entry.command ?? [];\n\t\t\tif (!command) continue;\n\t\t\tout[name] = {\n\t\t\t\ttype: \"stdio\",\n\t\t\t\tcommand,\n\t\t\t\t...(args.length > 0 ? { args } : {}),\n\t\t\t\t...(entry.environment && Object.keys(entry.environment).length > 0\n\t\t\t\t\t? { env: entry.environment }\n\t\t\t\t\t: {}),\n\t\t\t};\n\t\t} else if (entry.type === \"remote\") {\n\t\t\tif (!entry.url) continue;\n\t\t\tconst oauth = oauthConfig(entry);\n\t\t\tconst auth = toCursorAuth(oauth);\n\t\t\t// OAuth server with no shareable client registration: opencode holds the\n\t\t\t// token and it never lands in config.mcp, so skip rather than forward a\n\t\t\t// bare URL that would 401. The plugin notifies the user (see\n\t\t\t// findUnshareableOAuthServers).\n\t\t\tif (oauth && !auth) continue;\n\t\t\tout[name] = {\n\t\t\t\ttype: \"http\",\n\t\t\t\turl: entry.url,\n\t\t\t\t...(entry.headers && Object.keys(entry.headers).length > 0\n\t\t\t\t\t? { headers: entry.headers }\n\t\t\t\t\t: {}),\n\t\t\t\t...(auth ? { auth } : {}),\n\t\t\t};\n\t\t}\n\t}\n\n\treturn out;\n}\n","import { tool, type ToolContext, type ToolDefinition } from \"@opencode-ai/plugin\";\nimport { runCloudAgent } from \"../provider/cloud-agent.js\";\nimport { runDelegate } from \"../provider/delegate.js\";\nimport { linkDelegateSession } from \"../provider/subagent-bridge.js\";\n\nconst s = tool.schema;\n\nexport interface CursorToolDeps {\n /**\n * Resolve the Cursor API key (from opencode auth, captured by the plugin's\n * auth loader, or the CURSOR_API_KEY env var). Returns undefined when no key\n * is available so the tool can return a clear \"needs auth\" message.\n */\n resolveApiKey: () => string | undefined;\n /** Default working directory for local delegation (the session worktree/cwd). */\n defaultCwd: () => string;\n}\n\nconst NEEDS_AUTH =\n \"No Cursor API key available. Run `opencode auth login` and choose Cursor, or set CURSOR_API_KEY.\";\n\n/**\n * Request approval for a sensitive Cursor invocation. `context.ask` is the\n * opencode mechanism a custom tool uses to gate itself; it honors the user's\n * `permission` config (allow resolves silently, ask prompts, deny rejects).\n *\n * Returns `{ ok: true }` when approved, or `{ ok: false, reason }` when the\n * request was rejected. We deliberately do not claim the rejection was a policy\n * \"deny\" — `context.ask` rejects on both an explicit deny and an internal\n * failure, and conflating them produces misleading messages. The gate is\n * fail-closed: any rejection (including a host that doesn't provide `ask`)\n * blocks the call rather than silently allowing it.\n */\nasync function requestApproval(\n context: ToolContext,\n permission: string,\n patterns: string[],\n metadata: Record<string, unknown>,\n): Promise<{ ok: boolean; reason?: string }> {\n try {\n await context.ask({ permission, patterns, always: patterns, metadata });\n return { ok: true };\n } catch (err) {\n return { ok: false, reason: err instanceof Error ? err.message : String(err) };\n }\n}\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Build the Cursor delegation tools that complement the native provider:\n * - `cursor_cloud_agent`: run a background agent on a remote repo (optionally\n * opening a PR) — work that maps poorly onto the synchronous provider path.\n * - `cursor_delegate`: run a single local Cursor turn as a permission-gated,\n * auditable tool call (for users who want Cursor as a delegate rather than\n * as their primary model).\n *\n * Both are gated via `context.ask`, so a user `permission` policy controls them.\n */\nexport function buildCursorTools(deps: CursorToolDeps): Record<string, ToolDefinition> {\n return {\n cursor_cloud_agent: tool({\n description:\n \"Launch a Cursor background ('cloud') agent on a remote repository. Runs autonomously \" +\n \"(may take minutes) and can open a pull request. Returns the cloud agent id, final \" +\n \"status, result, and PR url when available.\",\n args: {\n prompt: s.string().describe(\"The task/instruction for the background agent.\"),\n repoUrl: s\n .string()\n .describe(\"Target repository URL, e.g. https://github.com/owner/repo.\"),\n startingRef: s\n .string()\n .optional()\n .describe(\"Branch or ref to start from (defaults to the repo default branch).\"),\n model: s.string().optional().describe(\"Cursor model id (optional for cloud).\"),\n mode: s.enum([\"agent\", \"plan\"]).optional().describe(\"Conversation mode.\"),\n thinking: s.string().optional().describe(\"Thinking level, e.g. 'high'.\"),\n autoCreatePR: s\n .boolean()\n .optional()\n .describe(\"Open a pull request automatically when finished.\"),\n workOnCurrentBranch: s\n .boolean()\n .optional()\n .describe(\"Operate on the current branch instead of creating a new one.\"),\n },\n execute: async (args, context) => {\n const apiKey = deps.resolveApiKey();\n if (!apiKey) return NEEDS_AUTH;\n\n const approval = await requestApproval(\n context,\n \"cursor_cloud_agent\",\n [args.repoUrl],\n { repoUrl: args.repoUrl, autoCreatePR: args.autoCreatePR ?? false },\n );\n if (!approval.ok) {\n return `Cloud agent not approved for ${args.repoUrl}${approval.reason ? `: ${approval.reason}` : \".\"}`;\n }\n\n let result;\n try {\n result = await runCloudAgent({\n apiKey,\n prompt: args.prompt,\n repoUrl: args.repoUrl,\n ...(args.startingRef ? { startingRef: args.startingRef } : {}),\n ...(args.model ? { model: args.model } : {}),\n ...(args.mode ? { mode: args.mode } : {}),\n ...(args.thinking ? { thinking: args.thinking } : {}),\n ...(args.autoCreatePR !== undefined ? { autoCreatePR: args.autoCreatePR } : {}),\n ...(args.workOnCurrentBranch !== undefined\n ? { workOnCurrentBranch: args.workOnCurrentBranch }\n : {}),\n abortSignal: context.abort,\n });\n } catch (err) {\n return `Cloud agent failed: ${errorMessage(err)}`;\n }\n\n const lines = [\n `Cloud agent ${result.agentId} — ${result.status}`,\n ...(result.prUrl ? [`PR: ${result.prUrl}`] : []),\n ...(result.branches.length > 0\n ? [`Branches: ${result.branches.map((b) => b.branch ?? b.repoUrl).join(\", \")}`]\n : []),\n ...(result.result ? [\"\", result.result] : []),\n ...(result.progress.length > 0 ? [\"\", \"Progress:\", ...result.progress] : []),\n ];\n\n return {\n title: `Cursor cloud agent (${result.status})`,\n output: lines.join(\"\\n\"),\n metadata: {\n agentId: result.agentId,\n status: result.status,\n prUrl: result.prUrl ?? null,\n durationMs: result.durationMs ?? null,\n },\n };\n },\n }),\n\n cursor_delegate: tool({\n description:\n \"Delegate a single subtask to a local Cursor agent and return its result. Use to hand \" +\n \"off discrete work to Cursor while keeping your primary model in control. Permission-gated.\",\n args: {\n prompt: s.string().describe(\"The subtask to delegate to Cursor.\"),\n model: s.string().describe(\"Cursor model id to run the delegation on.\"),\n mode: s.enum([\"agent\", \"plan\"]).optional().describe(\"Conversation mode.\"),\n thinking: s.string().optional().describe(\"Thinking level, e.g. 'high'.\"),\n cwd: s\n .string()\n .optional()\n .describe(\"Working directory (defaults to the session directory).\"),\n additionalCwds: s\n .array(s.string())\n .optional()\n .describe(\"Extra workspace roots; combined with cwd into a multi-root agent workspace.\"),\n sandbox: s.boolean().optional().describe(\"Run the agent's tools in Cursor's sandbox.\"),\n agentId: s\n .string()\n .optional()\n .describe(\"Resume a specific Cursor agent id instead of starting fresh.\"),\n },\n execute: async (args, context) => {\n const apiKey = deps.resolveApiKey();\n if (!apiKey) return NEEDS_AUTH;\n\n const approval = await requestApproval(context, \"cursor_delegate\", [args.model], {\n model: args.model,\n prompt: args.prompt,\n });\n if (!approval.ok) {\n return `Delegation to ${args.model} not approved${approval.reason ? `: ${approval.reason}` : \".\"}`;\n }\n\n let result;\n try {\n const baseCwd = args.cwd ?? context.directory ?? deps.defaultCwd();\n result = await runDelegate({\n apiKey,\n prompt: args.prompt,\n model: args.model,\n cwd: args.additionalCwds?.length ? [baseCwd, ...args.additionalCwds] : baseCwd,\n // Honour the user's project settings layer so delegated turns pick\n // up `.cursor/skills/` from the delegate's cwd. The delegate defaults\n // to [\"project\"] on its own, so this is only needed when the user\n // explicitly configured settingSources on the provider.\n settingSources: [\"project\"],\n ...(args.mode ? { mode: args.mode } : {}),\n ...(args.thinking ? { thinking: args.thinking } : {}),\n ...(args.sandbox !== undefined ? { sandbox: args.sandbox } : {}),\n ...(args.agentId ? { agentId: args.agentId } : {}),\n abortSignal: context.abort,\n });\n } catch (err) {\n return `Delegation failed: ${errorMessage(err)}`;\n }\n\n const toolNote =\n result.toolActivity.length > 0\n ? `\\n\\n(${result.toolActivity.length} tool call(s)` +\n `${result.toolActivity.some((t) => t.isError) ? \", some failed\" : \"\"})`\n : \"\";\n\n // Surface the delegate's work in a child session so it's discoverable\n // in the TUI's subagent panel. Best-effort: a failed link never breaks\n // the turn. The result card itself stays a tool block (a custom tool\n // can't render a navigable `task` part), so the child session is\n // reached via the subagent panel, not by clicking the result.\n if (context.sessionID) {\n const transcript = [\n result.text || \"(no text output)\",\n ...(result.reasoning ? [`\\n> ${result.reasoning}`] : []),\n ...(result.toolActivity.length > 0\n ? [`\\n(${result.toolActivity.length} tool call(s))`]\n : []),\n ].join(\"\\n\");\n await linkDelegateSession({\n parentSessionID: context.sessionID,\n title: `Cursor delegate (${args.model})`,\n prompt: args.prompt,\n transcript,\n });\n }\n\n return {\n title: `Cursor delegate (${args.model})`,\n output: (result.text || \"(no text output)\") + toolNote,\n metadata: {\n agentId: result.agentId,\n model: args.model,\n toolCalls: result.toolActivity.length,\n usage: result.usage ?? null,\n },\n };\n },\n }),\n };\n}\n","import type { AgentModeOption, ConversationStep, InteractionUpdate } from \"@cursor/sdk\";\nimport { loadCursorSdk } from \"../cursor-runtime.js\";\nimport { buildModelSelection } from \"./controls.js\";\n\n/**\n * A target repository for a cloud agent. Cursor's cloud runtime accepts an\n * array of repos; the tool surface exposes the common single-repo case.\n */\nexport interface CloudRepoTarget {\n url: string;\n startingRef?: string;\n}\n\nexport interface CloudAgentParams {\n apiKey: string;\n /** The instruction/task for the background agent. */\n prompt: string;\n /** Target repository URL (e.g. https://github.com/owner/repo). */\n repoUrl: string;\n /** Branch/ref to start from. Defaults to the repo's default branch. */\n startingRef?: string;\n /** Cursor model id. Optional for cloud (server picks a default otherwise). */\n model?: string;\n /** Conversation mode; defaults to \"agent\". */\n mode?: AgentModeOption;\n /** Convenience for the Cursor `thinking` model param (e.g. \"high\"). */\n thinking?: string;\n /** When true, open a PR automatically once the agent finishes. */\n autoCreatePR?: boolean;\n /** Operate on the current branch instead of creating a new one. */\n workOnCurrentBranch?: boolean;\n /** Cancels the run when aborted (wired to the tool's abort signal). */\n abortSignal?: AbortSignal;\n}\n\nexport interface CloudAgentBranch {\n repoUrl: string;\n branch?: string;\n prUrl?: string;\n}\n\nexport interface CloudAgentResult {\n agentId: string;\n /** Terminal run status: \"finished\" | \"error\" | \"cancelled\". */\n status: string;\n /** The agent's final textual result, when present. */\n result?: string;\n /** First PR url found across result branches (when `autoCreatePR`). */\n prUrl?: string;\n /** Per-repo branch/PR info reported by the run. */\n branches: CloudAgentBranch[];\n durationMs?: number;\n /** Human-readable progress lines captured from status/step/summary updates. */\n progress: string[];\n}\n\n/**\n * Run a Cursor background (\"cloud\") agent against a remote repository and wait\n * for it to finish, returning the final status, result text, and any PR url.\n *\n * A cloud agent can run for minutes and produce a PR rather than a chat reply,\n * which maps poorly onto the synchronous provider `doStream` path — so this is\n * exposed as an opencode tool instead (see plugin/index.ts). Progress is\n * collected into `progress[]` (opencode custom tools return a single result\n * rather than a live stream) and the lifecycle is bridged through the same\n * `loadCursorSdk` plumbing the provider uses.\n */\nexport async function runCloudAgent(params: CloudAgentParams): Promise<CloudAgentResult> {\n const { Agent } = await loadCursorSdk();\n const modelSelection = params.model\n ? buildModelSelection(params.model, params.thinking ? { thinking: params.thinking } : undefined)\n : undefined;\n const mode: AgentModeOption = params.mode ?? \"agent\";\n\n const createOptions = {\n apiKey: params.apiKey,\n ...(modelSelection ? { model: modelSelection } : {}),\n mode,\n cloud: {\n repos: [\n {\n url: params.repoUrl,\n ...(params.startingRef ? { startingRef: params.startingRef } : {}),\n },\n ],\n ...(params.autoCreatePR !== undefined ? { autoCreatePR: params.autoCreatePR } : {}),\n ...(params.workOnCurrentBranch !== undefined\n ? { workOnCurrentBranch: params.workOnCurrentBranch }\n : {}),\n },\n };\n\n const progress: string[] = [];\n const agent = await Agent.create(createOptions);\n\n // `onDelta` carries fine-grained updates; for a cloud (background) run the\n // higher-signal progress arrives via `onStep` (whole conversation steps) and\n // `run.onDidChangeStatus`. We capture all three — whichever the runtime emits.\n const onDelta = ({ update }: { update: InteractionUpdate }) => {\n if (update.type === \"summary\") progress.push(`summary: ${update.summary}`);\n };\n\n const onStep = ({ step }: { step: ConversationStep }) => {\n progress.push(`step: ${describeStep(step)}`);\n };\n\n try {\n const run = await agent.send(params.prompt, { mode, onDelta, onStep });\n\n const off = run.onDidChangeStatus?.((status: string) => {\n progress.push(`status: ${status}`);\n });\n const onAbort = () => {\n run.cancel().catch(() => {});\n };\n params.abortSignal?.addEventListener(\"abort\", onAbort);\n\n try {\n const result = await run.wait();\n const branches: CloudAgentBranch[] = (result.git?.branches ?? []).map((b) => ({\n repoUrl: b.repoUrl,\n ...(b.branch ? { branch: b.branch } : {}),\n ...(b.prUrl ? { prUrl: b.prUrl } : {}),\n }));\n const prUrl = branches.find((b) => b.prUrl)?.prUrl;\n return {\n agentId: agent.agentId,\n status: result.status,\n ...(result.result !== undefined ? { result: result.result } : {}),\n ...(prUrl ? { prUrl } : {}),\n branches,\n ...(result.durationMs !== undefined ? { durationMs: result.durationMs } : {}),\n progress,\n };\n } finally {\n off?.();\n params.abortSignal?.removeEventListener(\"abort\", onAbort);\n }\n } finally {\n try {\n agent.close();\n } catch {\n // best effort; cloud agents persist server-side regardless.\n }\n }\n}\n\n/** A short, log-friendly description of a conversation step for progress output. */\nfunction describeStep(step: ConversationStep): string {\n if (step.type === \"toolCall\") return `toolCall:${step.message.type}`;\n return step.type;\n}\n","import type { AgentModeOption, SettingSource } from \"@cursor/sdk\";\nimport type { CursorUsage } from \"./agent-events.js\";\nimport { streamAgentTurn } from \"./agent-events.js\";\nimport { resolveControls } from \"./controls.js\";\nimport { acquireAgent } from \"./session-pool.js\";\n\nexport interface DelegateParams {\n\tapiKey: string;\n\t/** The subtask to delegate to the Cursor agent. */\n\tprompt: string;\n\t/** Cursor model id to run the delegation on. */\n\tmodel: string;\n\t/** Conversation mode; defaults to \"agent\". */\n\tmode?: AgentModeOption;\n\t/** Convenience for the Cursor `thinking` model param (e.g. \"high\"). */\n\tthinking?: string;\n\t/**\n\t * Working directory the local agent operates in. An array supplies\n\t * additional workspace roots, giving the agent multi-root access.\n\t */\n\tcwd: string | string[];\n\t/** Run the agent's tools inside Cursor's sandbox. */\n\tsandbox?: boolean;\n\t/** Resume a specific Cursor agent by id instead of creating a fresh one. */\n\tagentId?: string;\n\t/** Cancels the run when aborted (wired to the tool's abort signal). */\n\tabortSignal?: AbortSignal;\n\t/**\n\t * Cursor settings layers to load from disk. When omitted, defaults to\n\t * `[\"project\"]` so the delegate picks up `.cursor/skills/` and other\n\t * project-level config from its cwd. Pass an explicit array to override.\n\t */\n\tsettingSources?: SettingSource[];\n}\n\nexport interface DelegateToolActivity {\n\tname: string;\n\tisError: boolean;\n}\n\nexport interface DelegateResult {\n\tagentId: string;\n\ttext: string;\n\treasoning: string;\n\ttoolActivity: DelegateToolActivity[];\n\tusage?: CursorUsage;\n}\n\n/**\n * Run a single delegated turn on a fresh (or explicitly resumed) local Cursor\n * agent and aggregate the outcome into a plain result. This backs the opt-in\n * `cursor_delegate` tool, which gives users a permission-gated boundary around\n * Cursor (the provider path runs Cursor's own loop without per-call gating).\n *\n * Reuses the provider's `acquireAgent` + `streamAgentTurn` plumbing; the turn\n * is consumed eagerly here because a tool returns a single result rather than a\n * live stream.\n */\nexport async function runDelegate(\n\tparams: DelegateParams,\n): Promise<DelegateResult> {\n\tconst { mode, modelSelection } = resolveControls(\n\t\tparams.model,\n\t\t{\n\t\t\tmode: params.mode ?? \"agent\",\n\t\t\t...(params.thinking ? { params: { thinking: params.thinking } } : {}),\n\t\t},\n\t\tundefined,\n\t);\n\n\tconst acquired = await acquireAgent({\n\t\tapiKey: params.apiKey,\n\t\tmodelSelection,\n\t\tmode,\n\t\tcwd: params.cwd,\n\t\t// Default to the \"project\" settings layer so the delegate picks up\n\t\t// `.cursor/skills/` and other project-level config from its cwd. An\n\t\t// explicit `settingSources` from the caller overrides this default.\n\t\tsettingSources: params.settingSources ?? [\"project\"],\n\t\t...(params.sandbox !== undefined ? { sandbox: params.sandbox } : {}),\n\t\t...(params.agentId ? { resumeAgentId: params.agentId } : {}),\n\t});\n\n\tconst text: string[] = [];\n\tconst reasoning: string[] = [];\n\tconst toolActivity: DelegateToolActivity[] = [];\n\tlet usage: CursorUsage | undefined;\n\n\ttry {\n\t\tfor await (const event of streamAgentTurn(\n\t\t\tacquired.agent,\n\t\t\t{ text: params.prompt },\n\t\t\t{\n\t\t\t\tmode,\n\t\t\t\t...(params.abortSignal ? { abortSignal: params.abortSignal } : {}),\n\t\t\t},\n\t\t)) {\n\t\t\tswitch (event.type) {\n\t\t\t\tcase \"text-delta\":\n\t\t\t\t\ttext.push(event.text);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"reasoning-delta\":\n\t\t\t\t\treasoning.push(event.text);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"tool-input-partial\":\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"tool-call\":\n\t\t\t\t\ttoolActivity.push({ name: event.name, isError: false });\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"tool-result\":\n\t\t\t\t\tif (event.isError)\n\t\t\t\t\t\ttoolActivity.push({ name: event.name, isError: true });\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"usage\":\n\t\t\t\t\tusage = event.usage;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"reasoning-complete\":\n\t\t\t\tcase \"compaction\":\n\t\t\t\tcase \"subagent-event\":\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"finish\":\n\t\t\t\t\t// The aggregated result text; prefer it when deltas were absent.\n\t\t\t\t\tif (event.text && text.length === 0) text.push(event.text);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t} finally {\n\t\tacquired.release();\n\t}\n\n\treturn {\n\t\tagentId: acquired.agent.agentId,\n\t\ttext: text.join(\"\"),\n\t\treasoning: reasoning.join(\"\"),\n\t\ttoolActivity,\n\t\t...(usage ? { usage } : {}),\n\t};\n}\n","import { createRequire } from \"node:module\";\nimport { homedir, tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { get } from \"node:https\";\nimport { mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport semver from \"semver\";\n\n/**\n * Inlined by tsup's `define` option in the published bundle (see\n * tsup.config.ts). In the bundle, a relative require of `../package.json`\n * would resolve inside `dist/` where no package.json exists, so the version\n * must be baked in at build time. When running un-bundled (tests against\n * `src/`), this stays undefined and `getLocalVersion` falls back to reading\n * package.json.\n */\ndeclare const __PKG_VERSION__: string | undefined;\n\nconst PACKAGE_NAME = \"@stablekernel/opencode-cursor\";\nconst REGISTRY_URL = `https://registry.npmjs.org/${encodeURIComponent(PACKAGE_NAME)}/latest`;\n\n/**\n * The path where opencode caches the installed plugin package.\n * Used by `warnIfStale` (to build the removal command) and by the\n * `cursor_update_plugin` tool (to actually clear the cache) — single source\n * of truth so both stay in sync.\n */\nexport const PLUGIN_CACHE_PATH =\n\tprocess.platform === \"win32\"\n\t\t? join(\n\t\t\t\tprocess.env.LocalAppData ?? join(homedir(), \"AppData\", \"Local\"),\n\t\t\t\t\"opencode\",\n\t\t\t\t\"cache\",\n\t\t\t\t\"packages\",\n\t\t\t\t\"@stablekernel\",\n\t\t\t\t\"opencode-cursor@latest\",\n\t\t )\n\t\t: join(homedir(), \".cache\", \"opencode\", \"packages\", `${PACKAGE_NAME}@latest`);\nconst CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;\n// Failed fetches are retried sooner than successful ones so a transient\n// network error doesn't suppress the check for a full day.\nconst FAILURE_TTL_MS = 60 * 60 * 1000;\nconst REQUEST_TIMEOUT_MS = 5000;\n\ninterface VersionCheckCache {\n\tcheckedAt: number;\n\tlatest: string | undefined;\n}\n\nfunction cacheDir(): string {\n\tconst base =\n\t\tprocess.env.XDG_CACHE_HOME?.trim() ||\n\t\t(homedir() ? join(homedir(), \".cache\") : tmpdir());\n\treturn join(base, \"opencode-cursor\");\n}\n\nfunction cacheFile(): string {\n\treturn join(cacheDir(), \"version-check.json\");\n}\n\nfunction readCache(): VersionCheckCache | undefined {\n\ttry {\n\t\tconst parsed = JSON.parse(readFileSync(cacheFile(), \"utf8\")) as VersionCheckCache;\n\t\tif (typeof parsed.checkedAt === \"number\") return parsed;\n\t} catch {\n\t\t// ignore\n\t}\n\treturn undefined;\n}\n\nfunction writeCache(latest: string | undefined): void {\n\ttry {\n\t\tmkdirSync(cacheDir(), { recursive: true });\n\t\twriteFileSync(\n\t\t\tcacheFile(),\n\t\t\tJSON.stringify({ checkedAt: Date.now(), latest }),\n\t\t\t\"utf8\",\n\t\t);\n\t} catch {\n\t\t// Best-effort; never block plugin init.\n\t}\n}\n\n/** Remove the on-disk version-check cache so the next startup re-fetches from npm. */\nexport function clearVersionCache(): void {\n\ttry {\n\t\trmSync(cacheFile(), { force: true });\n\t} catch {\n\t\t// Best-effort.\n\t}\n}\n\nexport function getLocalVersion(): string | undefined {\n\t// Build-time inlined version (published bundle path).\n\tif (typeof __PKG_VERSION__ === \"string\") return __PKG_VERSION__;\n\t// Un-bundled fallback: resolve package.json relative to this source file.\n\ttry {\n\t\tconst require = createRequire(import.meta.url);\n\t\tconst pkg = require(\"../package.json\") as { version: string };\n\t\treturn pkg.version;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction fetchLatestVersion(): Promise<string | undefined> {\n\treturn new Promise((resolve) => {\n\t\tconst req = get(\n\t\t\tREGISTRY_URL,\n\t\t\t{ headers: { Accept: \"application/json\", Connection: \"close\" } },\n\t\t\t(res) => {\n\t\t\t\tif (res.statusCode !== 200) {\n\t\t\t\t\tres.resume();\n\t\t\t\t\tresolve(undefined);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tlet body = \"\";\n\t\t\t\tres.setEncoding(\"utf8\");\n\t\t\t\tres.on(\"data\", (chunk: string) => {\n\t\t\t\t\tbody += chunk;\n\t\t\t\t});\n\t\t\t\tres.on(\"end\", () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst parsed = JSON.parse(body) as { version?: string };\n\t\t\t\t\t\tresolve(parsed.version);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tresolve(undefined);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tres.on(\"error\", () => resolve(undefined));\n\t\t\t},\n\t\t);\n\t\treq.setTimeout(REQUEST_TIMEOUT_MS, () => {\n\t\t\treq.destroy();\n\t\t\tresolve(undefined);\n\t\t});\n\t\treq.on(\"error\", () => resolve(undefined));\n\t});\n}\n\n/** Return the cached latest version if fresh, else fetch from npm. */\nexport async function getLatestVersion(): Promise<string | undefined> {\n\tconst cached = readCache();\n\tif (cached) {\n\t\t// Successful lookups are trusted for 24h; failures only briefly.\n\t\tconst ttl = cached.latest ? CHECK_INTERVAL_MS : FAILURE_TTL_MS;\n\t\tif (Date.now() - cached.checkedAt < ttl) return cached.latest;\n\t}\n\tconst latest = await fetchLatestVersion();\n\twriteCache(latest);\n\treturn latest;\n}\n\n/**\n * Check whether this installed plugin is older than the registry's `latest`\n * tag. opencode resolves `@latest` once and then never reinstalls the plugin,\n * so users can silently stay on old versions.\n *\n * The registry fetch is throttled to once per 24h via an on-disk cache.\n * Staleness is surfaced via the UI toast (plugin/index.ts); no terminal output\n * is emitted. Set CI or NO_UPDATE_NOTIFIER to skip the check entirely.\n *\n * @param prefetchedLatest - Optional already-resolved latest version string.\n * Pass this when the caller has already awaited `getLatestVersion()` so the\n * registry is only fetched once per startup rather than twice.\n */\nexport async function warnIfStale(prefetchedLatest?: string): Promise<void> {\n\tif (process.env.CI || process.env.NO_UPDATE_NOTIFIER) return;\n\n\tconst local = getLocalVersion();\n\tif (!local || !semver.valid(local)) return;\n\tconst latest = prefetchedLatest ?? (await getLatestVersion());\n\tif (!latest || !semver.valid(latest)) return;\n\tif (!semver.gt(latest, local)) return;\n\n\t// Update notice is surfaced via the UI toast (plugin/index.ts); no\n\t// terminal output needed.\n}\n","import {\n\tmkdirSync,\n\twriteFileSync,\n\treadFileSync,\n\texistsSync,\n\trmSync,\n\treaddirSync,\n\tstatSync,\n\tcopyFileSync,\n\trealpathSync,\n} from \"node:fs\";\nimport type { Dirent } from \"node:fs\";\nimport { join, relative, dirname } from \"node:path\";\nimport { entryKind } from \"../plugin/skill-discovery.js\";\nimport type { DiscoveredSkill } from \"../plugin/skill-discovery.js\";\n\n/** Location of the generated mirror, relative to the agent's cwd. */\nconst SKILLS_DIR = join(\".cursor\", \"skills\");\nconst IGNORE_FILE = \".gitignore\";\n\n/**\n * Frontmatter sentinel marking mirrored skills as generated by this plugin.\n * Only directories whose `SKILL.md` carries it are ever overwritten or\n * deleted, so a user-owned `.cursor/skills/<id>` is never clobbered.\n */\nconst SENTINEL = \"generated: opencode-cursor\";\n\n/** Max bytes per individual supporting file (skip oversized files, not the whole skill). */\nconst MAX_FILE_BYTES = 1_048_576; // 1 MB\n\n/** Max total mirrored bytes across all skills. */\nconst MAX_TOTAL_BYTES = 10_485_760; // 10 MB\n\n/** Outcome of a {@link writeSkillMirror} attempt. */\nexport type SkillMirrorWrite =\n\t/** One or more skill directories were created or updated. */\n\t| \"written\"\n\t/** All skills already mirrored with identical content; no writes. */\n\t| \"unchanged\"\n\t/** No skills to mirror. */\n\t| \"empty\"\n\t/** A write error occurred; some skills may be unavailable. */\n\t| \"partial\";\n\n/** True when the file carries the generated-by sentinel in its frontmatter. */\nfunction isGenerated(content: string): boolean {\n\tif (!content.startsWith(\"---\")) return false;\n\tconst end = content.indexOf(\"\\n---\", 3);\n\tconst frontmatter = end === -1 ? content : content.slice(0, end);\n\treturn frontmatter.split(/\\r?\\n/).includes(SENTINEL);\n}\n\n/** Inject the sentinel into frontmatter, preserving name and description. */\nfunction stampSentinel(content: string): string {\n\tif (!content.startsWith(\"---\")) {\n\t\t// No frontmatter at all — shouldn't happen (discovery requires it),\n\t\t// but handle gracefully by wrapping the whole content.\n\t\treturn `---\\n${SENTINEL}\\n---\\n\\n${content}`;\n\t}\n\tconst end = content.indexOf(\"\\n---\", 3);\n\tif (end === -1) {\n\t\treturn `---\\n${SENTINEL}\\n${content.slice(3)}`;\n\t}\n\tconst frontmatter = content.slice(3, end);\n\t// Already has the sentinel — return as-is.\n\tif (frontmatter.includes(SENTINEL)) return content;\n\t// Insert sentinel before the closing ---.\n\treturn `---${frontmatter}\\n${SENTINEL}\\n${content.slice(end)}`;\n}\n\n/** Recursively copy a file tree, skipping oversized files. Returns bytes copied + skipped files. */\nfunction copyTree(\n\tsrcDir: string,\n\tdestDir: string,\n\tskillId: string,\n\tmaxBytes: number,\n\twarn: (message: string) => void,\n): { bytes: number; skipped: string[] } {\n\tlet bytes = 0;\n\tconst skipped: string[] = [];\n\t// Following symlinked directories admits cycles; track resolved paths.\n\tconst visited = new Set<string>();\n\n\tfunction walk(src: string, dest: string) {\n\t\tlet realSrc: string;\n\t\ttry {\n\t\t\trealSrc = realpathSync(src);\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tif (visited.has(realSrc)) return;\n\t\tvisited.add(realSrc);\n\t\tlet entries;\n\t\ttry {\n\t\t\tentries = readdirSync(src, { withFileTypes: true });\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tfor (const entry of entries) {\n\t\t\tconst srcPath = join(src, entry.name);\n\t\t\tconst destPath = join(dest, entry.name);\n\t\t\tconst kind = entryKind(entry, srcPath);\n\t\t\tif (kind === \"dir\") {\n\t\t\t\twalk(srcPath, destPath);\n\t\t\t} else if (kind === \"file\") {\n\t\t\t\t// Never copy SKILL.md here — it's already written with the\n\t\t\t\t// sentinel stamped by the caller. Copying the original would\n\t\t\t\t// overwrite the stamped version.\n\t\t\t\tif (entry.name === \"SKILL.md\") continue;\n\t\t\t\ttry {\n\t\t\t\t\tconst size = statSync(srcPath).size;\n\t\t\t\t\tif (size > MAX_FILE_BYTES) {\n\t\t\t\t\t\tskipped.push(relative(srcDir, srcPath));\n\t\t\t\t\t\twarn(\n\t\t\t\t\t\t\t`Skill \"${skillId}\": skipped oversized file \"${relative(srcDir, srcPath)}\" (${size} bytes > ${MAX_FILE_BYTES} limit). The rest of the skill is still mirrored.`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (bytes + size > maxBytes) {\n\t\t\t\t\t\tskipped.push(relative(srcDir, srcPath));\n\t\t\t\t\t\twarn(\n\t\t\t\t\t\t\t`Skill \"${skillId}\": skipped file \"${relative(srcDir, srcPath)}\" because the mirror's ${MAX_TOTAL_BYTES}-byte total size limit would be exceeded.`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tmkdirSync(dirname(destPath), { recursive: true });\n\t\t\t\t\tcopyFileSync(srcPath, destPath);\n\t\t\t\t\tbytes += size;\n\t\t\t\t} catch {\n\t\t\t\t\t// Best effort — skip unreadable files.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\twalk(srcDir, destDir);\n\treturn { bytes, skipped };\n}\n\n/** Compare file contents and skip the write when unchanged. */\nfunction writeIfChanged(path: string, content: string): boolean {\n\tconst existing = existsSync(path) ? readFileSync(path, \"utf8\") : undefined;\n\tif (existing === content) return false;\n\tmkdirSync(dirname(path), { recursive: true });\n\twriteFileSync(path, content, \"utf8\");\n\treturn true;\n}\n\n/**\n * Keep the generated skills out of git via `.cursor/skills/.gitignore` (which\n * also ignores itself so it doesn't pollute `git status`). Lists each mirrored\n * skill directory by id plus the `.gitignore` itself.\n */\nfunction ensureGitIgnored(dir: string, skillIds: string[]): void {\n\tconst path = join(dir, IGNORE_FILE);\n\tconst existing = existsSync(path) ? readFileSync(path, \"utf8\") : \"\";\n\tconst lines = existing.split(/\\r?\\n/);\n\tconst missing = [...skillIds, IGNORE_FILE].filter(\n\t\t(entry) => !lines.includes(entry),\n\t);\n\tif (missing.length === 0) return;\n\tconst prefix =\n\t\texisting && !existing.endsWith(\"\\n\") ? `${existing}\\n` : existing;\n\twriteFileSync(path, `${prefix}${missing.join(\"\\n\")}\\n`, \"utf8\");\n}\n\n/**\n * Materialise a git-ignored mirror of opencode's resolved skills into\n * `<cwd>/.cursor/skills/`, which Cursor discovers natively when the `project`\n * settings layer is loaded.\n *\n * Each skill is written to `<cwd>/.cursor/skills/<id>/SKILL.md` with a\n * `generated: opencode-cursor` sentinel stamped into the frontmatter.\n * Supporting files are copied alongside, preserving relative paths. A\n * user-owned skill directory (sentinel-less `SKILL.md`) is never overwritten —\n * it's skipped with a warning. Oversized individual files are skipped (not the\n * whole skill). Writes are idempotent (content compared before writing).\n * Sentinel-bearing directories for skills that no longer resolve are pruned.\n *\n * Never throws. Every failure degrades to \"this skill is unavailable this\n * turn\" plus a warning, mirroring how `resolveSystemDelivery` falls back.\n */\nexport function writeSkillMirror(\n\tcwd: string,\n\tskills: DiscoveredSkill[],\n\twarn: (message: string) => void,\n): SkillMirrorWrite {\n\tif (skills.length === 0) {\n\t\t// Still prune stale sentinel-bearing dirs.\n\t\ttry {\n\t\t\tpruneStale(cwd, new Set(), warn);\n\t\t} catch {\n\t\t\t// best effort\n\t\t}\n\t\treturn \"empty\";\n\t}\n\n\tconst dir = join(cwd, SKILLS_DIR);\n\tlet totalBytes = 0;\n\tlet wroteAny = false;\n\tlet partial = false;\n\tconst mirroredIds: string[] = [];\n\n\tfor (const skill of skills) {\n\t\tif (totalBytes >= MAX_TOTAL_BYTES) {\n\t\t\twarn(\n\t\t\t\t`Skill mirror: total size limit (${MAX_TOTAL_BYTES} bytes) reached; skipping remaining skills: ${skills\n\t\t\t\t\t.filter((s) => !mirroredIds.includes(s.id))\n\t\t\t\t\t.map((s) => s.id)\n\t\t\t\t\t.join(\", \")}`,\n\t\t\t);\n\t\t\tbreak;\n\t\t}\n\n\t\tconst skillDir = join(dir, skill.id);\n\t\tconst skillMdPath = join(skillDir, \"SKILL.md\");\n\n\t\t// Check for user-owned skill (sentinel-less SKILL.md).\n\t\tif (existsSync(skillMdPath)) {\n\t\t\ttry {\n\t\t\t\tconst existing = readFileSync(skillMdPath, \"utf8\");\n\t\t\t\tif (!isGenerated(existing)) {\n\t\t\t\t\twarn(\n\t\t\t\t\t\t`.cursor/skills/${skill.id}/SKILL.md exists but was not generated by opencode-cursor; leaving it untouched and skipping this skill.`,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Can't read — assume it's ours and proceed.\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\t// Read and stamp the SKILL.md.\n\t\t\tconst sourceContent = readFileSync(\n\t\t\t\tjoin(skill.sourceDir, \"SKILL.md\"),\n\t\t\t\t\"utf8\",\n\t\t\t);\n\t\t\tconst stamped = stampSentinel(sourceContent);\n\t\t\tconst skillMdBytes = Buffer.byteLength(stamped);\n\t\t\tif (totalBytes + skillMdBytes > MAX_TOTAL_BYTES) {\n\t\t\t\twarn(\n\t\t\t\t\t`Skill mirror: total size limit (${MAX_TOTAL_BYTES} bytes) reached; skipping skill \"${skill.id}\" and all remaining skills.`,\n\t\t\t\t);\n\t\t\t\tpartial = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst changed = writeIfChanged(skillMdPath, stamped);\n\t\t\tif (changed) wroteAny = true;\n\t\t\ttotalBytes += skillMdBytes;\n\n\t\t\t// Copy supporting files.\n\t\t\tconst { bytes, skipped } = copyTree(\n\t\t\t\t\tskill.sourceDir,\n\t\t\t\t\tskillDir,\n\t\t\t\t\tskill.id,\n\t\t\t\t\tMAX_TOTAL_BYTES - totalBytes,\n\t\t\t\t\twarn,\n\t\t\t\t);\n\t\t\ttotalBytes += bytes;\n\t\t\tif (skipped.length > 0) partial = true;\n\n\t\t\tmirroredIds.push(skill.id);\n\t\t} catch (error) {\n\t\t\tpartial = true;\n\t\t\twarn(\n\t\t\t\t`Failed to mirror skill \"${skill.id}\": ${error instanceof Error ? error.message : String(error)}. This skill will be unavailable this turn.`,\n\t\t\t);\n\t\t}\n\t}\n\n\t// Prune stale sentinel-bearing dirs for skills no longer in the set.\n\ttry {\n\t\tconst staleRemoved = pruneStale(\n\t\t\tcwd,\n\t\t\tnew Set(mirroredIds),\n\t\t\twarn,\n\t\t);\n\t\tif (staleRemoved) wroteAny = true;\n\t} catch {\n\t\t// best effort\n\t}\n\n\t// Update .gitignore with the current set of mirrored skill ids.\n\tif (mirroredIds.length > 0) {\n\t\ttry {\n\t\t\tensureGitIgnored(dir, mirroredIds);\n\t\t} catch {\n\t\t\t// non-fatal\n\t\t}\n\t}\n\n\tif (partial) return \"partial\";\n\treturn wroteAny ? \"written\" : \"unchanged\";\n}\n\n/**\n * Remove sentinel-bearing skill directories for skills that no longer resolve.\n * User-owned directories (no sentinel) are left in place. Returns true if any\n * directories were removed.\n */\nfunction pruneStale(\n\tcwd: string,\n\tcurrentIds: Set<string>,\n\twarn: (message: string) => void,\n): boolean {\n\tconst dir = join(cwd, SKILLS_DIR);\n\tif (!existsSync(dir)) return false;\n\tlet removed = false;\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = readdirSync(dir, { withFileTypes: true });\n\t} catch {\n\t\treturn false;\n\t}\n\tfor (const entry of entries) {\n\t\tif (!entry.isDirectory()) continue;\n\t\tif (entry.name === \".\" || entry.name === \"..\") continue;\n\t\tif (currentIds.has(entry.name)) continue;\n\t\tconst skillMdPath = join(dir, entry.name, \"SKILL.md\");\n\t\tif (!existsSync(skillMdPath)) continue;\n\t\ttry {\n\t\t\tconst content = readFileSync(skillMdPath, \"utf8\");\n\t\t\tif (isGenerated(content)) {\n\t\t\t\trmSync(join(dir, entry.name), { recursive: true, force: true });\n\t\t\t\tremoved = true;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Can't read — leave it alone.\n\t\t}\n\t}\n\treturn removed;\n}\n\n/**\n * Remove the entire generated skill mirror (best-effort); used on plugin\n * dispose. Only deletes directories whose `SKILL.md` carries the sentinel —\n * user-owned `.cursor/skills/<id>` directories are left in place. Also\n * removes the `.gitignore` if it was generated (contains the sentinel pattern).\n */\nexport function removeSkillMirror(cwd: string): void {\n\tconst dir = join(cwd, SKILLS_DIR);\n\tif (!existsSync(dir)) return;\n\ttry {\n\t\tconst entries = readdirSync(dir, { withFileTypes: true });\n\t\tfor (const entry of entries) {\n\t\t\tif (!entry.isDirectory()) continue;\n\t\t\tconst skillMdPath = join(dir, entry.name, \"SKILL.md\");\n\t\t\tif (!existsSync(skillMdPath)) continue;\n\t\t\ttry {\n\t\t\t\tif (isGenerated(readFileSync(skillMdPath, \"utf8\"))) {\n\t\t\t\t\trmSync(join(dir, entry.name), { recursive: true, force: true });\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// best effort\n\t\t\t}\n\t\t}\n\t\t// Remove the .gitignore if all sentinel dirs are gone.\n\t\tconst remaining = readdirSync(dir, { withFileTypes: true });\n\t\tconst hasSentinelDir = remaining.some((e) => {\n\t\t\tif (!e.isDirectory()) return false;\n\t\t\tconst p = join(dir, e.name, \"SKILL.md\");\n\t\t\tif (!existsSync(p)) return false;\n\t\t\ttry {\n\t\t\t\treturn isGenerated(readFileSync(p, \"utf8\"));\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t});\n\t\tif (!hasSentinelDir) {\n\t\t\tconst ignorePath = join(dir, IGNORE_FILE);\n\t\t\tif (existsSync(ignorePath)) {\n\t\t\t\ttry {\n\t\t\t\t\trmSync(ignorePath);\n\t\t\t\t} catch {\n\t\t\t\t\t// best effort\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// best effort — already gone or unreadable\n\t}\n}\n\n/**\n * Build the `<available_skills>` catalogue text for the system rule. Lists each\n * skill's id and description, plus a one-line instruction to load the matching\n * skill file before starting relevant work. Returns undefined when no skills\n * are mirrored (so the caller can skip appending an empty section).\n */\nexport function buildSkillsCatalogue(\n\tskills: DiscoveredSkill[],\n): string | undefined {\n\tif (skills.length === 0) return undefined;\n\tconst lines = skills.map(\n\t\t(s) => `- **${s.id}**: ${s.description}`,\n\t);\n\treturn [\n\t\t\"<available_skills>\",\n\t\t\"The following skills are available. Load the matching skill from `.cursor/skills/<id>/SKILL.md` before starting relevant work:\",\n\t\t\"\",\n\t\t...lines,\n\t\t\"</available_skills>\",\n\t].join(\"\\n\");\n}\n","import {\n\treaddirSync,\n\treadFileSync,\n\tstatSync,\n\texistsSync,\n\trealpathSync,\n\tmkdtempSync,\n\tmkdirSync,\n\trmSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport type { Dirent } from \"node:fs\";\nimport {\n\tjoin,\n\trelative,\n\tdirname,\n\tresolve as resolvePath,\n\tisAbsolute,\n} from \"node:path\";\nimport { homedir, tmpdir } from \"node:os\";\nimport { execSync } from \"node:child_process\";\nimport type { Config } from \"@opencode-ai/plugin\";\n\n/** A skill discovered from the filesystem, ready for permission filtering. */\nexport interface DiscoveredSkill {\n\t/** Skill id (the `name` field from frontmatter, also the directory name). */\n\tid: string;\n\t/** Human-readable name from frontmatter. */\n\tname: string;\n\t/** Description from frontmatter — used by Cursor for on-demand loading. */\n\tdescription: string;\n\t/** Absolute path to the skill's source directory (containing SKILL.md). */\n\tsourceDir: string;\n\t/** Relative paths of supporting files alongside SKILL.md (not SKILL.md itself). */\n\tfiles: string[];\n}\n\n/** Outcome of discovery + permission filtering. */\nexport interface ResolvedSkills {\n\t/** Skills permitted to mirror. */\n\tskills: DiscoveredSkill[];\n\t/** Skills withheld and why (for logging / user notification). */\n\twithheld: Array<{ id: string; reason: string }>;\n}\n\n/** Manual include/exclude override from plugin options. */\nexport interface SkillFilterOptions {\n\tinclude?: string[];\n\texclude?: string[];\n}\n\n// --- Frontmatter parsing ---\n\n/** Parse the small recognised frontmatter field set (name, description). */\nfunction parseFrontmatter(content: string): {\n\tname?: string;\n\tdescription?: string;\n} {\n\tif (!content.startsWith(\"---\")) return {};\n\tconst end = content.indexOf(\"\\n---\", 3);\n\tif (end === -1) return {};\n\tconst frontmatter = content.slice(3, end);\n\tconst result: { name?: string; description?: string } = {};\n\tfor (const line of frontmatter.split(/\\r?\\n/)) {\n\t\tconst trimmed = line.trim();\n\t\tif (!trimmed || trimmed.startsWith(\"#\")) continue;\n\t\tconst colon = trimmed.indexOf(\":\");\n\t\tif (colon === -1) continue;\n\t\tconst key = trimmed.slice(0, colon).trim();\n\t\tlet value = trimmed.slice(colon + 1).trim();\n\t\t// Strip surrounding quotes if present.\n\t\tif (\n\t\t\t(value.startsWith('\"') && value.endsWith('\"')) ||\n\t\t\t(value.startsWith(\"'\") && value.endsWith(\"'\"))\n\t\t) {\n\t\t\tvalue = value.slice(1, -1);\n\t\t}\n\t\tif (key === \"name\") result.name = value;\n\t\telse if (key === \"description\") result.description = value;\n\t}\n\treturn result;\n}\n\n// --- Filesystem walk ---\n\n/** Directory names under each config root that may contain skills. */\nconst SKILL_DIR_NAMES = [\"skill\", \"skills\"];\n\n/** External (non-opencode) config roots that contain a `skills/` subdir. */\nconst EXTERNAL_DIR_NAMES = [\".claude\", \".agents\"];\n\n/**\n * Directory names, inside each opencode config root, that may hold file-based\n * plugins (`<name>.ts`). Skills bundled alongside file plugins live in sibling\n * skill dirs.\n */\nconst FILE_PLUGIN_DIR_NAMES = [\"plugin\", \"plugins\"];\n\n/**\n * Directory names under each opencode plugin package root that may contain\n * skills. Both spellings are accepted because the repo's general skill scans\n * use `skill/` and `skills/` interchangeably.\n */\nconst PLUGIN_SKILL_DIR_NAMES = [\"skills\", \"skill\"];\n\n/**\n * Resolve the root where opencode caches installed plugin packages: plugins\n * listed in `plugin: []` are installed here (npm or git specs). Skills bundled\n * inside such a package live under `node_modules/<pkg>/skills/`.\n *\n * Layout matches opencode's own cache location logic and mirrors the\n * existing helper in `version-check.ts` (`PLUGIN_CACHE_PATH`).\n */\nexport function opencodePackagesRoot(home = homedir()): string {\n\tif (process.platform === \"win32\") {\n\t\treturn join(\n\t\t\tprocess.env.LocalAppData ?? join(home, \"AppData\", \"Local\"),\n\t\t\t\"opencode\",\n\t\t\t\"cache\",\n\t\t\t\"packages\",\n\t\t);\n\t}\n\treturn join(\n\t\tprocess.env.XDG_CACHE_HOME ?? join(home, \".cache\"),\n\t\t\"opencode\",\n\t\t\"packages\",\n\t);\n}\n\n/**\n * Collect the `skills/`-style directories inside a cache entry. Handles the\n * layouts observed in real caches:\n *\n * - flat packages: `<entry>/node_modules/<pkg>/skills/`\n * - scoped packages: `<entry>/node_modules/@scope/<pkg>/skills/`\n * - git specs: the spec dir nests (`spec@git+https:/github.com/owner/repo.git`)\n * before the `node_modules` install dir; found by a bounded\n * downward walk.\n *\n * Follows symlinks (real caches symlink the installed package into\n * node_modules). Never throws.\n */\nexport function pluginCacheSkillDirs(entry: string): string[] {\n\tconst dirs: string[] = [];\n\tconst visited = new Set<string>();\n\n\t/** Scan one `node_modules` dir: each child is a package root. */\n\tfunction scanNodeModules(nodeModules: string): void {\n\t\tlet entries: Dirent[];\n\t\ttry {\n\t\t\tentries = readdirSync(nodeModules, { withFileTypes: true });\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tfor (const ent of entries) {\n\t\t\tif (ent.name === \".bin\") continue;\n\t\t\tconst fullPath = join(nodeModules, ent.name);\n\t\t\tif (entryKind(ent, fullPath) !== \"dir\") continue;\n\t\t\tif (ent.name.startsWith(\"@\")) {\n\t\t\t\t// Scope dir: its children are package roots.\n\t\t\t\tscanNodeModules(fullPath);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (const skillName of SKILL_DIR_NAMES) {\n\t\t\t\tconst candidate = join(fullPath, skillName);\n\t\t\t\tif (existsSync(candidate)) dirs.push(candidate);\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Walk down from the entry dir (bounded) to find `node_modules`. */\n\tfunction findNodeModules(dir: string, depth: number): void {\n\t\tif (depth > 5) return;\n\t\tlet realDir: string;\n\t\ttry {\n\t\t\trealDir = realpathSync(dir);\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tif (visited.has(realDir)) return;\n\t\tvisited.add(realDir);\n\t\tconst nm = join(dir, \"node_modules\");\n\t\tif (existsSync(nm)) {\n\t\t\tscanNodeModules(nm);\n\t\t\treturn;\n\t\t}\n\t\tlet entries: Dirent[];\n\t\ttry {\n\t\t\tentries = readdirSync(dir, { withFileTypes: true });\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tfor (const ent of entries) {\n\t\t\tconst fullPath = join(dir, ent.name);\n\t\t\tif (entryKind(ent, fullPath) === \"dir\") {\n\t\t\t\tfindNodeModules(fullPath, depth + 1);\n\t\t\t}\n\t\t}\n\t}\n\n\tfindNodeModules(entry, 0);\n\treturn dirs;\n}\n\n/**\n * Enumerate every plugin cache entry that may contain skills. Each entry is a\n * directory in the opencode packages root (npm specs like `name@latest` or\n * `@scope/name@latest`, git specs like `superpowers@git+https:...`, or bare\n * dirs). Top-level `node_modules` and package/lock files are skipped; the\n * remaining dirs are scanned for `skills/` regardless — non-plugin cache\n * entries (language servers, formatters) simply have none, and the README\n * documents that the cache also holds such tooling.\n */\nexport function pluginCacheEntries(root: string): string[] {\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = readdirSync(root, { withFileTypes: true });\n\t} catch {\n\t\treturn [];\n\t}\n\tconst skip = new Set([\n\t\t\"node_modules\",\n\t\t\"package.json\",\n\t\t\"package-lock.json\",\n\t\t\"bun.lock\",\n\t\t\"bun.lockb\",\n\t]);\n\tconst out: string[] = [];\n\tfor (const ent of entries) {\n\t\tif (skip.has(ent.name)) continue;\n\t\tconst full = join(root, ent.name);\n\t\tif (entryKind(ent, full) !== \"dir\") continue;\n\t\tout.push(full);\n\t}\n\treturn out;\n}\n\n/**\n * Discover `skills/` dirs that ship inside opencode's plugin cache. Lowest\n * priority source: project/global/configured paths always win on duplicate ids\n * (first-wins ordering in {@link discoverSkills}).\n */\nexport function discoverPluginSkillDirs(\n\tcacheRoot?: string,\n\thome?: string,\n): string[] {\n\tconst root = cacheRoot ?? opencodePackagesRoot(home);\n\tif (!existsSync(root)) return [];\n\tconst dirs: string[] = [];\n\tfor (const entry of pluginCacheEntries(root)) {\n\t\tfor (const skillDir of pluginCacheSkillDirs(entry)) {\n\t\t\tdirs.push(skillDir);\n\t\t}\n\t}\n\treturn dirs;\n}\n\n/**\n * Find the git worktree root by walking up from `cwd`. Falls back to `cwd`\n * itself when not in a git repo (so a non-git project still discovers skills\n * in its own `.opencode/skills/`).\n */\nfunction worktreeRoot(cwd: string): string {\n\ttry {\n\t\tconst root = execSync(\"git rev-parse --show-toplevel\", {\n\t\t\tcwd,\n\t\t\tencoding: \"utf8\",\n\t\t\tstdio: [\"pipe\", \"pipe\", \"pipe\"],\n\t\t\ttimeout: 3000,\n\t\t}).trim();\n\t\treturn root || cwd;\n\t} catch {\n\t\treturn cwd;\n\t}\n}\n\n/** Walk up from `start` to `stop` (inclusive), yielding each directory. */\nfunction* walkUp(start: string, stop: string): Generator<string> {\n\tlet current = start;\n\twhile (current) {\n\t\tyield current;\n\t\tif (current === stop) break;\n\t\tconst parent = dirname(current);\n\t\tif (parent === current) break;\n\t\tcurrent = parent;\n\t}\n}\n\n/** List immediate subdirectories of `dir` that contain a `SKILL.md`. */\nfunction scanSkillDir(dir: string): Array<{ id: string; sourceDir: string }> {\n\tif (!existsSync(dir)) return [];\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = readdirSync(dir, { withFileTypes: true });\n\t} catch {\n\t\treturn [];\n\t}\n\tconst found: Array<{ id: string; sourceDir: string }> = [];\n\tfor (const entry of entries) {\n\t\t// Symlinks are admitted here rather than filtered: `Dirent.isDirectory()`\n\t\t// is false for a symlink pointing at a directory, which would silently\n\t\t// drop skills linked in from a shared checkout. The `SKILL.md` check\n\t\t// below follows symlinks, so it rejects broken links and links to files.\n\t\tif (!entry.isDirectory() && !entry.isSymbolicLink()) continue;\n\t\tconst skillDir = join(dir, entry.name);\n\t\tif (!existsSync(join(skillDir, \"SKILL.md\"))) continue;\n\t\tfound.push({ id: entry.name, sourceDir: skillDir });\n\t}\n\treturn found;\n}\n\n/**\n * Classify a directory entry, following symlinks. `Dirent` reports a symlink\n * as neither file nor directory, so symlinked supporting files would be lost\n * without this. Broken symlinks and non-regular targets resolve to \"other\".\n */\nexport function entryKind(\n\tentry: Dirent,\n\tfullPath: string,\n): \"dir\" | \"file\" | \"other\" {\n\tif (entry.isDirectory()) return \"dir\";\n\tif (entry.isFile()) return \"file\";\n\tif (!entry.isSymbolicLink()) return \"other\";\n\ttry {\n\t\tconst target = statSync(fullPath);\n\t\tif (target.isDirectory()) return \"dir\";\n\t\tif (target.isFile()) return \"file\";\n\t} catch {\n\t\t// Broken symlink.\n\t}\n\treturn \"other\";\n}\n\n/** Collect supporting files (relative paths) alongside SKILL.md in a skill dir. */\nfunction collectFiles(sourceDir: string): string[] {\n\tconst files: string[] = [];\n\t// Following symlinked directories admits cycles; track resolved paths.\n\tconst visited = new Set<string>();\n\tfunction walk(dir: string, base: string) {\n\t\tlet realDir: string;\n\t\ttry {\n\t\t\trealDir = realpathSync(dir);\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tif (visited.has(realDir)) return;\n\t\tvisited.add(realDir);\n\t\tlet entries;\n\t\ttry {\n\t\t\tentries = readdirSync(dir, { withFileTypes: true });\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tfor (const entry of entries) {\n\t\t\tconst fullPath = join(dir, entry.name);\n\t\t\tconst relPath = relative(base, fullPath);\n\t\t\tif (entry.name === \"SKILL.md\") continue;\n\t\t\tconst kind = entryKind(entry, fullPath);\n\t\t\tif (kind === \"dir\") {\n\t\t\t\twalk(fullPath, base);\n\t\t\t} else if (kind === \"file\") {\n\t\t\t\tfiles.push(relPath);\n\t\t\t}\n\t\t}\n\t}\n\twalk(sourceDir, sourceDir);\n\treturn files;\n}\n\n/** Load and parse a single skill from its source directory. */\nfunction loadSkill(id: string, sourceDir: string): DiscoveredSkill | undefined {\n\tconst skillMdPath = join(sourceDir, \"SKILL.md\");\n\tlet content: string;\n\ttry {\n\t\tcontent = readFileSync(skillMdPath, \"utf8\");\n\t} catch {\n\t\treturn undefined;\n\t}\n\tconst fm = parseFrontmatter(content);\n\t// Both name and description are required for the mirror — Cursor matches\n\t// skills by description, and the id must match the name for consistency.\n\tif (!fm.name || !fm.description) return undefined;\n\treturn {\n\t\tid,\n\t\tname: fm.name,\n\t\tdescription: fm.description,\n\t\tsourceDir,\n\t\tfiles: collectFiles(sourceDir),\n\t};\n}\n\n/**\n * Expand a path from `skills.paths` the way opencode does: `~/` prefix →\n * home, relative paths → resolved against the project directory, absolute\n * paths used as-is. Returns undefined for empty input.\n */\nfunction expandSkillPath(\n\traw: string,\n\tcwd: string,\n\thome: string,\n): string | undefined {\n\tconst trimmed = raw.trim();\n\tif (!trimmed) return undefined;\n\tif (trimmed.startsWith(\"~/\")) return join(home, trimmed.slice(2));\n\tif (isAbsolute(trimmed)) return trimmed;\n\treturn resolvePath(cwd, trimmed);\n}\n\n/**\n * Collect skill directories that ship alongside file-based plugins — single\n * `.ts` files under `<root>/plugin/` or `<root>/plugins/` in each opencode\n * config root. A file plugin can bundle skills in a sibling `skills/` or\n * `skill/` dir (checked directly, not per-file) — see\n * {@link PLUGIN_SKILL_DIR_NAMES}.\n */\nexport function discoverFilePluginSkillDirs(roots: string[]): string[] {\n\tconst dirs: string[] = [];\n\tfor (const root of roots) {\n\t\tfor (const sub of FILE_PLUGIN_DIR_NAMES) {\n\t\t\tconst pluginDir = join(root, sub);\n\t\t\tif (!existsSync(pluginDir)) continue;\n\t\t\tfor (const skillName of PLUGIN_SKILL_DIR_NAMES) {\n\t\t\t\tconst skillDir = join(pluginDir, skillName);\n\t\t\t\tif (existsSync(skillDir)) dirs.push(skillDir);\n\t\t\t}\n\t\t}\n\t}\n\treturn dirs;\n}\n\n/**\n * Locate plugin-bundled skill sources for the current install.\n *\n * Returns discovery options for {@link discoverSkills}: the opencode plugin\n * cache root, plus any config roots that actually contain file-plugin sibling\n * skill dirs (`skills/` or `skill/`, per {@link PLUGIN_SKILL_DIR_NAMES}).\n * File-plugin roots are checked cheaply (just an existence test per candidate)\n * so the default scan stays fast even when most users have no file-plugin\n * skills. Never throws — fs errors degrade to cache-only discovery.\n */\nexport function resolvePluginSkillSources(cwd?: string): {\n\tcacheRoot?: string;\n\tfilePluginRoots?: string[];\n} {\n\tconst home = homedir();\n\tconst cacheRoot = opencodePackagesRoot(home);\n\tlet filePluginRoots: string[] = [];\n\ttry {\n\t\tconst candidates: string[] = [];\n\t\tconst start = cwd ?? process.cwd();\n\t\tconst stop = worktreeRoot(start);\n\t\tfor (const ancestor of walkUp(start, stop)) {\n\t\t\t// File plugins live in the `.opencode` config root of each project.\n\t\t\tcandidates.push(join(ancestor, \".opencode\"));\n\t\t}\n\t\tconst xdgConfig = process.env[\"XDG_CONFIG_HOME\"] || join(home, \".config\");\n\t\tcandidates.push(join(xdgConfig, \"opencode\"), home);\n\t\tfilePluginRoots = discoverFilePluginSkillDirs(candidates)\n\t\t\t.map((dir) => dirname(dirname(dir)))\n\t\t\t.filter((v, i, arr) => arr.indexOf(v) === i);\n\t} catch {\n\t\t// Degrade to cache-only discovery.\n\t}\n\treturn { cacheRoot, filePluginRoots };\n}\n\n/**\n * Discover skills from the filesystem, using a deterministic resolution\n * order that prioritises specificity: project beats global, nearer beats\n * farther, `.opencode` beats `.claude`/`.agents`.\n *\n * Scan order (first wins on duplicate id — a skill already seen is kept,\n * later duplicates are skipped):\n * 1. Project `.opencode/skill/`, `.opencode/skills/` walk-up (near→far)\n * 2. Project `.claude/skills/`, `.agents/skills/` walk-up (near→far)\n * 3. Global `~/.config/opencode/skill/`, `~/.config/opencode/skills/`\n * 4. Global `~/.claude/skills/`, `~/.agents/skills/`\n * 5. `~/.opencode/skill/`, `~/.opencode/skills/` (if `~/.opencode` exists)\n * 6. Extra paths from `config.skills.paths`\n * 7. Skills bundled inside installed opencode plugins — the opencode plugin\n * cache (`opencodePackagesRoot()`), plus file-plugin sibling dirs\n * (`~/.config/opencode/plugins/` etc). Lowest priority.\n *\n * This differs from opencode's own resolution, which loads concurrently with\n * unbounded concurrency (making \"last wins\" non-deterministic). We use\n * first-wins for a deterministic, specificity-ordered mirror.\n *\n * `extraPaths` corresponds to opencode's `config.skills.paths` — additional\n * directories to scan for skills. Paths are expanded: `~/` → home, relative\n * → resolved against `cwd`, absolute used as-is. Non-existent directories\n * are silently skipped (matching opencode's behaviour).\n */\nexport function discoverSkills(\n\tcwd: string,\n\textraPaths?: string[],\n\toptions?: { cacheRoot?: string; filePluginRoots?: string[] },\n): DiscoveredSkill[] {\n\tconst home = homedir();\n\tconst xdgConfig = process.env[\"XDG_CONFIG_HOME\"] || join(home, \".config\");\n\tconst stop = worktreeRoot(cwd);\n\n\t// Build the scan list in specificity order (first wins).\n\tconst scanRoots: string[] = [];\n\n\t// 1. Project .opencode walk-up (near→far)\n\tfor (const ancestor of walkUp(cwd, stop)) {\n\t\tfor (const sub of SKILL_DIR_NAMES) {\n\t\t\tscanRoots.push(join(ancestor, \".opencode\", sub));\n\t\t}\n\t}\n\n\t// 2. Project external walk-up (near→far)\n\tfor (const ancestor of walkUp(cwd, stop)) {\n\t\tfor (const ext of EXTERNAL_DIR_NAMES) {\n\t\t\tscanRoots.push(join(ancestor, ext, \"skills\"));\n\t\t}\n\t}\n\n\t// 3. Global opencode\n\tfor (const sub of SKILL_DIR_NAMES) {\n\t\tscanRoots.push(join(xdgConfig, \"opencode\", sub));\n\t}\n\n\t// 4. Global external\n\tfor (const ext of EXTERNAL_DIR_NAMES) {\n\t\tscanRoots.push(join(home, ext, \"skills\"));\n\t}\n\n\t// 5. ~/.opencode (if it exists)\n\tconst tildeOpencode = join(home, \".opencode\");\n\tif (existsSync(tildeOpencode)) {\n\t\tfor (const sub of SKILL_DIR_NAMES) {\n\t\t\tscanRoots.push(join(tildeOpencode, sub));\n\t\t}\n\t}\n\n\t// 6. Extra paths from config.skills.paths\n\tif (extraPaths) {\n\t\tfor (const raw of extraPaths) {\n\t\t\tconst expanded = expandSkillPath(raw, cwd, home);\n\t\t\tif (!expanded) continue;\n\t\t\tif (!existsSync(expanded)) continue;\n\t\t\tscanRoots.push(expanded);\n\t\t}\n\t}\n\n\t// 7. Plugin-bundled skills (lowest priority): the opencode plugin cache,\n\t// then sibling skill dirs of file-based plugins. File-plugin roots follow\n\t// the same specificity order as other project dirs (walk-up near→far, then\n\t// global), so a project's local file plugins are found before global ones.\n\tfor (const dir of discoverPluginSkillDirs(options?.cacheRoot, home)) {\n\t\tscanRoots.push(dir);\n\t}\n\tconst filePluginRoots = options?.filePluginRoots;\n\tif (filePluginRoots) {\n\t\tfor (const dir of discoverFilePluginSkillDirs(filePluginRoots)) {\n\t\t\tscanRoots.push(dir);\n\t\t}\n\t} else {\n\t\t// Project config roots (near→far), then global: same specificity order\n\t\t// as the other project skill scans.\n\t\tfor (const ancestor of walkUp(cwd, stop)) {\n\t\t\tfor (const dir of discoverFilePluginSkillDirs([\n\t\t\t\tjoin(ancestor, \".opencode\"),\n\t\t\t])) {\n\t\t\t\tscanRoots.push(dir);\n\t\t\t}\n\t\t}\n\t\tfor (const dir of discoverFilePluginSkillDirs([\n\t\t\tjoin(xdgConfig, \"opencode\"),\n\t\t\thome,\n\t\t])) {\n\t\t\tscanRoots.push(dir);\n\t\t}\n\t}\n\n\t// Scan in order, first wins on duplicate id (skip if already seen).\n\tconst byId = new Map<string, DiscoveredSkill>();\n\tfor (const dir of scanRoots) {\n\t\tconst found = scanSkillDir(dir);\n\t\tfor (const { id, sourceDir } of found) {\n\t\t\tif (byId.has(id)) continue;\n\t\t\tconst skill = loadSkill(id, sourceDir);\n\t\t\tif (skill) byId.set(id, skill);\n\t\t}\n\t}\n\n\treturn Array.from(byId.values());\n}\n\n// --- Permission filtering ---\n\n/** Wildcard pattern match supporting `*` (any sequence) and literal text. */\nfunction wildcardMatch(pattern: string, value: string): boolean {\n\tif (pattern === \"*\") return true;\n\tif (!pattern.includes(\"*\")) return pattern === value;\n\t// Convert glob to regex: escape everything except *, replace * with .*\n\tconst regex = pattern\n\t\t.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n\t\t.replace(/\\*/g, \".*\");\n\treturn new RegExp(`^${regex}$`).test(value);\n}\n\n/** Action for a skill under the map-form permission config. */\ntype SkillAction = \"allow\" | \"deny\" | \"ask\";\n\n/** Resolve the action for a skill id from the map-form `skill` permission rule. */\nfunction resolveMapPermission(\n\tskillPerm: unknown,\n\tskillId: string,\n): SkillAction | undefined {\n\tif (typeof skillPerm === \"string\") {\n\t\treturn skillPerm as SkillAction;\n\t}\n\tif (typeof skillPerm !== \"object\" || skillPerm === null) return undefined;\n\tconst map = skillPerm as Record<string, string>;\n\t// Last-matching-pattern wins (iterate in insertion order).\n\tlet action: SkillAction | undefined;\n\tfor (const [pattern, value] of Object.entries(map)) {\n\t\tif (wildcardMatch(pattern, skillId)) {\n\t\t\taction = value as SkillAction;\n\t\t}\n\t}\n\treturn action;\n}\n\n/** Resolve the action for a skill id from the rule-array permission config. */\nfunction resolveRuleArrayPermission(\n\trules: Array<{ permission: string; pattern: string; action: string }>,\n\tskillId: string,\n): SkillAction | undefined {\n\t// Last-matching-rule wins.\n\tlet action: SkillAction | undefined;\n\tfor (const rule of rules) {\n\t\tif (rule.permission !== \"skill\") continue;\n\t\tif (wildcardMatch(rule.pattern, skillId)) {\n\t\t\taction = rule.action as SkillAction;\n\t\t}\n\t}\n\treturn action;\n}\n\n/**\n * Filter discovered skills through opencode's live permission config and the\n * plugin's manual include/exclude override.\n *\n * - `deny` → excluded entirely.\n * - `ask` → excluded (the ask prompt can't be enforced across the Cursor\n * boundary). Logged as withheld.\n * - `allow` → included.\n * - No permission config for skills → all included (default allow).\n *\n * Manual `include`/`exclude` from plugin options takes precedence over\n * permission config: `exclude` always drops, `include` always keeps (even if\n * permission says deny — the user explicitly asked for it).\n */\nexport function filterSkills(\n\tskills: DiscoveredSkill[],\n\tconfig: Config | undefined,\n\toptions?: SkillFilterOptions,\n): ResolvedSkills {\n\tconst include = options?.include ?? [];\n\tconst exclude = options?.exclude ?? [];\n\tconst matchesAny = (patterns: string[], id: string) =>\n\t\tpatterns.some((pattern) => wildcardMatch(pattern, id));\n\n\t// Extract skill permission config from both forms.\n\tconst permission = config?.permission as Record<string, unknown> | undefined;\n\tconst mapSkillPerm = permission?.[\"skill\"];\n\tconst ruleArray = Array.isArray(permission?.[\"permission\"])\n\t\t? (permission![\"permission\"] as Array<{\n\t\t\t\tpermission: string;\n\t\t\t\tpattern: string;\n\t\t\t\taction: string;\n\t\t\t}>)\n\t\t: undefined;\n\n\t// Also check the V2 PermissionRuleset form (config.permission as array).\n\tconst v2Ruleset = Array.isArray(config?.permission)\n\t\t? (config!.permission as Array<{\n\t\t\t\tpermission: string;\n\t\t\t\tpattern: string;\n\t\t\t\taction: string;\n\t\t\t}>)\n\t\t: undefined;\n\n\tconst permitted: DiscoveredSkill[] = [];\n\tconst withheld: Array<{ id: string; reason: string }> = [];\n\n\tfor (const skill of skills) {\n\t\t// Manual exclude always wins.\n\t\tif (matchesAny(exclude, skill.id)) {\n\t\t\twithheld.push({ id: skill.id, reason: \"excluded by plugin options\" });\n\t\t\tcontinue;\n\t\t}\n\t\t// Manual include always wins.\n\t\tif (include.length > 0 && matchesAny(include, skill.id)) {\n\t\t\tpermitted.push(skill);\n\t\t\tcontinue;\n\t\t}\n\t\t// If include list is specified and this skill isn't on it, skip.\n\t\tif (include.length > 0 && !matchesAny(include, skill.id)) {\n\t\t\twithheld.push({\n\t\t\t\tid: skill.id,\n\t\t\t\treason: \"not in plugin include list\",\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Resolve permission action.\n\t\tlet action: SkillAction | undefined;\n\t\tif (v2Ruleset) {\n\t\t\taction = resolveRuleArrayPermission(v2Ruleset, skill.id);\n\t\t}\n\t\tif (action === undefined && ruleArray) {\n\t\t\taction = resolveRuleArrayPermission(ruleArray, skill.id);\n\t\t}\n\t\tif (action === undefined && mapSkillPerm !== undefined) {\n\t\t\taction = resolveMapPermission(mapSkillPerm, skill.id);\n\t\t}\n\n\t\t// Default to allow when no permission config touches this skill.\n\t\tif (action === undefined || action === \"allow\") {\n\t\t\tpermitted.push(skill);\n\t\t} else if (action === \"deny\") {\n\t\t\twithheld.push({ id: skill.id, reason: \"denied by permission config\" });\n\t\t} else if (action === \"ask\") {\n\t\t\twithheld.push({\n\t\t\t\tid: skill.id,\n\t\t\t\treason:\n\t\t\t\t\t\"ask-permissioned skills are withheld (the ask prompt can't cross the Cursor boundary)\",\n\t\t\t});\n\t\t}\n\t}\n\n\treturn { skills: permitted, withheld };\n}\n\n/**\n * A skill as reported by opencode's live `app.skills` endpoint.\n * `location` is the absolute path of the skill's SKILL.md.\n */\nexport interface LiveSkill {\n\tname: string;\n\tdescription?: string;\n\tlocation: string;\n\t/**\n\t * The skill's full body. Present for content-only skills (e.g. opencode's\n\t * `<built-in>` skills, which have no on-disk SKILL.md).\n\t */\n\tcontent?: string;\n}\n\n/**\n * Scratch root holding materialised copies of skills that only exist in\n * opencode's live inventory (no on-disk SKILL.md — e.g. opencode's own\n * `<built-in>` skills, whose content is registered in code). Stable across\n * calls so `skillSetHash` mtime checks don't churn per turn; wiped on exit.\n */\nlet liveScratchRoot: string | undefined;\nlet liveScratchCleanupRegistered = false;\n\n/** Test hook: drop the scratch root so tests don't share state. */\nexport function resetLiveSkillScratch(): void {\n\tconst root = liveScratchRoot;\n\tif (root) {\n\t\ttry {\n\t\t\trmSync(root, { recursive: true, force: true });\n\t\t} catch {\n\t\t\t// best effort\n\t\t}\n\t}\n\tliveScratchRoot = undefined;\n}\n\nfunction liveScratchDir(): string {\n\tif (!liveScratchRoot) {\n\t\tliveScratchRoot = mkdtempSync(join(tmpdir(), \"opencode-cursor-skills-\"));\n\t\tif (!liveScratchCleanupRegistered) {\n\t\t\tliveScratchCleanupRegistered = true;\n\t\t\tconst rootAtExit = liveScratchRoot;\n\t\t\tprocess.once(\"exit\", () => {\n\t\t\t\trmSync(rootAtExit, { recursive: true, force: true });\n\t\t\t});\n\t\t}\n\t}\n\treturn liveScratchRoot;\n}\n\n/**\n * Convert live `app.skills` entries into {@link DiscoveredSkill}s.\n *\n * Disk-backed entries point at the skill's on-disk directory (derived from\n * `location`); entries whose location isn't resolvable are skipped — the\n * filesystem scan already covers anything reachable.\n *\n * Content-only entries (no on-disk SKILL.md — opencode's `<built-in>` skills\n * and anything else opencode serves from memory) are materialised into a\n * scratch dir so the mirror can stamp and copy them like any other skill.\n * The rewritten file carries frontmatter (the live `description`) + the\n * live `content` body, keeping the mirror in sync with opencode's version.\n */\nexport function liveSkillsToDiscovered(live: LiveSkill[]): DiscoveredSkill[] {\n\tconst out: DiscoveredSkill[] = [];\n\tfor (const skill of live) {\n\t\tif (!skill.location || !skill.name) continue;\n\t\tconst sourceDir = skill.location.endsWith(\"SKILL.md\")\n\t\t\t? dirname(skill.location)\n\t\t\t: skill.location;\n\t\tif (existsSync(join(sourceDir, \"SKILL.md\"))) {\n\t\t\tconst loaded = loadSkill(skill.name, sourceDir);\n\t\t\tif (loaded) out.push(loaded);\n\t\t\tcontinue;\n\t\t}\n\t\t// Not on disk: materialise if opencode gave us content.\n\t\tif (!skill.content) continue;\n\t\tconst scratchDir = join(liveScratchDir(), skill.name);\n\t\tconst scratchMd = join(scratchDir, \"SKILL.md\");\n\t\t// Rewrite only when the content or description actually changed, so\n\t\t// per-turn calls don't touch mtimes and invalidate the skill hash.\n\t\tlet needsWrite = true;\n\t\tif (existsSync(scratchMd)) {\n\t\t\ttry {\n\t\t\t\tconst existing = readFileSync(scratchMd, \"utf8\");\n\t\t\t\tif (existing === renderLiveSkillMd(skill)) needsWrite = false;\n\t\t\t} catch {\n\t\t\t\t// unreadable → rewrite\n\t\t\t}\n\t\t}\n\t\tif (needsWrite) {\n\t\t\ttry {\n\t\t\t\tmkdirSync(scratchDir, { recursive: true });\n\t\t\t\twriteFileSync(scratchMd, renderLiveSkillMd(skill), \"utf8\");\n\t\t\t} catch {\n\t\t\t\tcontinue; // scratch fs unavailable — skip this skill\n\t\t\t}\n\t\t}\n\t\tconst loaded = loadSkill(skill.name, scratchDir);\n\t\tif (loaded) out.push(loaded);\n\t}\n\treturn out;\n}\n\n/** Render a live (content-only) skill as a stamped SKILL.md. */\nfunction renderLiveSkillMd(skill: LiveSkill): string {\n\t// YAML: quote the description to survive colons/quotes inside it.\n\tconst escaped = (skill.description ?? \"\").replace(/\"/g, '\\\\\"');\n\tconst body = skill.content ?? \"\";\n\tconst separator = body.startsWith(\"\\n\") ? \"\" : \"\\n\";\n\treturn `---\\nname: ${skill.name}\\ndescription: \"${escaped}\"\\n---\\n${separator}${body}`;\n}\n\n/**\n * Discover and filter skills in one call. This is the main entry point for the\n * plugin's config and chat.params hooks. Never throws — fs errors degrade to\n * an empty skill list.\n *\n * `config.skills.paths` is extracted and passed to {@link discoverSkills} as\n * `extraPaths`, so skills configured via the `skills.paths` config option are\n * included in the mirror (lowest priority, first-wins).\n *\n * When `liveSkills` is supplied (from opencode's `app.skills` endpoint), those\n * skills are merged in at the LOWEST priority — the filesystem scan wins on\n * duplicate ids, but anything opencode knows about that the scan missed\n * (e.g. skills sourced from locations this mirror doesn't scan) still reaches\n * the Cursor agent.\n */\nexport function resolveSkills(\n\tcwd: string,\n\tconfig?: Config,\n\toptions?: SkillFilterOptions,\n\tdiscoveryOptions?: {\n\t\tcacheRoot?: string;\n\t\tfilePluginRoots?: string[];\n\t\tliveSkills?: LiveSkill[];\n\t},\n): ResolvedSkills {\n\t// SAFETY: `config` at runtime is the live opencode config JSON returned by\n\t// client.config.get(); its shape always carries a `skills` object when the\n\t// user configured one. The V1 Config type just omits that field, so we\n\t// widen it here. Access is optional-chain guarded below.\n\tconst skillsConfig = config as unknown as\n\t\t| { skills?: { paths?: string[] } }\n\t\t| undefined;\n\tconst extraPaths = skillsConfig?.skills?.paths;\n\n\tlet discovered: DiscoveredSkill[];\n\ttry {\n\t\tdiscovered = discoverSkills(cwd, extraPaths, discoveryOptions);\n\t} catch {\n\t\tdiscovered = [];\n\t}\n\tif (discoveryOptions?.liveSkills?.length) {\n\t\tconst seen = new Set(discovered.map((s) => s.id));\n\t\tfor (const skill of liveSkillsToDiscovered(discoveryOptions.liveSkills)) {\n\t\t\tif (seen.has(skill.id)) continue;\n\t\t\tseen.add(skill.id);\n\t\t\tdiscovered.push(skill);\n\t\t}\n\t}\n\treturn filterSkills(discovered, config, options);\n}\n\n/**\n * A stable hash of the resolved skill set, used to skip re-materialisation\n * when nothing changed between turns. Based on skill ids + source dirs + file\n * mtimes so content changes are detected.\n */\nexport function skillSetHash(skills: DiscoveredSkill[]): string {\n\tconst parts = skills.map((s) => {\n\t\tconst files = [\"SKILL.md\", ...s.files].map((file) => {\n\t\t\ttry {\n\t\t\t\tconst stat = statSync(join(s.sourceDir, file));\n\t\t\t\treturn `${file}:${stat.mtimeMs}:${stat.size}`;\n\t\t\t} catch {\n\t\t\t\treturn `${file}:missing`;\n\t\t\t}\n\t\t});\n\t\tfiles.sort();\n\t\treturn `${s.id}:${s.sourceDir}:${files.join(\",\")}`;\n\t});\n\tparts.sort();\n\treturn parts.join(\"|\");\n}\n","/**\n * Mirror the `tool` maps of other installed opencode plugins so the Cursor\n * agent can call them through a local MCP bridge.\n *\n * How it works: opencode loads plugins in `config.plugin` order and executes\n * their `hooks.tool` definitions in-process (`tool/registry.ts`). A sibling\n * plugin registered *after* them can re-`import()` the same module and invoke\n * its exported `server`/default function to read the identical `hooks.tool`\n * map — the exact closures opencode will execute. The mirror never runs a\n * plugin's lifecycle hooks (`config`, `event`, `chat.*`); it only reads the\n * `tool` map and forwards `execute` calls, with the host plugin providing a\n * real `context.ask` so the user's permission config still gates every call.\n *\n * What is mirrored:\n * - `config.plugin` specs that are bare package names or `name@latest` /\n * `@scope/name@latest` (resolved against the opencode package cache).\n * - git specs (`name@git+https:...`) when the cache entry resolves.\n * - explicit local file paths (`.ts`/`.js`), imported directly.\n * Skipped: anything that fails to import/init (logged, never fatal), and the\n * `@stablekernel/opencode-cursor` spec itself (never mirror ourselves).\n */\nimport { existsSync, readdirSync, statSync } from \"node:fs\";\nimport type { Dirent } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { createRequire } from \"node:module\";\nimport { pathToFileURL } from \"node:url\";\nimport { homedir } from \"node:os\";\nimport { tool } from \"@opencode-ai/plugin\";\nimport type { Config, ToolDefinition } from \"@opencode-ai/plugin\";\nimport { opencodePackagesRoot } from \"./skill-discovery.js\";\n\n/** Our own spec, excluded from mirroring (never mirror ourselves). */\nconst SELF_SPECS = new Set([\n\t\"@stablekernel/opencode-cursor\",\n\t\"@stablekernel/opencode-cursor@latest\",\n]);\n\n/** A tool definition mirrored from another plugin, ready to execute. */\nexport interface MirroredTool {\n\tid: string;\n\tdescription: string;\n\tparameters: Record<string, unknown>;\n\texecute: ToolDefinition[\"execute\"];\n\t/** The plugin the tool came from (for logging/permission keys). */\n\tsourcePlugin: string;\n}\n\nexport interface MirrorPluginToolsOptions {\n\t/** Only mirror tools whose id matches one of these patterns. */\n\tinclude?: string[];\n\t/** Never mirror tools whose id matches one of these patterns. */\n\texclude?: string[];\n\t/** Override the package cache root (tests). */\n\tcacheRoot?: string;\n}\n\nexport interface MirrorResult {\n\ttools: MirroredTool[];\n\t/** Specs that were attempted but failed (id → reason). */\n\tfailed: Record<string, string>;\n}\n\n/**\n * Best-effort parse of a `config.plugin` entry into a resolvable form.\n * Entries may be `name`, `name@latest`, `@scope/name[@latest]`,\n * `name@git+<url>`, or a filesystem path. Returns undefined for entries we\n * don't attempt to mirror (relative paths, URL-only specs).\n */\nexport function parsePluginSpec(\n\tspec: string,\n):\n\t| { kind: \"npm\"; name: string; version?: string }\n\t| { kind: \"git\"; name: string; raw: string }\n\t| { kind: \"path\"; path: string }\n\t| { kind: \"unsupported\"; raw: string }\n\t| undefined {\n\tconst trimmed = spec.trim();\n\tif (!trimmed) return undefined;\n\t// Filesystem paths: absolute, or explicitly relative.\n\tif (\n\t\ttrimmed.startsWith(\"/\") ||\n\t\ttrimmed.startsWith(\"./\") ||\n\t\ttrimmed.startsWith(\"../\") ||\n\t\ttrimmed.startsWith(\"~/\") ||\n\t\t/\\.[cm]?[jt]sx?$/.test(trimmed)\n\t) {\n\t\tconst expanded = trimmed.startsWith(\"~/\")\n\t\t\t? join(homedir(), trimmed.slice(2))\n\t\t\t: trimmed;\n\t\treturn { kind: \"path\", path: expanded };\n\t}\n\t// npm spec: `name`, `name@latest`, `@scope/name@version`.\n\tconst at = trimmed.lastIndexOf(\"@\");\n\tif (at > 0) {\n\t\tconst name = trimmed.slice(0, at);\n\t\tconst version = trimmed.slice(at + 1);\n\t\t// Git specs (`git+https:...`) land in the cache under the raw spec\n\t\t// string. Other URL forms (tarball specs) are valid npm but never\n\t\t// appear in opencode's cache layout — mark them unsupported instead\n\t\t// of misclassifying them as git.\n\t\tif (version.startsWith(\"git+\")) {\n\t\t\treturn { kind: \"git\", name, raw: trimmed };\n\t\t}\n\t\tif (version.includes(\"://\")) {\n\t\t\treturn { kind: \"unsupported\", raw: trimmed };\n\t\t}\n\t\treturn { kind: \"npm\", name, version };\n\t}\n\treturn { kind: \"npm\", name: trimmed };\n}\n\n/**\n * Locate a plugin's install directory in the opencode package cache for an\n * npm or git spec. Tries the layouts opencode produces: `<name>@latest`,\n * `<name>@<version>`, bare `<name>`, and (for git specs) the spec string\n * verbatim with any nesting depth.\n */\nexport function resolveCacheEntry(\n\tcacheRoot: string,\n\tparsed:\n\t\t| { kind: \"npm\"; name: string; version?: string }\n\t\t| { kind: \"git\"; name: string; raw: string }\n\t\t| { kind: \"unsupported\"; raw: string },\n): string | undefined {\n\tif (parsed.kind === \"unsupported\") return undefined;\n\tif (parsed.kind === \"git\") {\n\t\t// Git specs land as the spec string itself, potentially nested\n\t\t// (superpowers@git+https:/github.com/owner/repo.git). Walk bounded.\n\t\tconst parts = parsed.raw.split(\"/\");\n\t\tlet current = cacheRoot;\n\t\tfor (const part of parts) {\n\t\t\tconst candidate = join(current, part);\n\t\t\tif (!existsSync(candidate)) return undefined;\n\t\t\tcurrent = candidate;\n\t\t}\n\t\treturn current;\n\t}\n\tconst { name, version } = parsed;\n\tconst candidates = [\n\t\tversion ? join(cacheRoot, `${name}@${version}`) : undefined,\n\t\tjoin(cacheRoot, `${name}@latest`),\n\t\tjoin(cacheRoot, name),\n\t].filter((c): c is string => Boolean(c));\n\tfor (const candidate of candidates) {\n\t\tif (existsSync(candidate)) return candidate;\n\t}\n\treturn undefined;\n}\n\n/**\n * Resolve a cache entry's importable module path via its package.json\n * (`main` / `exports`), matching how opencode itself loads the plugin. A\n * scoped or nested `node_modules` root is used for require-resolution so\n * relative `main` paths land inside the package dir.\n */\nfunction resolvePackageMain(\n\tcacheEntry: string,\n\tpkgDir: string,\n\tname: string,\n): string | undefined {\n\tconst tryRequire = (baseDir: string): string | undefined => {\n\t\ttry {\n\t\t\tconst req = createRequire(join(baseDir, \"noop.js\"));\n\t\t\treturn req.resolve(name);\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t};\n\t// Prefer resolution from the package dir itself (handles `exports`),\n\t// then from the cache entry root (handles bare `main` layouts).\n\treturn tryRequire(pkgDir) ?? tryRequire(cacheEntry);\n}\n\n/** The package root inside a cache entry: `node_modules/<pkg>`. */\nfunction packageRoot(cacheEntry: string, name: string): string | undefined {\n\tconst direct = join(cacheEntry, \"node_modules\", name);\n\tif (existsSync(direct)) return direct;\n\t// Fallback: first node_modules child (git specs nest the real package).\n\tconst nm = join(cacheEntry, \"node_modules\");\n\tif (!existsSync(nm)) return undefined;\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = readdirSync(nm, { withFileTypes: true });\n\t} catch {\n\t\treturn undefined;\n\t}\n\tfor (const ent of entries) {\n\t\tif (ent.name === \".bin\") continue;\n\t\tconst full = join(nm, ent.name);\n\t\tlet isDir = ent.isDirectory();\n\t\tif (!isDir && ent.isSymbolicLink()) {\n\t\t\ttry {\n\t\t\t\tisDir = statSync(full).isDirectory();\n\t\t\t} catch {\n\t\t\t\tisDir = false;\n\t\t\t}\n\t\t}\n\t\tif (isDir) return full;\n\t}\n\treturn undefined;\n}\n\n/** Extract a JSON Schema from a plugin's Zod-or-plain args map. */\nexport function argsToJsonSchema(args: unknown): Record<string, unknown> {\n\tif (args == null || typeof args !== \"object\")\n\t\treturn { type: \"object\", properties: {}, required: [] };\n\tconst entries = Object.entries(args as Record<string, unknown>);\n\tconst allZod = entries.length > 0 && entries.every(([, v]) => isZodType(v));\n\tif (allZod) {\n\t\ttry {\n\t\t\t// `tool.schema` is the same Zod instance opencode bundles plugins\n\t\t\t// against, so `_zod`-shaped args always parse with it. zod v4\n\t\t\t// exposes toJSONSchema; keep the call dynamic so this module also\n\t\t\t// typechecks against a zod v3 root (legacy path below covers it).\n\t\t\t// SAFETY: `tool.schema` is always a Zod namespace object exposing\n\t\t\t// `object()`; `toJSONSchema` is only present on Zod v4 builds, so\n\t\t\t// the cast widens to a shape that makes both versions typecheck.\n\t\t\tconst zodLike = tool.schema as unknown as {\n\t\t\t\tobject: (shape: unknown) => unknown;\n\t\t\t\ttoJSONSchema?: (schema: unknown, opts?: unknown) => Record<string, unknown>;\n\t\t\t};\n\t\t\tif (typeof zodLike.toJSONSchema === \"function\") {\n\t\t\t\tconst schema = zodLike.toJSONSchema(zodLike.object(args), {\n\t\t\t\t\tio: \"input\",\n\t\t\t\t});\n\t\t\t\treturn normalizeZodSchema(schema);\n\t\t\t}\n\t\t} catch {\n\t\t\t// fall through to the legacy path\n\t\t}\n\t}\n\t// Legacy: treat non-Zod entries as raw JSON Schema properties.\n\tconst properties: Record<string, unknown> = {};\n\tfor (const [key, value] of entries) {\n\t\tif (\n\t\t\ttypeof value === \"boolean\" ||\n\t\t\t(typeof value === \"object\" && value !== null && !Array.isArray(value))\n\t\t) {\n\t\t\tproperties[key] = value;\n\t\t}\n\t}\n\treturn { type: \"object\", properties, required: Object.keys(properties) };\n}\n\nfunction isZodType(value: unknown): boolean {\n\treturn typeof value === \"object\" && value !== null && \"_zod\" in value;\n}\n\n/**\n * Zod v4 emits `$schema` and `definitions`/`$defs` blocks; Cursor's MCP\n * layer only needs a plain object schema, so strip the meta fields and\n * inline nothing (definitions are referenced by name and MCP accepts them).\n */\nfunction normalizeZodSchema(\n\tschema: Record<string, unknown>,\n): Record<string, unknown> {\n\tconst out = { ...schema };\n\tdelete out[\"$schema\"];\n\treturn out;\n}\n\n/** Read the `tool` map from a loaded plugin module's hooks. */\nfunction extractToolMap(\n\thooks: unknown,\n): Record<string, ToolDefinition> | undefined {\n\tif (!hooks || typeof hooks !== \"object\") return undefined;\n\tconst tool = (hooks as { tool?: unknown }).tool;\n\tif (!tool || typeof tool !== \"object\") return undefined;\n\tconst out: Record<string, ToolDefinition> = {};\n\tfor (const [id, def] of Object.entries(tool as Record<string, unknown>)) {\n\t\tif (isPluginTool(def)) out[id] = def;\n\t}\n\treturn Object.keys(out).length > 0 ? out : undefined;\n}\n\nfunction isPluginTool(value: unknown): value is ToolDefinition {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t\"args\" in value &&\n\t\t\"description\" in value &&\n\t\t\"execute\" in value\n\t);\n}\n\n/**\n * Load one plugin module and read its `tool` map. The plugin's `server`\n * function is invoked with a minimal input shaped like opencode's\n * `PluginInput`; only fields the plugin touches at load time matter, and\n * most read nothing until hook execution. Throws on import/init failure —\n * callers collect the error.\n */\nasync function loadToolMap(\n\tmodulePath: string,\n\tinput: unknown,\n): Promise<Record<string, ToolDefinition> | undefined> {\n\tconst mod = (await import(pathToFileURL(modulePath).href)) as Record<\n\t\tstring,\n\t\tunknown\n\t>;\n\t// Preferred export shapes, in order: `server`, `default`, then any\n\t// function export (some plugins export a single named factory).\n\tconst candidates = [mod[\"server\"], mod[\"default\"]];\n\tfor (const value of Object.values(mod)) {\n\t\tif (typeof value === \"function\" && !candidates.includes(value)) {\n\t\t\tcandidates.push(value);\n\t\t}\n\t}\n\tfor (const candidate of candidates) {\n\t\tif (typeof candidate !== \"function\") continue;\n\t\tlet hooks: unknown;\n\t\ttry {\n\t\t\thooks = await candidate(input);\n\t\t} catch {\n\t\t\tcontinue; // try the next candidate shape\n\t\t}\n\t\tconst tools = extractToolMap(hooks);\n\t\tif (tools) return tools;\n\t}\n\treturn undefined;\n}\n\n/**\n * Mirror the tool maps of every other plugin listed in `config.plugin`.\n *\n * Never throws: every failure is recorded in `failed` and the remaining\n * plugins are still processed. The returned `execute` functions are the\n * original closures from each plugin module; the caller supplies the\n * `ToolContext` (with a working `ask`) at call time.\n */\nexport async function mirrorPluginTools(\n\tconfig: Config | undefined,\n\tinput: unknown,\n\toptions?: MirrorPluginToolsOptions,\n): Promise<MirrorResult> {\n\tconst failed: Record<string, string> = {};\n\tconst tools: MirroredTool[] = [];\n\tconst seen = new Set<string>();\n\n\tconst specs = (config?.plugin ?? [])\n\t\t.map((entry) =>\n\t\t\ttypeof entry === \"string\"\n\t\t\t\t? entry\n\t\t\t\t: Array.isArray(entry)\n\t\t\t\t\t? entry[0]\n\t\t\t\t\t: undefined,\n\t\t)\n\t\t.filter((s): s is string => typeof s === \"string\" && s.length > 0);\n\n\tconst cacheRoot = options?.cacheRoot ?? opencodePackagesRoot(homedir());\n\n\tfor (const spec of specs) {\n\t\tif (SELF_SPECS.has(spec)) continue;\n\t\tconst parsed = parsePluginSpec(spec);\n\t\tif (!parsed) {\n\t\t\tfailed[spec] = \"unsupported spec format\";\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (parsed.kind === \"unsupported\") {\n\t\t\tfailed[spec] =\n\t\t\t\t\"unsupported spec format (URL tarball specs are not mirrored)\";\n\t\t\tcontinue;\n\t\t}\n\t\tlet modulePath: string | undefined;\n\t\tif (parsed.kind === \"path\") {\n\t\t\tmodulePath = parsed.path.startsWith(\"/\") ? parsed.path : undefined;\n\t\t\tif (!modulePath || !existsSync(modulePath)) {\n\t\t\t\tfailed[spec] = \"plugin file not found\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t} else {\n\t\t\tconst entry = resolveCacheEntry(cacheRoot, parsed);\n\t\t\tif (!entry) {\n\t\t\t\tfailed[spec] = \"not found in opencode package cache\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst pkg = packageRoot(entry, parsed.name);\n\t\t\tif (!pkg) {\n\t\t\t\tfailed[spec] = \"package root not found in cache entry\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Resolve through the cache entry's package.json (main/exports)\n\t\t\t// so bundled plugins load exactly where their manifest says.\n\t\t\tconst resolved = resolvePackageMain(entry, pkg, parsed.name);\n\t\t\tif (!resolved) {\n\t\t\t\tfailed[spec] = \"package entry point not found\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tmodulePath = resolved;\n\t\t}\n\n\t\tif (!modulePath) {\n\t\t\tfailed[spec] = \"plugin module path not resolved\";\n\t\t\tcontinue;\n\t\t}\n\t\tlet toolMap: Record<string, ToolDefinition> | undefined;\n\t\ttry {\n\t\t\ttoolMap = await loadToolMap(modulePath, input);\n\t\t} catch (error) {\n\t\t\tfailed[spec] = error instanceof Error ? error.message : String(error);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!toolMap) {\n\t\t\tfailed[spec] = \"no tool map exported\";\n\t\t\tcontinue;\n\t\t}\n\n\t\tfor (const [id, def] of Object.entries(toolMap)) {\n\t\t\tif (seen.has(id)) continue;\n\t\t\tif (options?.exclude?.some((p) => matchPattern(p, id))) continue;\n\t\t\tif (\n\t\t\t\toptions?.include &&\n\t\t\t\toptions.include.length > 0 &&\n\t\t\t\t!options.include.some((p) => matchPattern(p, id))\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tseen.add(id);\n\t\t\ttools.push({\n\t\t\t\tid,\n\t\t\t\tdescription: def.description,\n\t\t\t\tparameters: argsToJsonSchema(def.args),\n\t\t\t\texecute: def.execute,\n\t\t\t\tsourcePlugin: spec,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn { tools, failed };\n}\n\n/** Wildcard match: `*` = any sequence, otherwise literal. */\nfunction matchPattern(pattern: string, value: string): boolean {\n\tif (pattern === \"*\") return true;\n\tif (!pattern.includes(\"*\")) return pattern === value;\n\tconst regex = pattern\n\t\t.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n\t\t.replace(/\\*/g, \".*\");\n\treturn new RegExp(`^${regex}$`).test(value);\n}\n","/**\n * Localhost control channel + MCP wiring for the plugin-tools bridge.\n *\n * The host plugin owns the mirrored tool closures (see\n * `plugin-tool-registry.ts`). The stdio MCP child\n * (`sidecar/plugin-tools-mcp.mjs`) can't hold those closures, so it talks to\n * this HTTP server on 127.0.0.1 for `tools/list` and `tools/call`. The server\n * binds to loopback only and requires a bearer token (generated per session,\n * passed to the child via env) so nothing else on the machine can invoke the\n * user's plugin tools through it.\n *\n * Execution happens through the mirrored `execute` closures with a synthetic\n * `ToolContext` whose `ask` delegates to the user's opencode permission gate\n * (same `context.ask` pattern the delegation tools use), so a `permission`\n * config entry for a tool id applies to Cursor-originated calls too.\n */\nimport { createServer, type Server } from \"node:http\";\nimport { randomBytes } from \"node:crypto\";\nimport { existsSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { execSync } from \"node:child_process\";\nimport type { ToolContext } from \"@opencode-ai/plugin\";\nimport type { MirroredTool } from \"./plugin-tool-registry.js\";\nimport { pluginLog } from \"../provider/log-bridge.js\";\n\nexport interface PluginToolsBridge {\n\t/** The MCP server config to hand to the Cursor agent, or undefined. */\n\tmcpServer?: {\n\t\ttype: \"stdio\";\n\t\tcommand: string;\n\t\targs: string[];\n\t\tenv: Record<string, string>;\n\t};\n\t/** Stop the control server (called on dispose). */\n\tclose: () => Promise<void>;\n}\n\n/**\n * Build the `ToolContext` handed to a mirrored tool's `execute`. `ask`\n * delegates to the supplied gate so the user's opencode permission config\n * applies; when no gate exists the call fails closed (matches the\n * delegation-tool behaviour — never silently allow a sensitive action).\n */\nfunction buildToolContext(\n\targs: { sessionID: string; agent: string; directory: string },\n\taskGate?: ToolContext[\"ask\"],\n): ToolContext {\n\tconst controller = new AbortController();\n\treturn {\n\t\tsessionID: args.sessionID,\n\t\tmessageID: \"cursor-plugin-tools\",\n\t\tagent: args.agent,\n\t\tdirectory: args.directory,\n\t\tworktree: args.directory,\n\t\tabort: controller.signal,\n\t\tmetadata: () => {},\n\t\task: async (input) => {\n\t\t\tif (!askGate) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"permission gate unavailable — refusing to run plugin tool without approval\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tawait askGate(input);\n\t\t},\n\t};\n}\n\n/**\n * Locate the stdio MCP server script across dist/dev layouts (same pattern\n * as `resolveSidecarScript` in provider/agent-backend.ts).\n */\nexport function resolvePluginToolsScript(): string | undefined {\n\tconst candidates = [\n\t\t\"./plugin-tools-mcp.js\", // importer is a chunk at dist root\n\t\t\"../sidecar/plugin-tools-mcp.js\", // importer is dist/plugin/index.js\n\t\t\"../sidecar/plugin-tools-mcp.mjs\", // importer is src/plugin/*.ts (dev/tests)\n\t];\n\tfor (const candidate of candidates) {\n\t\tconst path = fileURLToPath(new URL(candidate, import.meta.url));\n\t\tif (existsSync(path)) return path;\n\t}\n\treturn undefined;\n}\n\nfunction execBasename(execPath: string): string {\n\tconst base = execPath.split(/[/\\\\]/).pop() ?? \"\";\n\treturn base.replace(/\\.exe$/i, \"\").toLowerCase();\n}\n\nexport function resolvePluginToolsNodeCommand(\n\texecPath = process.execPath,\n\tlookupNode?: () => string | undefined,\n): string | undefined {\n\tconst name = execBasename(execPath);\n\tif (name === \"node\" || name === \"bun\") return execPath;\n\tif (lookupNode) return lookupNode() || undefined;\n\ttry {\n\t\tconst out = execSync(\n\t\t\tprocess.platform === \"win32\" ? \"where node\" : \"command -v node\",\n\t\t\t{\n\t\t\t\tencoding: \"utf8\",\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"ignore\"],\n\t\t\t},\n\t\t).trim();\n\t\treturn out.split(\"\\n\")[0] || undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport interface StartBridgeOptions {\n\ttools: MirroredTool[];\n\t/** Directory the mirrored tools should see as `context.directory`. */\n\tdirectory: string;\n\t/**\n\t * Permission gate for `context.ask`. When omitted, tools whose execution\n\t * calls `ask` fail closed.\n\t */\n\taskGate?: ToolContext[\"ask\"];\n\t/** Session id stamped into the synthetic ToolContext. */\n\tsessionID?: string;\n\t/** Agent name stamped into the synthetic ToolContext. */\n\tagent?: string;\n}\n\n/**\n * Start the localhost control server and build the MCP server config for the\n * Cursor agent. Returns `{ close }` with no `mcpServer` when the script or a\n * usable Node binary can't be found — the bridge degrades to \"not offered\"\n * rather than failing plugin init.\n */\nexport async function startPluginToolsBridge(\n\toptions: StartBridgeOptions,\n): Promise<PluginToolsBridge> {\n\tconst scriptPath = resolvePluginToolsScript();\n\tif (!scriptPath) {\n\t\tpluginLog(\"warn\", \"plugin-tools MCP script not found; bridge disabled\");\n\t\treturn { close: async () => {} };\n\t}\n\tconst nodePath = resolvePluginToolsNodeCommand();\n\tif (!nodePath) {\n\t\tpluginLog(\"warn\", \"plugin-tools MCP needs node on PATH; bridge disabled\");\n\t\treturn { close: async () => {} };\n\t}\n\n\tconst token = randomBytes(24).toString(\"hex\");\n\tconst toolById = new Map(options.tools.map((t) => [t.id, t]));\n\n\tconst server: Server = createServer((req, res) => {\n\t\tconst send = (status: number, body: unknown) => {\n\t\t\tres.writeHead(status, { \"content-type\": \"application/json\" });\n\t\t\tres.end(JSON.stringify(body));\n\t\t};\n\t\tconst auth = req.headers[\"authorization\"];\n\t\tif (auth !== `Bearer ${token}`) {\n\t\t\tsend(401, { error: \"unauthorized\" });\n\t\t\treturn;\n\t\t}\n\t\tif (req.method === \"GET\" && req.url === \"/tools\") {\n\t\t\tsend(200, {\n\t\t\t\ttools: options.tools.map((t) => ({\n\t\t\t\t\tid: t.id,\n\t\t\t\t\tdescription: t.description,\n\t\t\t\t\tparameters: t.parameters,\n\t\t\t\t})),\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tif (req.method === \"POST\" && req.url === \"/call\") {\n\t\t\t// Cap request bodies: loopback + token limits the blast radius, but\n\t\t\t// an unbounded accumulator would still let a caller exhaust memory.\n\t\t\tconst MAX_BODY = 10 * 1024 * 1024;\n\t\t\tlet raw = \"\";\n\t\t\tlet size = 0;\n\t\t\treq.on(\"data\", (chunk) => {\n\t\t\t\tsize += chunk.length;\n\t\t\t\tif (size > MAX_BODY) {\n\t\t\t\t\treq.destroy();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\traw += chunk;\n\t\t\t});\n\t\t\treq.on(\"end\", async () => {\n\t\t\t\tif (size > MAX_BODY) return; // destroyed above\n\t\t\t\tlet body: { id?: string; args?: Record<string, unknown> };\n\t\t\t\ttry {\n\t\t\t\t\tbody = JSON.parse(raw);\n\t\t\t\t} catch {\n\t\t\t\t\tsend(400, { ok: false, error: \"invalid JSON body\" });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst tool = body.id ? toolById.get(body.id) : undefined;\n\t\t\t\tif (!tool) {\n\t\t\t\t\tsend(404, { ok: false, error: `unknown tool: ${body.id}` });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst ctx = buildToolContext(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsessionID: options.sessionID ?? \"cursor-plugin-tools\",\n\t\t\t\t\t\t\tagent: options.agent ?? \"cursor\",\n\t\t\t\t\t\t\tdirectory: options.directory,\n\t\t\t\t\t\t},\n\t\t\t\t\t\toptions.askGate,\n\t\t\t\t\t);\n\t\t\t\t\tconst result = await tool.execute((body.args ?? {}) as never, ctx);\n\t\t\t\t\tif (typeof result === \"string\") {\n\t\t\t\t\t\tsend(200, { ok: true, output: result });\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsend(200, {\n\t\t\t\t\t\t\tok: true,\n\t\t\t\t\t\t\ttitle: result.title,\n\t\t\t\t\t\t\toutput: result.output,\n\t\t\t\t\t\t\tmetadata: result.metadata,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\t\tsend(200, { ok: false, error: message });\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tsend(404, { error: \"not found\" });\n\t});\n\n\tawait new Promise<void>((resolve, reject) => {\n\t\tserver.once(\"error\", reject);\n\t\tserver.listen(0, \"127.0.0.1\", () => resolve());\n\t});\n\tconst address = server.address();\n\tconst port = typeof address === \"object\" && address ? address.port : undefined;\n\tif (!port) {\n\t\tawait new Promise<void>((resolve) => server.close(() => resolve()));\n\t\tpluginLog(\n\t\t\t\"warn\",\n\t\t\t\"plugin-tools control server failed to bind; bridge disabled\",\n\t\t);\n\t\treturn { close: async () => {} };\n\t}\n\n\treturn {\n\t\tmcpServer: {\n\t\t\ttype: \"stdio\",\n\t\t\tcommand: nodePath,\n\t\t\targs: [scriptPath],\n\t\t\tenv: {\n\t\t\t\tOPENCODE_PLUGIN_TOOLS_PORT: String(port),\n\t\t\t\tOPENCODE_PLUGIN_TOOLS_TOKEN: token,\n\t\t\t},\n\t\t},\n\t\tclose: () =>\n\t\t\tnew Promise<void>((resolve) => {\n\t\t\t\tserver.close(() => resolve());\n\t\t\t\t// Force-close lingering keep-alive sockets so dispose doesn't hang.\n\t\t\t\tserver.closeAllConnections?.();\n\t\t\t}),\n\t};\n}\n"],"mappings":"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","names":["rmSync","homedir","semver","out","s","homedir","tmpdir","join","mkdirSync","readFileSync","writeFileSync","cacheDir","cacheFile","require","mkdirSync","writeFileSync","readFileSync","existsSync","rmSync","readdirSync","statSync","realpathSync","join","relative","dirname","readFileSync","mkdirSync","rmSync","writeFileSync","join","homedir","tmpdir","join","tmpdir","rmSync","loaded","readFileSync","mkdirSync","writeFileSync","s","join","realpathSync","readdirSync","statSync","relative","mkdirSync","dirname","existsSync","readFileSync","writeFileSync","s","rmSync","existsSync","readdirSync","statSync","join","createRequire","homedir","tool","join","homedir","existsSync","createRequire","readdirSync","statSync","tool","s","existsSync","execSync","existsSync","execSync","tool","semver","wildcardMatch","pattern","homedir","input","rmSync"]}
|