From 36f7d8dbc549b1f16c2e688823cbda1300655d6e Mon Sep 17 00:00:00 2001 From: trick77 Date: Thu, 23 Jul 2026 19:56:45 +0200 Subject: [PATCH 1/2] feat: add plugin-litellm + provider-litellm presets Wire opencode to a LiteLLM proxy with runtime model discovery via the opencode-plugin-litellm plugin, instead of a hand-maintained models list. - plugin-litellm: appends opencode-plugin-litellm@0.5.0 to the plugin array. At startup the plugin queries the proxy's /v1/models and injects every model into the picker. For localhost with the key in $LITELLM_API_KEY the plugin auto-detects the URL and creates the provider itself, so this preset alone suffices. - provider-litellm: for a custom/remote proxy, sets provider.litellm (@ai-sdk/openai-compatible) with the prompted baseURL and apiKey={env:LITELLM_API_KEY}. Declares NO models; the plugin fills them. Plugin pinned to an exact version (repo supply-chain stance) rather than the README's @latest. Adds README rows and parseConf tests. --- README.md | 2 ++ presets/plugin-litellm.conf | 17 ++++++++++++++++ presets/provider-litellm.conf | 22 ++++++++++++++++++++ test/builtin-presets.test.ts | 38 +++++++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+) create mode 100644 presets/plugin-litellm.conf create mode 100644 presets/provider-litellm.conf diff --git a/README.md b/README.md index c5cfe0e..8dfcd5c 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,8 @@ on a readline for the next. | `mcp-intellij` | MCP | replace | Add the JetBrains IDE MCP server (loopback HTTP, default port 64342) | | `mcp-playwright` | MCP | replace | Add the Playwright MCP server (`@playwright/mcp`, local stdio via npx) | | `mcp-vscode` | MCP | replace | Add the VS Code MCP server via the `JuehangQin.vscode-mcp-server` extension (loopback HTTP, default port 3000) | +| `plugin-litellm` | Plugin | append | Add `opencode-plugin-litellm` — discovers a LiteLLM proxy's models at runtime and adds them to the picker (localhost auto-detected; key from `$LITELLM_API_KEY`) | +| `provider-litellm` | Provider | replace | Point the `litellm` provider at a custom/remote proxy URL for `plugin-litellm` (prompts for base URL; key via `$LITELLM_API_KEY`, no models list) | | `plugin-superpowers` | Plugin | append | Add the Superpowers OpenCode plugin from `obra/superpowers` (brainstorming, plans, TDD, review workflows) | | `permissions-git-safe` | Permissions | merge | Read-only git commands (status, diff, log, branch --list, fetch, etc.) | | `permissions-webfetch-ask` | Permissions | merge | Requires approval before opencode uses the webfetch tool | diff --git a/presets/plugin-litellm.conf b/presets/plugin-litellm.conf new file mode 100644 index 0000000..f56b6ef --- /dev/null +++ b/presets/plugin-litellm.conf @@ -0,0 +1,17 @@ +// @name: plugin-litellm +// @description: Adds the opencode-plugin-litellm plugin (npm +// opencode-plugin-litellm, repo yuseferi/opencode-litellm). At opencode +// startup it queries a LiteLLM proxy's /v1/models and injects every model +// into the picker at runtime — no hand-maintained models list. For a proxy +// on localhost:4000/8000/8080 with the key in $LITELLM_API_KEY (or +// $LITELLM_MASTER_KEY), this preset alone is enough: the plugin auto-detects +// the URL and creates the litellm provider itself. For a custom/remote URL, +// also install provider-litellm to set provider.litellm.options.baseURL. +// Pinned to an exact version; bump it here to update. After installing a +// plugin preset, quit and restart opencode. +// @author: Jan +// @version: 0.1.0 +// @path: plugin +// @mode: append + +["opencode-plugin-litellm@0.5.0"] diff --git a/presets/provider-litellm.conf b/presets/provider-litellm.conf new file mode 100644 index 0000000..00b6bd3 --- /dev/null +++ b/presets/provider-litellm.conf @@ -0,0 +1,22 @@ +// @name: provider-litellm +// @description: Points opencode's litellm provider at a custom/remote LiteLLM +// proxy URL for use with the plugin-litellm preset (opencode-plugin-litellm), +// which discovers the model list at runtime. Sets provider.litellm with +// @ai-sdk/openai-compatible, the proxy baseURL, and apiKey read from the +// $LITELLM_API_KEY environment variable — export it in your shell before +// starting opencode (the plugin reads $LITELLM_API_KEY / $LITELLM_MASTER_KEY +// directly for model discovery). Deliberately declares NO models: the plugin +// fills them in. Skip this preset if your proxy is on localhost with the key +// in env — plugin-litellm auto-detects that on its own. +// @author: Jan +// @version: 0.2.0 +// @path: provider.litellm +// @prompt: baseURL | text | LiteLLM proxy base URL (OpenAI-compatible /v1 endpoint) | http://localhost:4000/v1 +{ + "npm": "@ai-sdk/openai-compatible", + "name": "LiteLLM (proxy)", + "options": { + "baseURL": "{{prompt:baseURL}}", + "apiKey": "{env:LITELLM_API_KEY}" + } +} diff --git a/test/builtin-presets.test.ts b/test/builtin-presets.test.ts index 98e75cb..7fd4268 100644 --- a/test/builtin-presets.test.ts +++ b/test/builtin-presets.test.ts @@ -39,3 +39,41 @@ test('ships a runaway guard preset with step limits for built-in agents', async }, }); }); + +test('ships a litellm plugin preset that appends the runtime-discovery plugin', async () => { + const preset = resolve(process.cwd(), 'presets/plugin-litellm.conf'); + + const { meta, body } = await parseConf(preset); + + assert.equal(meta.name, 'plugin-litellm'); + assert.equal(meta.path, 'plugin'); + assert.equal(meta.mode, 'append'); + assert.deepEqual(body, ['opencode-plugin-litellm@0.5.0']); +}); + +test('ships a litellm provider preset that points at a proxy URL, no models', async () => { + const preset = resolve(process.cwd(), 'presets/provider-litellm.conf'); + + const { meta, body } = await parseConf(preset); + + assert.equal(meta.name, 'provider-litellm'); + assert.equal(meta.path, 'provider.litellm'); + assert.equal(meta.mode, 'replace'); + + // Only the base URL is prompted; the key comes from $LITELLM_API_KEY. + assert.deepEqual( + meta.prompts.map((p) => ({ name: p.name, type: p.type })), + [{ name: 'baseURL', type: 'text' }], + ); + + // No `models` block — the plugin discovers models at runtime. + // Body keeps the {{prompt:...}} placeholder until install-time expansion. + assert.deepEqual(body, { + npm: '@ai-sdk/openai-compatible', + name: 'LiteLLM (proxy)', + options: { + baseURL: '{{prompt:baseURL}}', + apiKey: '{env:LITELLM_API_KEY}', + }, + }); +}); From 252b49d2b01df7b3d32d271ded689c20ddc72137 Mon Sep 17 00:00:00 2001 From: trick77 Date: Thu, 23 Jul 2026 21:04:42 +0200 Subject: [PATCH 2/2] chore: release 0.9.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5458acc..807df22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "opencode-presets", - "version": "0.8.8", + "version": "0.9.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "opencode-presets", - "version": "0.8.8", + "version": "0.9.0", "license": "MIT", "dependencies": { "ajv": "8.20.0", diff --git a/package.json b/package.json index 8b85469..ddcb7e3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "opencode-presets", - "version": "0.8.8", + "version": "0.9.0", "description": "Interactive CLI that patches OpenCode config files with curated presets — LSP, MCP, permissions, TUI.", "type": "module", "bin": {