Contributing

wsp is AGPL-3.0-only. By sending a change you agree it is licensed the same way. New source files start with // SPDX-License-Identifier: AGPL-3.0-only.

File bugs and requests at github.com/Zingzy/wsp/issues. Say what you ran, what you saw, and the output of wsp --version. Never paste a key.

Setting up

Node 22 or newer and pnpm.

pnpm install && pnpm build
pnpm test                    # no key needed, creates nothing
pnpm -r exec tsc --noEmit

On a small computer run vitest by file: pnpm exec vitest run --minWorkers=1 --maxWorkers=1 <files>. The live tests, which create real machines, are described on Releases.

The command line package and the desktop app bundle their dependencies into one file, so their build uses whatever those last built. Building either on its own starts with pnpm --filter <name> build:deps. pnpm build and pnpm release already build everything in dependency order.

The rules the code follows

These are the decisions the project has already paid for. A change that breaks one is sent back, however good the rest is.

Dependencies point one way. Clients, the app, the command line and the desktop shell, import @wsp/protocol. The runtime imports the engine, the engine imports a backend. A client importing the engine, or the protocol importing anything, is wrong.

Wire types have one home. Every message shape lives in @wsp/protocol as a zod schema. A second copy in the daemon, the runtime or the app is a bug, even when it matches today.

Text is formatted in one place. Bytes, durations, costs, memory sizes and the lines a person reads come from one module in the protocol package. Shell text goes through shellQuote from the same package. A test finds copies.

Anything that varies by kind sits behind one interface with one module per variant. Machine providers, agents, install roads, sign-in kinds, config paths, status checks, renderer targets: each has a registry, and adding a variant touches the registry entry and its module, nothing else. A switch on an agent, tool, provider or road id outside its registry is a bug. So is a second copy of a predicate, a path rule, a marker parser or a size rule.

Provider details stay in the engine. Solari assumptions above the engine are wrong; clients read capabilities flags instead of assuming.

The daemon's token is the only gate on 0.0.0.0. It travels in the first WebSocket frame, never in a URL. No handler is registered before that frame passes. Nothing in wsp collects, stores or proxies a model provider's credentials outside the person's own computer and machines. Key material is never printed or committed; fake keys in tests look fake.

No curl | sh in anything that installs software a person chose. Release binaries are pinned by sha256.

Comments state a constraint the code cannot show, in one line. No narration of what was changed, no ticket numbers, no TODO, FIXME or HACK.

No em dashes anywhere: code, comments, docs, commit messages, UI text.

Tests

Logic gets a test that failed before the change and passes after; say so in the pull request, or say why not. Components get a render test and an interaction test at least.

Unit tests never touch the cloud: fake backends, stores and daemons run in process. pnpm test is green with no key set, and tsc --noEmit is clean, before anything merges. App tests run under jsdom through the root vitest workspace.

Never kill a process by pattern (pkill -f, killall) in a test, a script or your own cleanup. Start processes with a recorded pid and kill that pid.

The app

Layout follows the approved design. State is muted monospace text, never a chip or a badge. Rows in a list are uniform. Colors come from the theme block in the app's stylesheet, never literals in a component. Green means running and nothing else; orange means spend or a confirmation; everything else is zinc. Terminal bytes flow from the daemon to the pane without a React re-render per chunk. Check both themes, and re-run the Chromium test when one covers the surface you changed.

Commits and pull requests

One change per commit, however many files it touches. The subject is a conventional one under 72 characters, then a blank line, then two to five lines on why the change exists, not what it does. No trailers.

A pull request links the issue it answers, says what it does in two sentences, and reports any place where it departs from what the issue asked and why. Deviations that are not reported are sent back even when the code is right.