From d32d5fb701c87b85b7c7fe1f92665bc004f40664 Mon Sep 17 00:00:00 2001 From: ArthurErlich Date: Sat, 3 Oct 2026 20:38:09 +0200 Subject: [PATCH] docs: add README, prepare changelog for 0.0.1, fix plugin skill loading rules Co-Authored-By: Claude Sonnet 5.5 --- .claude/skills/opencode-v2-plugin/SKILL.md | 21 +++--- CHANGELOG.md | 10 +++ README.md | 85 ++++++++++++++++++++++ 3 files changed, 106 insertions(+), 10 deletions(-) create mode 100644 README.md diff --git a/.claude/skills/opencode-v2-plugin/SKILL.md b/.claude/skills/opencode-v2-plugin/SKILL.md index 97430f0..fcb576a 100644 --- a/.claude/skills/opencode-v2-plugin/SKILL.md +++ b/.claude/skills/opencode-v2-plugin/SKILL.md @@ -45,26 +45,27 @@ await ctx.websearch.transform((editor) => { { "plugins": [ { - "package": "~/.config/opencode/plugins/fastcrw", + "package": "@haylan/opencode-fastcrw", "options": { "baseURL": "http://crw.home:3000", "limit": 8 }, }, ], - "websearch": { "provider": "fastcrw" }, } ``` - Key is plural `plugins` (v1: `plugin`). Entries are a string or `{ package, options }`. -- `package` may be an npm name (`name`, `name@1.2.0`, `@scope/name`), `./local`, an absolute path or `file://`. -- Auto-discovered: `.ts`/`.js` files and packages in `.opencode/plugins/` and `~/.config/opencode/plugins/`. -- `websearch.provider` is a provider id or `"random"`; `"websearch": false` disables search. Built-in ids: exa, firecrawl, parallel, tavily, tinyfish. -- CLI: `opencode plugin add|list|check|update|remove`. +- `package` may be an npm name (`name`, `name@1.2.0`, `@scope/name`), `./relative` (resolved from the config file), an absolute path or `file://`. `~` is not expanded. +- **Local paths must be directories**, resolved by file path (`/server`, then `/index`); `package.json` `exports` are ignored. A file entry is ignored with a warning, and a directory without a root `index`/`server` file loads silently nothing. npm names resolve through `exports`. +- Auto-discovered: `.ts`/`.js` files (not `.mjs`) and directories in `.opencode/plugins/` and `~/.config/opencode/plugins/`. Drop-in files get no options. +- `websearch.provider` is a provider id or `"random"`; `"websearch": false` disables search. Built-in ids: exa, firecrawl, parallel, tavily, tinyfish. A provider named here **wins** over a plugin's `editor.default.set`; with it unset, the plugin's default applies. +- CLI: `opencode plugin add|list|check|update|remove` (`add` writes the global config). +- Loading is lazy: after startup or reload the plugin list fills in after several seconds. Load errors show as `state.status: "failed"` in `GET /api/plugin` (private server: `opencode serve`, Basic auth user `opencode`). ## Packaging (npm) `"type": "module"`, `exports["."]` pointing at the entry, dependency on `@opencode/plugin`. -## Unverified: check at runtime, do not assume +## Still unverified -- Exact TypeScript types for the editor, `time` and `execute` args (read `@opencode/plugin` typings after install). -- Inner shape of `time`, and whether extra result fields are allowed. -- Precedence between config `websearch.provider` and `editor.default.set(id)`. +- Installing by name from the Gitea registry with a scoped `.npmrc`. + +Verified results: `docs/runtime-checks.md`. diff --git a/CHANGELOG.md b/CHANGELOG.md index ffb1aba..20abf93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,3 +5,13 @@ All notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased] + +## [0.0.1] - 2026-10-03 + +### Added + +- opencode v2 plugin `fastcrw.websearch` that registers a `fastcrw` search provider backed by a self-hosted fastCRW server (`POST /v1/search`) and makes it the default websearch provider. +- Plugin options `baseURL`, `apiKey`, `limit`, `lang` and `tbs`, with `CRW_API_URL` and `CRW_API_KEY` as environment fallbacks. +- Validation at plugin load: a bad option makes the plugin fail with a message naming the option. +- Mapping of both fastCRW response shapes (`data[]` and `data.results`) and descriptive errors for API failures, non-JSON responses, unreachable servers and timeouts. +- Gitea Actions workflows for pull request checks and for publishing releases to the Gitea npm registry. diff --git a/README.md b/README.md new file mode 100644 index 0000000..0752fed --- /dev/null +++ b/README.md @@ -0,0 +1,85 @@ +# @haylan/opencode-fastcrw + +An [opencode](https://opencode.ai) v2 plugin that makes a self-hosted [fastCRW](https://docs.fastcrw.com) server opencode's websearch. It calls `POST {baseURL}/v1/search` directly; it does not use the `crw-mcp` MCP server. + +The plugin registers a search provider with id `fastcrw` and makes it the default. If fastCRW fails, searches fail; they never fall back to the built-in providers (Exa, Firecrawl, Parallel, Tavily, TinyFish). + +## Install + +The package is published to the Gitea npm registry. Map the scope to it in your `.npmrc`: + +``` +@haylan:registry=https://git.arthurerlich.de/api/packages/haylan/npm/ +``` + +Then add the plugin to your `opencode.json`: + +```jsonc +{ + "plugins": [ + { + "package": "@haylan/opencode-fastcrw", + "options": { "baseURL": "http://crw.home:3000" }, + }, + ], +} +``` + +Installing by name from the Gitea registry has not been verified end to end yet; see [docs/runtime-checks.md](docs/runtime-checks.md). + +### Local build + +To try the plugin without a registry: + +```bash +npm ci +npm run build +``` + +opencode loads local plugins as directories, resolved by file path (`/server`, then `/index`), and ignores `package.json` `exports`, so this package cannot be pointed at directly. Use a small shim instead. Create `~/.config/opencode/plugins/fastcrw.js` (a `.js` or `.ts` file; `.mjs` is ignored) with: + +```js +export { default } from "file:///ABSOLUTE/PATH/TO/fastcrw-opencode-websarch/dist/index.js" +``` + +A drop-in file cannot receive plugin options, so configure the plugin with the environment variables below (`CRW_API_URL`, `CRW_API_KEY`). To use options instead, put the same line in an `index.js` inside a directory and reference that directory as `"package": "./that-dir"` (a relative path is resolved from the config file; `~` is not expanded). + +## Configuration + +Plugin options come from `opencode.json`. Precedence is option, then environment variable, then default. + +| Option | Environment variable | Default | Notes | +| --------- | -------------------- | ----------------------- | --------------------------------------------------------------------------------------- | +| `baseURL` | `CRW_API_URL` | `http://localhost:3000` | Base URL of the fastCRW server, `http` or `https`. Trailing slashes are removed. | +| `apiKey` | `CRW_API_KEY` | none | Optional. Sent as a Bearer token. Prefer the environment variable over `opencode.json`. | +| `limit` | none | `8` | Results per search, clamped to 1 to 20. An invalid value falls back to 8. | +| `lang` | none | unset | Search language, for example `en` or `de`. Omitted from the request when unset. | +| `tbs` | none | unset | Time range: `qdr:h`, `qdr:d`, `qdr:w`, `qdr:m` or `qdr:y`. | + +A bad `baseURL`, `tbs`, `lang` or `apiKey` makes the plugin fail to load, with a message naming the option. opencode keeps running and shows the plugin as failed. + +### Choosing the provider + +Do not set `websearch.provider` to another provider. In opencode, a provider named in `opencode.json` wins over the one a plugin sets as default, so `"websearch": { "provider": "exa" }` would send your searches to Exa. Leaving it unset, or setting it to `"fastcrw"`, uses this plugin. + +## Errors + +Failures are thrown as `fastCRW : `, with a hint for a missing or invalid key (401) and for a server without a search backend (503 `search_disabled`). A server that is unreachable, times out (30 s) or returns something that is not JSON gets its own message. opencode currently shows only a generic "Web search request failed: fastcrw", so check the server when searches fail. + +A self-hosted fastCRW server needs a SearXNG-style search backend (`CRW_SEARCH__SEARCH_BACKEND_URL`); without one, searches return 503. + +## Development + +```bash +npm ci +npm run check # Prettier and type check +npm test # node --test, needs Node 22.18 or later +npm run build # compile to dist/ +npm run smoke -- "query" # one real search against CRW_API_URL (manual) +``` + +`docs/spec.md` is the build spec, `docs/runtime-checks.md` records what was verified against a real opencode, and `CONTEXT.md` defines the vocabulary. + +### Releasing + +Releases are published by the manually started `release` workflow on Gitea Actions. It publishes the latest version heading in `CHANGELOG.md` (the repository keeps the dev version `0.0.0-dev` in `package.json`) and skips versions that already exist. To release: add a dated `## [x.y.z]` entry to `CHANGELOG.md`, merge it to `main`, then start the workflow. It needs a `PACKAGE_TOKEN` secret with `write:package` rights unless the job token already has them.