Skip to content

Contributing

Thanks for your interest in contributing to Warden! This guide covers everything you need to get started.

  • Good first issues are labeled good first issue on GitHub.
  • Feature requests and bugs are tracked in GitHub Issues. Comment on an issue to claim it before starting work.
  • If you have an idea that isn’t tracked yet, open an issue first to discuss the approach.
Terminal window
git clone https://github.com/thesimonho/warden.git
cd warden
go mod download
npm --prefix web install

Start the dev servers (Go + Vite):

Terminal window
just dev

Open http://localhost:5173 (Vite proxies /api/* to Go on :8090).

The dev server uses a separate database (/tmp/warden-dev/warden.db) so development and testing don’t interfere with your production Warden data at ~/.config/warden/. E2E tests use their own database at /tmp/warden-e2e-db/.

EnvironmentDB locationPort
Production (warden-desktop)~/.config/warden/:8090
Development (just dev)/tmp/warden-dev/:8090
E2E tests (standalone)/tmp/warden-e2e-db/:8090
E2E tests (piggybacking)Same as dev serverDev server port

Note: dev and production both use :8090, so they cannot run simultaneously. If you need to use Warden as a user, stop the dev server first.

E2E tests will piggyback on a running dev server if one is available, otherwise they start their own. Test projects are cleaned up automatically after each run.

The container image is built on CI when changes to container/ are pushed to main. For faster iteration during development, build the image locally:

Terminal window
docker build -t ghcr.io/thesimonho/warden:latest ./container

New containers created from the dashboard will use the locally built image. Existing containers need to be recreated to pick up the changes.

Terminal window
go test ./... # Go unit tests
npm --prefix web run test # Frontend unit tests (Vitest)
npm --prefix web run typecheck # TypeScript type checking
npm --prefix web run test:e2e # E2E tests (Playwright)

Run a single test:

Terminal window
go test ./engine/ -run TestParseGitWorktreeList # Single Go test
npm --prefix web run test -- --run lib/cost.test.ts # Single frontend test
npm --prefix web run test:e2e -- --grep "should connect terminal" # Single E2E test

Run these before submitting a PR:

Terminal window
# Go
golangci-lint run ./... # Linting
# Frontend
npm --prefix web run format # Prettier formatting
npm --prefix web run lint # ESLint
npm --prefix web run typecheck # TypeScript type checking

Go formatting is handled automatically by gofmt via gopls.

For architecture diagrams, project structure, and how the engine, API, and clients fit together, see the Architecture page.

Key directories for contributors:

DirectoryWhat lives here
engine/Container engine API wrapper (Docker)
service/Business logic layer
api/API contract types (request/response structs)
db/SQLite database store
eventbus/File-based event system (watcher, SSE broker)
agent/Multi-agent abstraction (registry, parsers, status providers)
internal/server/HTTP server, API routes, middleware
internal/terminal/WebSocket-to-PTY proxy
web/React + Vite frontend
container/Project container image and devcontainer feature

For detailed code maps, see docs/developer/codemaps/README.md for an index of all maps.

These rules are important to follow when contributing:

  1. The web SPA must only use HTTP calls to /api/v1/* — it serves as a reference implementation for developers building their own frontends.
  2. The TUI must be written against a Client interface — satisfiable by both the embedded service and the HTTP client.
  3. API routes include agentType as a path segment — all project-scoped routes follow the pattern /api/v1/projects/{projectId}/{agentType}/... to enforce the compound primary key (projectID + agentType).
  4. New API types go in api/ — shared by service/, client/, and the TUI.
  5. internal/ packages stay internalserver/ and terminal/ are HTTP plumbing, not public API.
  6. All audit writes go through db.AuditWriter — never call db.Store.Write() directly for audit events.
  7. PRs touching agent/ should include tests for both parsers — Claude Code and Codex each have their own JSONL parser in agent/claudecode/ and agent/codex/. Changes to shared parsing logic must be validated against both.
  8. Agent CLIs are installed at container startup, not in the imageinstall-agent.sh installs the correct CLI using pinned versions from agent/versions.go. Agent-specific event scripts live in container/scripts/claude/ and container/scripts/codex/.

Trunk-based. Branch off main for features/fixes, PR back in when ready. Squash merged to main.

Merges to main trigger release-please PR and changelog generation. Merging the release-please PR cuts a release and triggers builds/deployments/tags.

Before opening a PR:

  • Code compiles and passes all tests (go test ./..., npm --prefix web run test)
  • Frontend type checks pass (npm --prefix web run typecheck)
  • Linting passes (golangci-lint run ./..., npm --prefix web run lint)
  • Frontend code is formatted (npm --prefix web run format)
  • New API endpoints have OpenAPI annotations (see internal/server/routes.go)
  • Documentation is updated if behavior changed

Use Conventional Commits:

feat: add cost export to CSV
fix: resolve WebSocket reconnection race
refactor: extract symlink resolver from engine
docs: update integration guide for Go client
test: add E2E tests for worktree lifecycle

Your PR will be checked by these workflows:

WorkflowTriggerWhat it does
ci.ymlPRs targeting mainGo tests, TS typecheck, TS tests
release-please.ymlPush to mainAutomated release creation and version tagging
release-build.ymlRelease published, or manual dispatchCross-platform builds, installers (DMG/deb/rpm/Arch/AppImage/Inno Setup), checksums, SBOM
container.ymlPush to main (container/**), releaseBuild container image + devcontainer feature (:latest on push, semver on release)
container-scheduled.ymlDaily schedule (5 AM UTC)Validates container image, builds CLIs, validates JSONL parsers, pushes only on success
LayerTechnology
BackendGo (net/http), Docker Engine API
FrontendReact 19, Vite 7, TypeScript
UIshadcn/ui, Tailwind CSS v4
Terminalxterm.js via WebSocket to Go proxy
ContainerUbuntu 24.04, tmux, Claude Code CLI, Codex CLI
Dev toolsjust (task runner)