Skip to main content

Code of Conduct

This project follows the Contributor Covenant Code of Conduct. By participating, you are expected to uphold this code.

Getting Started

  1. Fork the repository on GitHub
  2. Clone your fork:
    git clone https://github.com/<your-username>/manifest.git
    cd manifest
    npm install
    
  3. Create a branch for your change:
    git checkout -b feature/my-new-feature
    
  4. Make your changes (see Development Setup)
  5. Write or update tests (see Testing Guide)
  6. Add a changeset (see Changesets below)
  7. Open a pull request against main

Development Workflow

1. Branch Naming

Use descriptive branch names:
  • feature/add-token-usage-export — New feature
  • fix/dashboard-crash-on-empty-data — Bug fix
  • refactor/simplify-routing-logic — Refactoring
  • docs/improve-setup-guide — Documentation
  • chore/update-dependencies — Maintenance

2. Make Changes

Follow the project structure and conventions:
  • Backend: NestJS services, controllers, entities, guards
  • Frontend: SolidJS components, pages, services
  • Plugin: TypeScript modules with zero runtime dependencies
See the Architecture Guide for details.

3. Write Tests

Every new source file or modified function must have corresponding tests. Codecov will fail the PR if changed lines are not covered.
  • Backend unit tests: *.spec.ts next to source files
  • Backend E2E tests: packages/backend/test/*.e2e-spec.ts
  • Frontend tests: *.test.tsx using Vitest
  • Plugin tests: *.spec.ts using Jest
Run tests locally:
npm test --workspace=packages/backend          # Backend unit tests
npm run test:e2e --workspace=packages/backend  # Backend E2E tests
npm test --workspace=packages/frontend         # Frontend tests
npm test --workspace=packages/openclaw-plugin  # Plugin tests

4. Add a Changeset

Every PR needs a changeset. The CI changeset-check job will fail without one.
Run the changeset CLI:
npx changeset
Follow the prompts:
  1. Select packages: Choose the packages your change affects
  2. Select bump type: patch (bug fix), minor (new feature), or major (breaking change)
  3. Write summary: Describe the change in 1-2 sentences
This creates a file in .changeset/commit it with your code.

Which Packages Need Changesets?

Packagenpm nameNeeds changeset?
packages/openclaw-pluginmanifestYes (published to npm)
packages/backendNo (private, but requires manifest changeset)
packages/frontendNo (private, but requires manifest changeset)
Backend or frontend changes always need a manifest changeset. These packages compile into the manifest plugin, so any change to packages/backend/ or packages/frontend/ must include a changeset bumping manifest. CI enforces this.

Empty Changesets

If your change doesn’t affect any publishable package (e.g., CI config, docs, tooling):
npx changeset add --empty

5. Verify the Build

Before opening a PR, ensure the production build works:
npm run build

Code Standards

TypeScript

  • Strict mode: Enabled across all packages
  • Type safety: Avoid any — use proper types or unknown
  • Null safety: Use optional chaining (?.) and nullish coalescing (??)

Formatting

  • Prettier: Automatic formatting via npm run format
  • ESLint: Linting via npm run lint
Pre-commit hooks run automatically via Husky + lint-staged.

Naming Conventions

  • Files: kebab-case.ts (e.g., user-cache.interceptor.ts)
  • Classes: PascalCase (e.g., UserCacheInterceptor)
  • Functions: camelCase (e.g., getUserSession)
  • Constants: UPPER_SNAKE_CASE (e.g., API_KEY_PREFIX)
  • Interfaces: PascalCase (no I prefix — e.g., CreateAgentDto, not ICreateAgentDto)

Commit Messages

Write clear, concise commit messages:
  • Use present tense (“Add feature” not “Added feature”)
  • Explain why the change was made, not just what changed
  • Reference issues when applicable (e.g., “Fix dashboard crash (#123)”)
Good examples:
  • Add token cost breakdown to overview page
  • Fix race condition in OTLP ingestion pipeline
  • Refactor routing logic to use QueryBuilder API
Bad examples:
  • Update files
  • Fix bug
  • WIP

Backend Standards

Services

  • Use dependency injection via constructor
  • Inject repositories using @InjectRepository(Entity)
  • Use TypeORM QueryBuilder instead of raw SQL
  • Apply multi-tenancy via addTenantFilter(qb, userId)
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { AgentMessage } from '../entities/agent-message.entity';
import { addTenantFilter } from './query-helpers';

@Injectable()
export class OverviewService {
  constructor(
    @InjectRepository(AgentMessage)
    private readonly messageRepo: Repository<AgentMessage>,
  ) {}

  async getOverview(userId: string, period: string) {
    const qb = this.messageRepo.createQueryBuilder('m');
    addTenantFilter(qb, userId);
    // ...
  }
}

Controllers

  • Use DTOs for request validation
  • Use @CurrentUser() decorator for user context
  • Mark public routes with @Public()
  • Return appropriate HTTP status codes
import { Controller, Get, Query, UseGuards } from '@nestjs/common';
import { CurrentUser } from '../auth/current-user.decorator';
import { AuthUser } from '../auth/auth.instance';
import { RangeQueryDto } from '../common/dto/range-query.dto';
import { OverviewService } from './overview.service';

@Controller('api/v1/overview')
export class OverviewController {
  constructor(private readonly overviewService: OverviewService) {}

  @Get()
  async getOverview(
    @CurrentUser() user: AuthUser,
    @Query() query: RangeQueryDto,
  ) {
    return this.overviewService.getOverview(user.id, query.period);
  }
}

DTOs

  • Use class-validator decorators
  • Use class-transformer for type coercion
  • Validate all inputs
import { IsString, IsOptional, IsIn } from 'class-validator';

export class RangeQueryDto {
  @IsString()
  @IsOptional()
  @IsIn(['1h', '24h', '7d', '30d'])
  period?: string = '24h';
}

Frontend Standards

Components

  • Use SolidJS functional components
  • Use signals for reactive state
  • Use createResource for async data fetching
  • Keep components small and focused
import { Component, createSignal, createResource } from 'solid-js';
import { fetchOverview } from '../services/api';

export const Overview: Component = () => {
  const [period, setPeriod] = createSignal('24h');
  const [data] = createResource(period, fetchOverview);

  return (
    <div>
      <h1>Overview</h1>
      {data.loading && <p>Loading...</p>}
      {data() && <div>{data().totalMessages} messages</div>}
    </div>
  );
};

Styling

  • Use custom CSS (no CSS-in-JS)
  • Follow existing naming conventions (BEM-like)
  • Avoid inline styles

Pull Request Process

Before Opening a PR

  1. Run tests locally:
    npm test --workspace=packages/backend
    npm run test:e2e --workspace=packages/backend
    npm test --workspace=packages/frontend
    
  2. Run linter:
    npm run lint
    
  3. Verify build:
    npm run build
    
  4. Ensure changeset is added:
    npx changeset status
    

PR Template

Include the following in your PR description:
  • Summary: What does this PR do?
  • Motivation: Why is this change needed?
  • Changes: List key changes (bullet points)
  • Testing: How did you test this?
  • Breaking changes: Any breaking changes? (if yes, explain migration path)
  • Related issues: Link to related issues (e.g., “Fixes #123”)

PR Guidelines

  • Keep PRs focused: One concern per PR
  • Small PRs: Easier to review and merge
  • Clear title: Summarize the change in the title
  • Screenshots: Include screenshots for UI changes
  • Documentation: Update docs if needed

CI Checks

Your PR must pass these CI checks:
  • Lint: ESLint across all packages
  • Backend (PostgreSQL): Unit + E2E tests with coverage
  • Backend (sql.js): Unit + E2E tests (Linux + macOS)
  • Frontend: Unit tests + Vite build with coverage
  • Plugin: Unit tests with coverage
  • Changeset check: Validates changeset presence
  • Codecov: Patch coverage must be >90%

Review Process

  1. A maintainer will review your PR
  2. Address feedback by pushing new commits
  3. Once approved, a maintainer will merge your PR

Release Process

Releases are automated via Changesets.

Workflow

  1. PR merged with changeset → Release workflow detects changesets
  2. “Version Packages” PR created → Bumps versions in package.json, updates CHANGELOG.md
  3. Version PR merged → Workflow publishes to npm automatically

Commands

npx changeset              # Add a changeset (interactive)
npx changeset status       # Check pending changesets
npm run version-packages   # Apply changesets (bump versions + changelogs)
npm run release            # Publish to npm (used by CI)
Only maintainers can publish releases. Contributors only need to add changesets to their PRs.

Reporting Issues

Found a bug or have a feature request?
  1. Check existing issues to avoid duplicates
  2. Open a new issue on GitHub
  3. Provide details:
    • Steps to reproduce (for bugs)
    • Expected behavior vs actual behavior
    • Environment (OS, Node version, etc.)
    • Screenshots or logs (if applicable)

Getting Help

License

By contributing, you agree that your contributions will be licensed under the MIT License.

Recognition

All contributors are recognized in:
  • GitHub contributors list
  • Release notes (when your PR is included in a release)
  • Community shoutouts
Thank you for contributing to Manifest!

Build docs developers (and LLMs) love