Skip to main content

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:

RepositoryDescription
ever-worksPlatform monorepo (API, Web Dashboard, CLI, AI agents, plugins)
directory-web-templateStandalone Next.js work website template
ever-works/apps/docsDocumentation 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.

The documentation is not a separate repository

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-alpine Docker base image and the root package.json engines 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 strict mode settings in tsconfig.json
  • Prefer explicit return types on exported functions
  • Use unknown over any where 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​

ElementConventionExample
Fileskebab-caseauth.service.ts, user-profile.tsx
Classes, Interfaces, TypesPascalCaseWorkService, UserProfile
Functions, VariablescamelCasegetWorkById, itemCount
ConstantsUPPER_SNAKE_CASEMAX_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 locallyWhyWhere it actually runs
job-runtime-real-infraneeds Redis + Postgres + Temporal auto-setup + an Inngest dev serverpush to main only
The /login redirect regression guardboots two production next start servers and uses pkill; not available in Git Bashpush to stage / main
The four job-runtime real-infra specsthey describe.skip silently when EW_TEST_REAL_* is unset, so a green local run has executed the mocks onlypush 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-msg is a single commented-out line (disabled on 2026-02-11 in ee573da0d), and the repo has never had a pre-commit or pre-push hook. There is no lefthook, simple-git-hooks, lint-staged or .pre-commit-config.yaml either. The convention is honoured by hand.

Use the following commit prefixes:

PrefixUsage
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​

  1. Fork the repository (or create a branch if you have write access).
  2. Create a feature branch from develop (Platform) or main (Template).
  3. Make your changes following the code standards above.
  4. Run quality checks before pushing (see below).
  5. Push your branch and open a Pull Request against the base branch.
  6. Fill out the PR template with a description of your changes, related issues, and testing notes.
  7. Wait for review. A maintainer will review your PR and may request changes.
  8. 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:

  1. Use an existing plugin as a reference (e.g., packages/plugins/openai).
  2. Define metadata in your plugin's package.json under the everworks.plugin field.
  3. Implement the required interfaces from @ever-works/plugin.
  4. Build with tsup and test with Vitest.
  5. 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.

PathWhat 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​

  1. Install once from the monorepo root with pnpm install, then start the docs site with pnpm dev:docs. It serves on http://localhost:3000; if the web dashboard already holds that port, run pnpm --filter ever-works-docs dev -- --port 3200 instead. Edits hot-reload.
  2. 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 in docs/internal/, and feature specs in docs/specs/<feature>/. Never leave a working document in the monorepo root.
  3. Give every new page frontmatter that matches its neighbours — id, title, sidebar_label, and an optional description — then open with an H1 that repeats the title.
  4. 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.
  5. Add the page's id to apps/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 how docs/specs/ and docs/internal/ stay off the nav.
  6. Use standard Markdown tables for reference material, and fenced code blocks tagged mermaid for diagrams — Mermaid is enabled through @docusaurus/theme-mermaid and markdown.mermaid: true, so no extra setup is needed. Admonitions (:::note, :::caution, :::tip) are available.
  7. Reference images from docs/assets/. That folder is a Docusaurus static directory, so docs/assets/overview.png is 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 format script globs **/*.{ts,tsx,jsx,json,css,md}, and the CI job runs pnpm 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 root build script and the CI build step pass --filter=!./apps/docs, so the site is only compiled when you run pnpm build:docs (or pnpm build:all). Run it yourself before you push.
  • Broken links warn, they do not fail. onBrokenLinks and onBrokenMarkdownLinks are both set to warn, 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/ and packages/, including the Plugin SDK (@ever-works/plugin and @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: