Contributing to Nerve
Thanks for wanting to help! This guide covers everything you need to start contributing.
Table of Contents
- Development Setup
- Project Structure
- Adding a Feature
- Testing
- Linting
- Commit Conventions
- Pull Request Process
- License
Development Setup
Prerequisites
- Node.js ≥ 22 — check with
node --version - npm (bundled with Node)
- A running OpenClaw gateway
Steps
Fork and clone the repository:
bashgit clone https://github.com/<your-username>/openclaw-nerve.git cd openclaw-nerveInstall dependencies:
bashnpm installConfigure environment:
bashnpm run setupThe interactive wizard auto-detects your gateway token and writes
.env. Alternatively, copy.env.exampleto.envand fill in values manually.Start development servers (two terminals):
bash# Terminal 1 — Vite frontend with HMR npm run dev # Terminal 2 — Backend with file watching npm run dev:serverOpen
http://localhost:3080. The frontend proxies API requests to the backend on:3081.
Project Structure
nerve/
├── src/ # Frontend (React + TypeScript)
│ ├── features/ # Feature modules (co-located)
│ │ ├── auth/ # Login page, auth gate, session hook
│ │ ├── chat/ # Chat panel, messages, input, search
│ │ ├── voice/ # Push-to-talk, wake word, audio feedback
│ │ ├── tts/ # Text-to-speech playback
│ │ ├── sessions/ # Session list, tree, spawn dialog
│ │ ├── workspace/ # Tabbed panel: memory, crons, skills, config
│ │ ├── file-browser/ # Workspace file browser with tabbed editor
│ │ ├── settings/ # Settings drawer (appearance, audio, connection)
│ │ ├── command-palette/ # ⌘K command palette
│ │ ├── markdown/ # Markdown renderer, code block actions
│ │ ├── charts/ # Inline chart extraction and rendering
│ │ ├── memory/ # Memory editor, add/delete dialogs
│ │ ├── activity/ # Agent log, event log
│ │ ├── dashboard/ # Token usage, memory list, limits
│ │ └── connect/ # Connect dialog (gateway setup)
│ ├── components/ # Shared UI components
│ │ ├── ui/ # Primitives (button, input, dialog, etc.)
│ │ └── skeletons/ # Loading skeletons
│ ├── contexts/ # React contexts (Chat, Session, Gateway, Settings)
│ ├── hooks/ # Shared hooks (WebSocket, SSE, keyboard, etc.)
│ ├── lib/ # Utilities (formatting, themes, sanitize, etc.)
│ ├── types.ts # Shared type definitions
│ └── test/ # Test setup
├── server/ # Backend (Hono + TypeScript)
│ ├── routes/ # API route handlers
│ ├── services/ # TTS engines, Whisper, usage tracking
│ ├── lib/ # Utilities (config, WS proxy, file watcher, etc.)
│ ├── middleware/ # Auth, rate limiting, security headers, caching
│ └── app.ts # Hono app assembly
├── config/ # TypeScript configs for server build
├── scripts/ # Setup wizard and utilities
├── docs/ # Documentation
├── vitest.config.ts # Test configuration
├── eslint.config.js # Lint configuration
└── vite.config.ts # Vite build configurationKey conventions
- Feature modules live in
src/features/<name>/. Each feature owns its components, hooks, types, and tests. @/import alias maps tosrc/— use it for cross-feature imports.- Tests are co-located with source files:
foo.ts→foo.test.ts. - Server routes are thin handlers that delegate to
services/andlib/.
Adding a Feature
Frontend
- Create a directory in
src/features/<your-feature>/. - Add your components, hooks, and types inside.
- Export the public API from an
index.tsbarrel file. - Wire it into the app (usually via
App.tsxor an existing panel component). - Write tests alongside your source files.
Backend
- Create a route file in
server/routes/<your-feature>.ts. - If you need business logic, add a service in
server/services/. - Register the route in
server/app.ts. - Add tests (co-located, e.g.
server/routes/<your-feature>.test.ts).
Both
- Update types in
src/types.tsif you're adding new WebSocket or API message shapes. - If your feature needs new environment variables, add them to
.env.exampleand document them indocs/CONFIGURATION.md.
Testing
Tests use Vitest with jsdom for React component testing and Testing Library for assertions.
npm test # Watch mode (re-runs on save)
npm test -- --run # Single run (CI-friendly)
npm run test:coverage # With V8 coverage report (text + HTML + lcov)Guidelines
- Co-locate tests with source:
useVoiceInput.ts→useVoiceInput.test.ts. - Use
@testing-library/reactfor component tests, plain Vitest for logic. - Test setup lives in
src/test/setup.ts(imports@testing-library/jest-dom). - Coverage excludes config files, type declarations, and test files themselves.
Linting
ESLint 9 with flat config. TypeScript-ESLint + React Hooks + React Refresh rules.
npm run lintKey rules:
react-hooks/exhaustive-deps: warn— keep dependency arrays honest.- TypeScript strict mode throughout.
- Ignores
dist/andserver-dist/.
Fix issues before committing. Your PR will fail CI if lint doesn't pass.
Commit Conventions
Use Conventional Commits:
<type>(<scope>): <short description>
[optional body]Types: feat, fix, docs, style, refactor, test, chore, perf, ci
Scope (optional): the feature or area — chat, tts, voice, server, sessions, workspace, etc.
Examples:
feat(chat): add image lightbox for inline images
fix(tts): handle empty audio response from Edge TTS
docs: update configuration guide with new env vars
refactor(server): extract TTS cache into service module
test(voice): add wake-word persistence testsPull Request Process
- Open an issue first for non-trivial changes. Discuss the approach before writing code.
- Branch from
master:git checkout -b feat/my-feature. - Keep PRs focused — one feature or fix per PR.
- Ensure all checks pass before requesting review:bash
npm run lint npm run build npm run build:server npm test -- --run - Fill out the PR template — describe what, why, and how.
- Include tests for new features. Bug fixes should include a regression test when feasible.
- Screenshots welcome for UI changes.
- A maintainer will review, possibly request changes, and merge.
License
By contributing, you agree that your contributions will be licensed under the MIT License.