@stablekernel/opencode-cursor 0.7.0-next.0 → 0.7.1-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -3
- package/README.md +83 -4
- package/dist/{chunk-COE6ZXMP.js → chunk-RDY3H2LE.js} +7 -6
- package/dist/chunk-RDY3H2LE.js.map +1 -0
- package/dist/plugin/index.js +795 -8
- package/dist/plugin/index.js.map +1 -1
- package/dist/provider/index.d.ts +7 -0
- package/dist/provider/index.js +6 -2
- package/dist/provider/index.js.map +1 -1
- package/package.json +4 -2
- package/dist/chunk-COE6ZXMP.js.map +0 -1
package/dist/plugin/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/plugin/index.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"],"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 { getLocalVersion, getLatestVersion, clearVersionCache, PLUGIN_CACHE_PATH } from \"../version-check.js\";\nimport { removeSystemRule } from \"../provider/system-rule.js\";\nimport { clearLogBridge, setLogBridge } from \"../provider/log-bridge.js\";\nimport {\n\tclearSubagentBridge,\n\tsetSubagentBridge,\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> = (async () => {\n\t\ttry {\n\t\t\tif (process.env.CI || process.env.NO_UPDATE_NOTIFIER) return null;\n\t\t\tconst local = getLocalVersion();\n\t\t\tconst latest = await _latestVersionPromise;\n\t\t\tif (!local || !latest || !semver.gt(latest, local)) return null;\n\t\t\treturn { local, latest };\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\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\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// 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<\n\t\t\t\tstring,\n\t\t\t\tunknown\n\t\t\t>;\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\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\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},\n\t\t\t\tmodels: { ...toOpencodeModels(models), ...(existing.models ?? {}) },\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);\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(\n\t\t\t\t\t\t\tliveMcp,\n\t\t\t\t\t\t\tstatus,\n\t\t\t\t\t\t).filter((name) => !warnedOAuth.has(name));\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\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: { local: undefined, latest: undefined, status: \"disabled\" as const },\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: \"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// Plugin is outdated — clear the opencode plugin cache so it re-fetches on next launch.\n\t\t\t\tconst cachePath = PLUGIN_CACHE_PATH;\n\t\t\t\tconst removeCommand = process.platform === \"win32\"\n\t\t\t\t\t? `rmdir /s /q \"${cachePath}\"`\n\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(\n\t\t\t\t\t\t(m) => `- ${m.id} — ${m.displayName}`,\n\t\t\t\t\t);\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 so it doesn't\n\t\t\t// linger in the user's workspace / Cursor IDE after the session ends.\n\t\t\t// Uses the same canonical cwd the provider wrote to; sentinel-guarded,\n\t\t\t// so a user-owned opencode.mdc is never deleted.\n\t\t\tremoveSystemRule(resolvedCwd);\n\t\t\tclearSubagentBridge();\n\t\t\tclearLogBridge();\n\t\t},\n\t};\n};\n\nexport default CursorPlugin;\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 { fingerprintApiKey, resolveCursorApiKey } from \"./api-key.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\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(items: ModelListItem[]): Record<string, OpencodeModelConfigEntry> {\n const out: Record<string, OpencodeModelConfigEntry> = {};\n for (const item of items) {\n const params = defaultModelParams(item);\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 };\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 { 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. Fields opencode does not get\n * from the Cursor catalog are filled with safe defaults (zero cost — Cursor\n * bills separately; generous context limits).\n */\nexport function buildModelV2Map(items: ModelListItem[]): 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: { input: 0, output: 0, cache: { read: 0, write: 0 } },\n limit: { context: 200_000, output: 32_000 },\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\";\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 ...(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 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 } 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}\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...(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\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"],"mappings":"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","names":["rmSync","semver","out","s","homedir","tmpdir","join","mkdirSync","readFileSync","writeFileSync","cacheDir","cacheFile","require","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"],"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 { getLocalVersion, getLatestVersion, clearVersionCache, PLUGIN_CACHE_PATH } from \"../version-check.js\";\nimport { removeSystemRule } from \"../provider/system-rule.js\";\nimport { clearLogBridge, pluginLog, setLogBridge } 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} 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> = (async () => {\n\t\ttry {\n\t\t\tif (process.env.CI || process.env.NO_UPDATE_NOTIFIER) return null;\n\t\t\tconst local = getLocalVersion();\n\t\t\tconst latest = await _latestVersionPromise;\n\t\t\tif (!local || !latest || !semver.gt(latest, local)) return null;\n\t\t\treturn { local, latest };\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\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\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// 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<\n\t\t\t\tstring,\n\t\t\t\tunknown\n\t\t\t>;\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\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: { ...toOpencodeModels(models), ...(existing.models ?? {}) },\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);\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(\n\t\t\t\t\t\t\tliveMcp,\n\t\t\t\t\t\t\tstatus,\n\t\t\t\t\t\t).filter((name) => !warnedOAuth.has(name));\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 =\n\t\t\t\t\t\t\t\tbuildSkillsCatalogue(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\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: { local: undefined, latest: undefined, status: \"disabled\" as const },\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: \"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// Plugin is outdated — clear the opencode plugin cache so it re-fetches on next launch.\n\t\t\t\tconst cachePath = PLUGIN_CACHE_PATH;\n\t\t\t\tconst removeCommand = process.platform === \"win32\"\n\t\t\t\t\t? `rmdir /s /q \"${cachePath}\"`\n\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(\n\t\t\t\t\t\t(m) => `- ${m.id} — ${m.displayName}`,\n\t\t\t\t\t);\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 * 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 { resolveContextLimit, resolveCost, resolveOutputLimit } 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. Both fields are required by the schema.\n */\n limit: { context: 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;\nconst _limitKeyGuard: _KeysAccepted<\n OpencodeModelConfigEntry[\"limit\"],\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(items: ModelListItem[]): 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 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 { resolveContextLimit, resolveCost, resolveOutputLimit } 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(items: ModelListItem[]): 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: { context: resolveContextLimit(item.id), output: resolveOutputLimit(item.id) },\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\";\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 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\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"]}
|