diff --git a/AGENTS.md b/AGENTS.md index 9c99928..bce115c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -99,7 +99,7 @@ chars. When adding entries to `permission.bash`: - TypeScript with `strict: true`, compiled by `tsc` to `dist/`. Source in `bin/` and `src/`. - Internal imports use `.js` extensions even though source is `.ts` (NodeNext convention; tsc emits the `.js` files at the matching paths). - Edits → `npm run build` → `node dist/bin/...` (or `npm link` once for global use). -- One runtime dep: `chalk`. Don't add more without justification. +- Two runtime deps: `chalk`, `ajv`. Don't add more without justification. - Atomic writes: `writeFile(tmp); rename(tmp, final)`. Never write the target in place. - All paths use `node:path`'s `resolve`/`join`; never string-concat @@ -130,6 +130,16 @@ Do NOT add any of the following without an explicit human review: the only source of truth. - New runtime dependencies in general — see "Code conventions" above. +Third-party code in a preset (`plugin`, `mcp` command, `@fetch`) → pin +exact version, git tag, or SHA. Unpinned ref or `@latest` → justify in +`@description`. opencode caches by full spec string and never re-checks, +so an unpinned ref freezes at first resolve; changing the string is what +triggers the re-fetch. + +`@fetch` → always `sha256=`, and version the dest filename too; +`fetchAsset` skips an existing dest, so reusing the filename makes a bump +a no-op. Verify the hash against a second source before committing. + The `npm publish` step must pass `--provenance` so the published tarball carries SLSA build attestation verifiable via `npm audit signatures`. diff --git a/README.md b/README.md index a9fde77..446c19f 100644 --- a/README.md +++ b/README.md @@ -84,18 +84,18 @@ on a readline for the next. | Preset | Category | Mode | Description | | --- | --- | --- | --- | -| `jdtls-lombok` | LSP | replace | Makes jdtls lombok-aware via `-javaagent` flag | +| `jdtls-lombok` | LSP | replace | Makes jdtls lombok-aware via `-javaagent` flag (pinned to lombok 1.18.46, sha256-verified) | | `jdtls-clean-workspace` | LSP | replace | Stops jdtls from writing `.project`/`.classpath`/etc. into your project root | | `mcp-http` | MCP | replace | Add an HTTP MCP server (localhost or remote) with one custom header (prompts for id, URL, header name, header value) | | `mcp-http-noauth` | MCP | replace | Add an HTTP MCP server (localhost or remote) without auth headers (prompts for id, URL) | | `mcp-intellij` | MCP | replace | Add the JetBrains IDE MCP server (loopback HTTP, default port 64342) | | `mcp-litellm` | MCP | replace | Add a LiteLLM proxy's MCP gateway as a remote MCP server (prompts for gateway URL and LiteLLM key; auth via `x-litellm-api-key`, no login flow) | | `mcp-litellm-passthrough` | MCP | replace | Add one `x-mcp--
` passthrough header to the `mcp.litellm` server so an upstream MCP server authenticates as you (run once per header; install `mcp-litellm` first) | -| `mcp-playwright` | MCP | replace | Add the Playwright MCP server (`@playwright/mcp`, local stdio via npx) | +| `mcp-playwright` | MCP | replace | Add the Playwright MCP server (`@playwright/mcp`, local stdio via npx; pinned to an exact version) | | `mcp-vscode` | MCP | replace | Add the VS Code MCP server via the `JuehangQin.vscode-mcp-server` extension (loopback HTTP, default port 3000) | -| `plugin-litellm-pricing` | Plugin | append | Add `opencode-litellm-pricing` — discovers a LiteLLM proxy's models at runtime and adds them to the picker with real per-model pricing instead of `$0` (pair with `provider-litellm` to set the proxy URL and key) | +| `plugin-litellm-pricing` | Plugin | append | Add `opencode-litellm-pricing` — discovers a LiteLLM proxy's models at runtime and adds them to the picker with real per-model pricing instead of `$0` (pair with `provider-litellm` to set the proxy URL and key; pinned to an exact version) | | `provider-litellm` | Provider | replace | Point the `litellm` provider at your proxy URL for `plugin-litellm-pricing` (prompts for base URL and API key; no models list) | -| `plugin-superpowers` | Plugin | append | Add the Superpowers OpenCode plugin from `obra/superpowers` (brainstorming, plans, TDD, review workflows) | +| `plugin-superpowers` | Plugin | append | Add the Superpowers OpenCode plugin from `obra/superpowers` (brainstorming, plans, TDD, review workflows; pinned to an exact upstream tag) | | `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 | | `permissions-shell-safe` | Permissions | merge | Low-risk shell commands (ls, cat, grep, rg, jq, yq, etc.) | diff --git a/package-lock.json b/package-lock.json index d50b91f..87059cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -408,9 +408,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", "funding": [ { "type": "github", diff --git a/presets/jdtls-lombok.conf b/presets/jdtls-lombok.conf index 5dc4b25..80f8004 100644 --- a/presets/jdtls-lombok.conf +++ b/presets/jdtls-lombok.conf @@ -3,12 +3,13 @@ // a -javaagent flag pointing at lombok.jar to the jdtls command. The // LSP then resolves lombok-generated getters, setters, builders, etc. // instead of flagging them as missing. Requires `jdtls` and a JDK 21+ -// on PATH. +// on PATH. Pinned to an exact lombok version and verified by sha256; +// bump both the @fetch line and the -javaagent path here to update. // @author: Jan -// @version: 0.1.0 +// @version: 0.2.0 // @path: lsp.jdtls.command -// @fetch: https://projectlombok.org/downloads/lombok.jar -> {{cache}}/lombok.jar +// @fetch: https://projectlombok.org/downloads/lombok-1.18.46.jar -> {{cache}}/lombok-1.18.46.jar sha256=01f7b1a015e33e2b62d5f5f37053306357ab1415fd181fcba7794f5d198c1126 [ "jdtls", - "--jvm-arg=-javaagent:{{cache}}/lombok.jar" + "--jvm-arg=-javaagent:{{cache}}/lombok-1.18.46.jar" ] diff --git a/presets/mcp-playwright.conf b/presets/mcp-playwright.conf index bf71d96..13c7bbb 100644 --- a/presets/mcp-playwright.conf +++ b/presets/mcp-playwright.conf @@ -5,17 +5,20 @@ // navigate, click, fill forms, take screenshots, and read // accessibility snapshots — useful for end-to-end testing prompts // and "check this page in a browser" workflows. Runs via -// `npx -y @playwright/mcp@latest`; the first invocation downloads +// `npx -y @playwright/mcp@0.0.78`; the first invocation downloads // the package and (on first browser use) the Playwright browser -// binaries via `npx playwright install`. No prompts — accepts +// binaries via `npx playwright install`. Pinned to an exact version; +// bump it here to update. npx re-resolves the spec on every server +// start, so an unpinned `@latest` would silently pull new upstream +// code each time opencode launches the browser. No prompts — accepts // Playwright MCP's defaults (Chromium, headed). Hand-edit // opencode.json afterwards if you want --browser=firefox/webkit, // --headless, or other flags. // @author: Jan -// @version: 0.1.0 +// @version: 0.2.0 // @path: mcp.playwright { "type": "local", - "command": ["npx", "-y", "@playwright/mcp@latest"], + "command": ["npx", "-y", "@playwright/mcp@0.0.78"], "enabled": true } diff --git a/presets/plugin-litellm-pricing.conf b/presets/plugin-litellm-pricing.conf index e921d66..ba512b5 100644 --- a/presets/plugin-litellm-pricing.conf +++ b/presets/plugin-litellm-pricing.conf @@ -12,8 +12,8 @@ // proxy API key. Pinned to an exact version; bump it here to update. After installing a // plugin preset, quit and restart opencode. // @author: Jan -// @version: 0.2.1 +// @version: 0.3.0 // @path: plugin // @mode: append -["opencode-litellm-pricing@0.1.1"] +["opencode-litellm-pricing@0.2.0"] diff --git a/presets/plugin-superpowers.conf b/presets/plugin-superpowers.conf index c6ce917..c74f09f 100644 --- a/presets/plugin-superpowers.conf +++ b/presets/plugin-superpowers.conf @@ -2,10 +2,14 @@ // @description: Adds the Superpowers OpenCode plugin from obra/superpowers. // Registers the Superpowers skill set, including brainstorming, // writing-plans, executing-plans, TDD, code review, and related -// workflow skills. Uses OpenCode's git-backed plugin install. +// workflow skills. Uses OpenCode's git-backed plugin install, pinned +// to an exact upstream tag; bump it here to update. opencode resolves +// a plugin spec once and caches it by the full spec string, so an +// unpinned ref would freeze at whatever the default branch happened to +// be on the day you installed. // @author: Jan -// @version: 0.1.0 +// @version: 0.2.0 // @path: plugin // @mode: append -["superpowers@git+https://github.com/obra/superpowers.git"] +["superpowers@git+https://github.com/obra/superpowers.git#v6.2.0"] diff --git a/test/builtin-presets.test.ts b/test/builtin-presets.test.ts index 0a0cfb3..d954a3e 100644 --- a/test/builtin-presets.test.ts +++ b/test/builtin-presets.test.ts @@ -48,7 +48,7 @@ test('ships a litellm plugin preset that appends the runtime-discovery plugin', assert.equal(meta.name, 'plugin-litellm-pricing'); assert.equal(meta.path, 'plugin'); assert.equal(meta.mode, 'append'); - assert.deepEqual(body, ['opencode-litellm-pricing@0.1.1']); + assert.deepEqual(body, ['opencode-litellm-pricing@0.2.0']); }); test('ships a litellm provider preset that points at a proxy URL, no models', async () => {