Files
jubnl bc10fba389 feat(plugins): TREK_PLUGINS_IGNORE_TREK_RANGE turns the version gate into a warning (#2226)
* feat(plugins): TREK_PLUGINS_IGNORE_TREK_RANGE turns the version gate into a warning

A plugin whose author has not updated its `trek` range for the running TREK can
now be installed, updated and activated when the operator sets
TREK_PLUGINS_IGNORE_TREK_RANGE to a truthy value. Every gate (registry picker,
post-extraction re-check, sideload, dev-link, activation) warns instead of
refusing; a missing range is tolerated the same way; "install latest" takes the
newest published version. The plugin-API version gate is not lifted and the
Discover compatibility verdict stays truthful.

The admin is warned at every step: each bypass is logged, install/upload/link/
update responses carry a `trekRangeBypassed` marker, and both admin panels show
a "Version checks off" pill, an "Install anyway" confirm dialog before a
registry install, the same notice after a path that could not ask first, and a
persistent chip on the row while the plugin runs outside its range.

Nine new i18n keys in every locale; wiki pages updated (Environment-Variables,
Admin-Plugins, Plugin-Development, Security-Hardening).

* refactor(plugins): one useRangeBypass hook behind both admin shells

SonarQube flagged PR 2226 at 10.8% duplicated new code: the range-bypass
logic had been added to the desktop and phone plugin panels as identical
copies. The marker/warning types, the row chip, the "Install anyway" offer,
the dialog copy and the confirm-or-notice state now live in
useRangeBypass (next to useInstanceSettings, the existing shared-logic
precedent); each panel keeps only its own dialog/sheet markup.

* docs(claude): record the SonarQube gate rules and the no-English-placeholder i18n rule

The CI SonarCloud gate (Sonar way, on new code: >=80% coverage, <=3%
duplication, A ratings, hotspots reviewed) was only discoverable by waiting
20-25 minutes for the workflow. The root CLAUDE.md now states the conditions,
the desktop/mobile shell-mirror duplication trap and its shared-hook fix, and
how to check a PR with the sonarqube MCP first; client and server CLAUDE.md
point at it. shared/CLAUDE.md says every locale needs a real translation —
parity cannot tell an English placeholder from a translation.

* docs(claude): keep CLAUDE.md to commands, invariants, gates and philosophy

Verified every concrete claim in the five CLAUDE.md files against the code
and fixed the stale ones: the websocket server is the Nest realtime gateway,
MCP tools live in <domain>.mcp.ts files, the client "native shell" section
described code that was never written, offline maps also prefetch vector
basemaps, plus several renamed files and wrong table names.

Then trimmed everything that drifts on its own: dates, migration history,
counts, and inventories of stores, slices, domains, sync files and CLI
commands. What stays is the commands, the invariants with the file that
enforces each one, what CI actually runs, the reference implementations to
copy, and the rules for new code. Each file now opens with that rule.
2026-09-04 00:43:06 +02:00

59 lines
6.0 KiB
Markdown

# CLAUDE.md
## What this package is
`trek-plugin-sdk` — the author-facing SDK and CLI for building TREK plugins. It lives in the TREK monorepo but is **not a root npm workspace**: own `package-lock.json`, versioned and published to npm independently, and it must ship standalone (a published copy has no access to the rest of the repo). Run all commands from `plugin-sdk/`. The root `CLAUDE.md` conventions (target `dev`, conventional commits, no attribution trailers) still apply.
Three bins from one codebase: `trek-plugin-sdk` / `trek-plugin` (same entry, `src/cli/trek-plugin.ts`, one file per command under `src/cli/`) and `create-trek-plugin` (`src/cli/create.ts`). `README.md` here is the author guide; the `trek-plugin-dev` skill is the long-form authoring reference.
## Commands
```bash
npm install
npm run build # tsc ESM build + tsc CJS build + scripts/finish-cjs.mjs
npm run typecheck # tsc --noEmit (CI)
npm test # vitest run
npm run test:coverage # what CI runs (feeds the Sonar new-code coverage gate)
npx vitest run test/sdk.test.ts # single test file
node scripts/gen-lucide-icon-names.mjs # regenerate src/lucide-icon-names.ts (uses repo-root lucide-react)
```
Node >= 18, `"type": "module"`; the dual build emits ESM to `dist/` and CJS to `dist/cjs/` (finish-cjs.mjs writes the `{"type":"commonjs"}` marker there — without it Node parses the CJS output as ESM). There is no lint script here.
**Release**: a pushed `plugin-sdk-v*` tag runs `npm ci && npm publish` via the root `publish-plugin-sdk.yml`; `prepublishOnly` builds and tests first. The published version is whatever `package.json` says — bump it in the same PR as the SDK change. Never push the tag yourself.
## The load-bearing principle: mirrored host code + parity tests
The SDK's promise is that local results equal host/registry results: `dev` enforces what a real TREK enforces, and `validate` passes iff the TREK-Plugins registry CI passes. A drift in the lenient direction is the worst bug in this package — a **false green** lets an author cut an effectively immutable GitHub release (the registry pins its sha256) that the registry then rejects.
Because the package ships standalone it cannot import server code, so several files are deliberate **copies** of host sources, each guarded by a parity test that reads the original directly. Those tests skip outside the monorepo but run in this checkout — keep the pairs in sync when touching either side:
| SDK copy | Source of truth | Parity test |
|---|---|---|
| `src/generated/host-facts.ts` (permissions, RPC methods, hooks, event catalog) | `server/src/nest/plugins/protocol/envelope.ts` via `server/scripts/gen-plugin-facts.ts` — **machine-written, never hand-edit** | server `check:plugin-facts` CI gate |
| `src/egress-policy.ts` (pure helpers, kept **byte-identical**) | `server/src/nest/plugins/runtime/egress-policy.ts` | `test/permissions-parity.test.ts` |
| `src/manifest.ts` (validation rules incl. the `trek` version range, `KNOWN_ADDONS`) | `server/src/nest/plugins/install/` + `server/src/addons.ts` | kept in sync by hand |
| `src/cli/checks/*` (registry gates) | TREK-Plugins registry repo (`scripts/validate-entry.mjs`, `check-readme.mjs`) | `test/checks-parity.test.ts` (runs the registry's real script; `TREK_PLUGINS_REPO` points at a checkout) |
| `src/zip.ts` (ZIP writer) | server `safe-extract.ts` reader | format frozen against the reader |
**Direction: single-source the contract.** The hand-kept copies are the residual risk, and the parity tests' skip-outside-the-monorepo guards mean the published SDK's own CI can green-light drift in them — keep moving copies into the generated/parity-tested set, never away from it. Changing an egress rule, manifest rule or registry gate means touching **every** copy in one change; parity gates fail closed; version/compat checks fail **closed** on unknown host versions.
A subtlety documented in `src/permissions.ts`: ctx methods fail loudly at call time, but hooks/events/jobs are gated *before* the plugin is reached and fail **silently** in production — so the dev server and mock host deliberately make those failures loud.
## Architecture
- **`src/index.ts`** — the plugin API surface: all types, `definePlugin`, `PLUGIN_API_VERSION` (bump on any breaking API change). Pure and dependency-free; it mirrors exactly what TREK's plugin runtime injects. Runtime deps for the whole package are only `@clack/prompts`, `semver`, `update-notifier` — keep it that way (Playwright for `shot` is the author's devDependency; the ZIP writer is in-tree).
- **Two package exports**: `.` (the SDK) and `./testing` (`src/mock-host.ts`) — a mock `PluginContext` enforcing the same permission model against fixtures.
- **`src/cli/`** — interactive TTY sessions get prompts; non-TTY stays flag-driven. Machine output (`entry` JSON, `pack --json`, PR URLs) goes to stdout, notices to stderr.
- **`src/cli/checks/`** — one check registry, two depths: `runOffline` (synchronous on purpose, so packing can validate before zipping) and `runAll` (adds the GitHub-dependent gates used by `preflight`/`publish`).
- **`src/ui/kit.ts`** — the design kit as plain strings, inlined where the `<!-- trek:ui -->` marker sits in a plugin's `client/index.html` (the opaque-origin iframe CSP forbids external link/script). Not a security boundary.
- **`src/lucide-icon-names.ts`** — generated snapshot; an unknown icon is a validate **warning**, never an error.
- **`test/helpers.ts`** — `makePublishable(dir)` turns a fresh scaffold into a registry-passing plugin; the scaffold deliberately fails publish gates, so tests choose "fresh" vs "publishable" explicitly.
## Behavioral contracts to not break
- `status` never exits non-zero; `validate` is the enforcing form.
- `publish` order is check → pack → release → preflight → submit, and a check failure must stop anything from being tagged or released.
- Signed → unsigned is refused forever; unsigned → signed is always allowed.
- Update notices print to stderr only and are silent in CI/`NODE_ENV=test`/non-TTY, so JSON output stays pipeable.