Contributing Guide
Thank you for your interest in contributing to Ever Works. This guide covers everything you need to know to make meaningful contributions to the project, whether you are fixing a bug, adding a feature, improving documentation, or building a new plugin.
Repositories
Ever Works is split across multiple repositories under the ever-works GitHub organization:
| Repository | Description |
|---|---|
| ever-works | Platform monorepo (API, Web Dashboard, CLI, AI agents, plugins) |
| directory-web-template | Standalone Next.js work website template |
| ever-works/apps/docs | Documentation site (this site), built from the platform monorepo |
Each repository has its own issue tracker. File issues in the repository most relevant to your contribution.
Every page on docs.ever.works is a Markdown file under docs/ in the ever-works monorepo, rendered by the Docusaurus app in apps/docs/. apps/docs/docusaurus.config.ts sets path: '../../docs/' and routeBasePath: '/', and points editUrl at https://github.com/ever-works/ever-works/tree/main/ — which is why the Edit this page link at the bottom of any page opens the exact source file in ever-works. Run the site locally with pnpm dev:docs, and open documentation issues and PRs against ever-works.
Prerequisites
Before you begin, make sure you have the following installed:
- Node.js >= 22 (LTS recommended; matches the
node:22-alpineDocker base image and the rootpackage.jsonengines field) - pnpm >= 10.x (strictly enforced; do not use npm or yarn)
- Git >= 2.30
- Docker (optional, for running the Platform locally with containers)
- PostgreSQL (optional, for Template development with a real database; SQLite works for local dev)
Installing pnpm
If you do not already have pnpm installed:
# Using corepack (recommended, ships with Node.js 20+)
corepack enable
corepack prepare pnpm@latest --activate
# Or via npm (one-time bootstrap)
npm install -g pnpm
Important: The repositories use packageManager fields and lockfiles that are specific to pnpm. Running npm install or yarn install will fail or produce incorrect dependency trees.
Development Setup
Platform (Monorepo)
git clone https://github.com/ever-works/ever-works.git
cd ever-works
pnpm install
# Start every app in watch mode (docs and the desktop apps are excluded)
pnpm dev:apps
# Or start individual apps
pnpm dev:api # NestJS API on port 3100
pnpm dev:web # Next.js Web Dashboard on port 3000
pnpm dev:docs # Docusaurus documentation site (this site)
The dev:* scripts in the root package.json are thin wrappers over Turborepo filters — pnpm dev:docs runs turbo run dev --filter=ever-works-docs, which runs docusaurus start in apps/docs/.
Template (Standalone)
git clone https://github.com/ever-works/directory-web-template.git
cd directory-web-template
pnpm install
# Copy environment file and configure
cp .env.example .env.local
# Edit .env.local with your values (see README for details)
pnpm dev # Next.js dev server on port 3000
Code Standards
TypeScript
Both repositories use TypeScript everywhere. Do not introduce plain .js files. Follow strict TypeScript practices:
- Enable and respect
strictmode settings intsconfig.json - Prefer explicit return types on exported functions
- Use
unknownoveranywhere possible - Validate input with Zod (Template) or class-validator (Platform)
Formatting (Prettier)
Formatting is enforced via Prettier. The configuration lives in the root package.json of each repository:
{
"printWidth": 120,
"singleQuote": true,
"semi": true,
"useTabs": true,
"tabWidth": 4,
"arrowParens": "always",
"trailingComma": "none",
"quoteProps": "as-needed"
}
Key rules:
- Indentation: Tabs with a width of 4 (except SCSS and YAML files, which use 2 spaces)
- Print width: 120 characters
- Quotes: Single quotes
- Semicolons: Always
- Trailing commas: None
Run the formatter before committing:
pnpm format # Format all files
pnpm format:check # Check without modifying (CI-friendly)
Linting (ESLint)
ESLint is configured per repository. Run it with:
pnpm lint
The Platform uses ESLint with TypeScript-specific rules across all workspaces. The Template uses the flat ESLint config (eslint.config.mjs) with React, React Hooks, and TypeScript plugins.
Naming Conventions
| Element | Convention | Example |
|---|---|---|
| Files | kebab-case | auth.service.ts, user-profile.tsx |
| Classes, Interfaces, Types | PascalCase | WorkService, UserProfile |
| Functions, Variables | camelCase | getWorkById, itemCount |
| Constants | UPPER_SNAKE_CASE | MAX_RETRY_COUNT, DEFAULT_LOCALE |
Before You Open a Pull Request
Pull requests no longer run the test suites. Since 2026-09-14 ci.yml runs only on
pushes to stage and main — a PR into develop produces zero check runs. The reason is
in the header of .github/workflows/ci.yml: the suite expands to ~15 checks on the shared
self-hosted ARC pool, and at 85 PR-triggered runs a week it was starving the deploy lanes.
That moves the first line of defence onto your machine. Nothing enforces it, so this is a contract, not a gate.
The fast tier — run this every time (seconds)
pnpm format:check
pnpm lint
This is not busywork: of the last 100 PR-triggered CI runs before the policy changed,
Check formatting was the single most common avoidable failure after the dependency audit.
It is also the cheapest command in the repo.
The full tier — run this when you have changed behaviour
pnpm install --frozen-lockfile
pnpm format:check
pnpm build
pnpm lint
pnpm test
pnpm build must precede pnpm test — turbo's test task declares no dependsOn, so a
cold pnpm test fails to resolve workspace entry points.
The node/platform wire contract — run this if you touched a DTO or packages/contracts
pnpm --filter @ever-works/contracts test
pnpm --filter ever-works-api exec jest --testPathPattern="fleet/__tests__/node-contract"
pnpm --filter @ever-works/contracts build
pnpm --filter ever-works-node exec vitest run src/core/node-contract.conformance.spec.ts src/core/api-base.spec.ts
These are the same four commands promotion-gate.yml runs, and that gate has no override
label — a broken wire contract stops the whole fleet at once.
What your machine cannot tell you
| Not reproducible locally | Why | Where it actually runs |
|---|---|---|
job-runtime-real-infra | needs Redis + Postgres + Temporal auto-setup + an Inngest dev server | push to main only |
The /login redirect regression guard | boots two production next start servers and uses pkill; not available in Git Bash | push to stage / main |
The four job-runtime real-infra specs | they describe.skip silently when EW_TEST_REAL_* is unset, so a green local run has executed the mocks only | push to stage / main |
For the third row you can close most of the gap with
docker compose -f docker-compose.infra.yml up -d and setting EW_TEST_REAL_REDIS_URL /
EW_TEST_REAL_PGBOSS_URL. Temporal and Inngest have no local compose entry.
A note on pnpm audit
CI retries transport errors three times and downgrades ERR_PNPM_AUDIT_BAD_RESPONSE to a
warning, because npm retired the legacy audit endpoints. A bare local
pnpm audit --prod --audit-level=high will hard-fail on that same outage. An
ERR_PNPM_AUDIT_BAD_RESPONSE is an endpoint outage, not a finding.
If you want the full suite on your branch anyway
gh workflow run ci.yml --ref <your-branch>
gh run watch
Where a batch is really verified
develop -> stage is the first rung that runs the suites, and stage -> main re-proves the
exact commit being promoted. Note that the develop -> stage pull request runs only
promotion-gate, which checks the node contract and not format, lint, build or test —
so the first automatic execution of those on your batch is the push to stage, after that
merge has already landed.
Commit Conventions
Both repositories use Conventional Commits.
Nothing enforces this automatically.
.husky/commit-msgis a single commented-out line (disabled on 2026-02-11 inee573da0d), and the repo has never had apre-commitorpre-pushhook. There is no lefthook, simple-git-hooks, lint-staged or.pre-commit-config.yamleither. The convention is honoured by hand.
Use the following commit prefixes:
| Prefix | Usage |
|---|---|
feat: | New features |
fix: | Bug fixes |
docs: | Documentation changes |
refactor: | Code restructuring without behavior change |
test: | Adding or updating tests |
chore: | Maintenance tasks, dependency updates |
style: | Formatting changes (no logic change) |
perf: | Performance improvements |
ci: | CI/CD configuration changes |
Example:
git commit -m "feat: add search filtering by category in work listing"
git commit -m "fix: resolve null pointer in plugin loader when config is missing"
Branch Naming
Use descriptive branch names with a prefix that matches the type of work:
feat/add-category-filter
fix/plugin-loader-null-check
docs/update-contributing-guide
refactor/simplify-auth-middleware
Pull Request Process
- Fork the repository (or create a branch if you have write access).
- Create a feature branch from
develop(Platform) ormain(Template). - Make your changes following the code standards above.
- Run quality checks before pushing (see below).
- Push your branch and open a Pull Request against the base branch.
- Fill out the PR template with a description of your changes, related issues, and testing notes.
- Wait for review. A maintainer will review your PR and may request changes.
- Once approved, a maintainer will merge your PR.
Quality Checks Before Submitting a PR
Run all of the following from the repository root:
# Platform
pnpm lint # ESLint across all workspaces
pnpm type-check # TypeScript compilation check
pnpm format:check # Prettier format verification
pnpm test # All tests (Jest for agent, Vitest for plugins)
# Template
pnpm lint # ESLint
pnpm tsc --noEmit # TypeScript check
pnpm build # Full production build
Testing Requirements
- Platform agent package: Uses Jest (26 test suites, 700+ tests). Run with
cd packages/agent && pnpm test. - Platform plugins: Use Vitest. Run with
cd packages/plugins/<name> && pnpm test. - Platform API: Uses Jest. Run with
cd apps/api && pnpm test. - Template: Uses Playwright for end-to-end tests. Run with
pnpm test:e2e.
If your changes touch existing functionality, ensure all related tests pass. If you add new functionality, include tests for it.
Plugin Contributions
The Platform has an extensible plugin system. Plugins live in packages/plugins/ and are standalone ESM packages. To contribute a new plugin:
- Use an existing plugin as a reference (e.g.,
packages/plugins/openai). - Define metadata in your plugin's
package.jsonunder theeverworks.pluginfield. - Implement the required interfaces from
@ever-works/plugin. - Build with tsup and test with Vitest.
- Document any required environment variables or API keys.
See the Plugin System documentation for architecture details.
Documentation Contributions
Documentation is not a side repository you have to hunt for: it is docs/** in the platform monorepo, and a docs change is an ordinary PR against ever-works.
| Path | What lives there |
|---|---|
docs/ | Every published page, as Markdown. docs/index.md is the site home (routeBasePath: '/'). |
docs/features/ | User-facing feature pages (Agents, Missions, Knowledge Base, …). |
docs/guides/ | Task-oriented how-to guides. |
docs/api/, docs/cli/ | Reference for the REST API and the CLI. |
docs/assets/ | Images and other static files, registered via staticDirectories and served from the site root. |
docs/specs/, docs/internal/ | Internal specs and working docs. Real pages, deliberately left out of the sidebar. |
apps/docs/ | The Docusaurus site itself: docusaurus.config.ts, sidebarsPlatform.ts, theme, translations. |
How to add or edit a page
- Install once from the monorepo root with
pnpm install, then start the docs site withpnpm dev:docs. It serves onhttp://localhost:3000; if the web dashboard already holds that port, runpnpm --filter ever-works-docs dev -- --port 3200instead. Edits hot-reload. - Create or edit the Markdown file under
docs/. Put user-facing pages in the folder that matches their area, internal plans and hand-off notes indocs/internal/, and feature specs indocs/specs/<feature>/. Never leave a working document in the monorepo root. - Give every new page frontmatter that matches its neighbours —
id,title,sidebar_label, and an optionaldescription— then open with an H1 that repeats the title. - Link to other pages with relative Markdown paths (
./missions.md,../features/agents.md). Docusaurus rewrites them to site URLs and reports the ones that no longer resolve. - Add the page's
idtoapps/docs/sidebarsPlatform.ts. The sidebar is hand-maintained: a file that is not listed still builds and is still reachable by URL, but it never appears in the navigation — which is exactly howdocs/specs/anddocs/internal/stay off the nav. - Use standard Markdown tables for reference material, and fenced code blocks tagged
mermaidfor diagrams — Mermaid is enabled through@docusaurus/theme-mermaidandmarkdown.mermaid: true, so no extra setup is needed. Admonitions (:::note,:::caution,:::tip) are available. - Reference images from
docs/assets/. That folder is a Docusaurus static directory, sodocs/assets/overview.pngis served as/overview.png.
Checks to run before opening a docs PR
# From the monorepo root
pnpm format # Prettier — its glob covers **/*.md
pnpm format:check # Exactly what CI runs
pnpm build:docs # turbo run build --filter=ever-works-docs
pnpm --filter ever-works-docs spellcheck # cspell over apps/docs (config: apps/docs/.cspell.json)
Three details are easy to get caught by:
- Prettier owns Markdown. The root
formatscript globs**/*.{ts,tsx,jsx,json,css,md}, and the CI job runspnpm format:check, so an unformatted table or a trailing space fails the build like any other file. - The docs build is not part of
pnpm build. Both the rootbuildscript and the CI build step pass--filter=!./apps/docs, so the site is only compiled when you runpnpm build:docs(orpnpm build:all). Run it yourself before you push. - Broken links warn, they do not fail.
onBrokenLinksandonBrokenMarkdownLinksare both set towarn, so a bad relative link ships silently unless you read the build output.
Translations
The site is internationalized with the Docusaurus i18n plugin. Translations live in apps/docs/i18n/, and DOCS_BUILD_LOCALES decides which locales are actually built — it defaults to en,fr because building every aspirational locale produced thousands of untranslated duplicate pages and exhausted the CI disk. To add strings for a locale, run pnpm --filter ever-works-docs write-translations, translate the generated JSON, then build with DOCS_BUILD_LOCALES=en,fr,<locale> pnpm build:docs.
How a docs change reaches docs.ever.works
Merging to main triggers .github/workflows/k8s-build.yml, which rebuilds the ever-works-docs image from .deploy/docker/docs/Dockerfile and pushes :prod and :sha-<sha> tags to GHCR. The docs-ever-works-prod ArgoCD application tracks the :prod tag by digest and rolls the site out. Nothing has to be deployed by hand.
License
- Ever Works Platform and Work Web Template are licensed under the GNU Affero General Public License v3.0 (AGPL-3.0). This applies to all packages in
apps/andpackages/, including the Plugin SDK (@ever-works/pluginand@ever-works/contracts) and all first-party plugins. - The only exception is the public CLI (
apps/cli), which is released under the MIT license so it can be freely embedded in projects with any license. - By submitting a contribution, you agree that your work will be licensed under the same license as the repository you are contributing to.
Code of Conduct
All contributors are expected to follow the project's Code of Conduct. Be respectful, constructive, and collaborative. Harassment, discrimination, and disruptive behavior will not be tolerated.
Getting Help
If you have questions about contributing:
- Open a GitHub Discussion for general questions
- Join the Discord community for real-time help
- Email ever@ever.co for private inquiries
Related
- Installation — get the monorepo running before you change it
- Development Workflow — the day-to-day commands, debugging, and quality loop
- Monorepo Structure — what lives in which
apps/*andpackages/*workspace - Testing Overview — the per-workspace test runners and how to run them
- Plugin System · Plugin Development Guide
- Support · Changelog