Files
flynn/CONTRIBUTING.md
William Valentin 8a6cd7f559 docs: Add comprehensive documentation for production deployment and contribution
This commit adds 6 new documentation files to fill critical gaps:

- CONTRIBUTING.md: Developer onboarding guide with setup, workflow,
  code style, testing, and adding features

- TROUBLESHOOTING.md: Common issues and solutions for errors,
  model issues, tool issues, channel issues, gateway issues,
  configuration issues, and memory/database issues

- docs/api/PROTOCOL.md: Gateway JSON-RPC protocol documentation
  with connection, authentication, message format, methods,
  events, error codes, and example client implementation

- docs/api/TOOLS.md: Tools API documentation covering tool interface,
  input schema format, result format, tool patterns,
  tool registration, tool policy, execution flow, and
  builtin tools reference

- docs/deployment/PRODUCTION.md: Production deployment guide
  covering Docker deployment, systemd service, security,
  configuration, monitoring, backup & recovery, and
  performance tuning

- docs/performance/TUNING.md: Performance optimization guide
  covering context management, model routing, tool execution,
  memory & embeddings, session management, database
  performance, gateway performance, and resource usage

These files complement the existing excellent documentation
(README.md, AGENTS.md, ARCHITECTURE.md, STRUCTURE.md,
CONVENTIONS.md) to provide complete coverage for users,
developers, and operators.
2026-02-13 16:07:29 -08:00

11 KiB

Contributing to Flynn

Thank you for your interest in contributing to Flynn! This guide will help you get started.

Table of Contents

Quick Start

# Clone the repository
git clone <repo-url>
cd flynn

# Install dependencies
pnpm install

# Build the project
pnpm build

# Run the daemon
pnpm start

Development Setup

Prerequisites

  • Node.js >= 22.0.0
  • pnpm (package manager)
  • Docker (optional, for sandbox features)

Installation

# Install dependencies
pnpm install

# Verify TypeScript compiles
pnpm typecheck

# Run linter
pnpm lint

# Run tests
pnpm test

Development Commands

Command Description
pnpm build Compile TypeScript to dist/
pnpm dev Run daemon with watch mode (tsx watch)
pnpm start Start production build
pnpm tui Minimal TUI (readline)
pnpm tui:fs Fullscreen TUI (React/Ink)
pnpm test Run vitest in watch mode
pnpm test:run Run tests once (CI)
pnpm lint Run ESLint
pnpm typecheck TypeScript check (no emit)

Running a Single Test File

pnpm test:run src/path/to/file.test.ts

Configuration for Development

Create a development config:

cp config/default.yaml ~/.config/flynn/config.yaml
# Edit config with your API keys and settings

Development Workflow

Branching Strategy

  1. Main branch: main - stable production code
  2. Feature branches: feature/description - new features
  3. Bugfix branches: bugfix/description - bug fixes
  4. Refactor branches: refactor/description - code improvements

Feature Development

  1. Create a feature branch from main

    git checkout -b feature/my-new-feature
    
  2. Make your changes

  3. Build and test

    pnpm build
    pnpm test
    pnpm lint
    pnpm typecheck
    
  4. Commit your changes (see Commit Guidelines)

  5. Push and create a pull request

Committing Changes

Before committing, ensure:

  • All tests pass: pnpm test:run
  • Linting passes: pnpm lint
  • Type checking passes: pnpm typecheck
  • Build succeeds: pnpm build
git add .
git commit -m "feat: add my new feature"

Code Style

Flynn follows specific conventions documented in .planning/codebase/CONVENTIONS.md.

Key Guidelines

  • 2-space indentation (no tabs)
  • Single quotes for strings
  • Trailing commas in multiline structures
  • Semicolons always used
  • camelCase for functions/variables
  • PascalCase for classes/interfaces
  • kebab-case for source files (my-feature.ts)
  • PascalCase for React components (MyComponent.tsx)
  • Test files co-located with source: file.test.ts beside file.ts

Import Organization

// 1. Node.js stdlib
import { readFileSync } from 'fs';
import { execFile } from 'child_process';

// 2. Third-party packages
import Anthropic from '@anthropic-ai/sdk';
import { z } from 'zod';

// 3. Local imports (always use .js extension)
import { NativeAgent } from './agent.js';
import type { Config } from '../config/schema.js';

Error Handling

// Pattern 1: Return ToolResult with error (tools)
try {
  const result = await someOperation();
  return { success: true, output: result };
} catch (error) {
  return {
    success: false,
    output: '',
    error: error instanceof Error ? error.message : String(error),
  };
}

// Pattern 2: Throw with descriptive message (config/setup)
if (envValue === undefined) {
  throw new Error(`Environment variable ${envVar} is not set`);
}

Testing

Test Framework

Flynn uses Vitest for testing. Test files are co-located with source files:

src/
├── agent.ts
├── agent.test.ts
├── models/
│   ├── anthropic.ts
│   └── anthropic.test.ts

Writing Tests

import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { MyComponent } from './my-component.js';

describe('MyComponent', () => {
  beforeEach(() => {
    // Setup before each test
  });

  afterEach(() => {
    // Cleanup after each test
  });

  it('should do something correctly', () => {
    const result = MyComponent.doSomething();
    expect(result).toBe('expected-value');
  });

  it('should handle errors gracefully', () => {
    expect(() => MyComponent.doSomethingInvalid()).toThrow();
  });
});

Testing Guidelines

  • Test both success and failure cases
  • Clean up resources (files, directories) in afterEach or it blocks
  • Mock external dependencies (APIs, databases, filesystem)
  • Use describe/it pattern for organization
  • Keep tests focused and independent

Running Tests

# Watch mode (during development)
pnpm test

# Run once (CI/pre-commit)
pnpm test:run

# Run specific test file
pnpm test:run src/models/anthropic.test.ts

# Run tests matching pattern
pnpm test -- --grep "anthropic"

Adding Features

Adding a New Tool

Flynn tools follow three patterns:

Pattern 1: Static Tool (no dependencies)

// src/tools/builtin/my-tool.ts
import type { Tool, ToolResult } from '../types.js';

interface MyToolArgs {
  input: string;
}

export const myTool: Tool = {
  name: 'my.tool',
  description: 'Description of what this tool does',
  inputSchema: {
    type: 'object',
    properties: {
      input: { type: 'string', description: 'Input parameter' },
    },
    required: ['input'],
  },
  execute: async (rawArgs: unknown): Promise<ToolResult> => {
    const args = rawArgs as MyToolArgs;
    // Implementation
    return { success: true, output: 'result' };
  },
};

Pattern 2: Factory Tool (needs dependency injection)

// src/tools/builtin/memory-read.ts
import type { Tool, ToolResult } from '../types.js';
import type { MemoryStore } from '../../memory/store.js';

export function createMemoryReadTool(store: MemoryStore): Tool {
  return {
    name: 'memory.read',
    description: 'Read from memory store',
    inputSchema: {
      type: 'object',
      properties: {
        namespace: { type: 'string', description: 'Memory namespace' },
      },
      required: ['namespace'],
    },
    execute: async (rawArgs: unknown): Promise<ToolResult> => {
      const args = rawArgs as { namespace: string };
      try {
        const content = store.read(args.namespace);
        return { success: true, output: content };
      } catch (error) {
        return {
          success: false,
          output: '',
          error: error instanceof Error ? error.message : String(error),
        };
      }
    },
  };
}
// src/tools/builtin/index.ts
export function createMemoryTools(store: MemoryStore, hybridSearch?: HybridSearch): Tool[] {
  return [
    createMemoryReadTool(store),
    createMemoryWriteTool(store),
    createMemorySearchTool(store, hybridSearch),
  ];
}

Registration Steps:

  1. Add export to src/tools/builtin/index.ts
  2. Add export to src/tools/index.ts
  3. Register in src/daemon/index.ts (call factory + register)
  4. Add to tool profiles in src/tools/policy.ts if needed
  5. Write tests in src/tools/builtin/my-tool.test.ts

Adding a New Channel Adapter

  1. Create directory: src/channels/<platform>/
  2. Create adapter.ts implementing ChannelAdapter interface
  3. Create index.ts re-exporting the adapter
  4. Add test: adapter.test.ts
  5. Register in src/channels/index.ts
  6. Register in src/daemon/index.ts
  7. Add config schema in src/config/schema.ts

Adding a New Model Provider

  1. Create src/models/<provider>.ts implementing ModelClient interface
  2. Add export to src/models/index.ts
  3. Add case in src/daemon/index.tscreateClientFromConfig()
  4. Add to src/config/schema.tsmodelConfigBaseSchema.provider enum
  5. Write tests in src/models/<provider>.test.ts

Adding a New CLI Command

// src/cli/my-cmd.ts
import { Command } from 'commander';
import { loadConfigSafe } from './shared.js';

export function registerMyCommand(program: Command) {
  program
    .command('my-cmd')
    .description('Description of my command')
    .option('-c, --config <path>', 'Config file path')
    .action(async (options) => {
      const configResult = await loadConfigSafe(options.config);
      if (configResult.error) {
        console.error(configResult.error);
        process.exit(1);
      }
      // Implementation
    });
}

Register in src/cli/index.ts:

import { registerMyCommand } from './my-cmd.js';

// In registerCommands()
registerMyCommand(program);

Commit Guidelines

Commit Message Format

Follow conventional commits:

<type>(<scope>): <description>

[optional body]

[optional footer]

Types

  • feat: New feature
  • fix: Bug fix
  • refactor: Code refactoring (no functional change)
  • docs: Documentation changes
  • test: Test additions/modifications
  • chore: Build process, dependencies, tooling
  • style: Code style changes (formatting, semicolons, etc.)

Examples

feat(tools): add image analysis tool

Implements image analysis using OpenAI Vision API.

Closes #123

fix(gateway): handle WebSocket disconnection gracefully

Prevents infinite loop when connection drops during event emission.

docs(readme): update quick start instructions

Clarify configuration steps for new users.

test(models): add Anthropic client retry tests

Verify exponential backoff and fallback behavior.

Submitting Changes

Pull Request Process

  1. Ensure your branch is up to date with main

    git fetch origin
    git rebase origin/main
    
  2. Push your branch

    git push -u origin feature/my-new-feature
    
  3. Create a pull request with:

    • Clear description of changes
    • Reference related issues
    • Screenshots if UI changes
    • Test results
    • Breaking changes noted

Code Review Checklist

Before submitting, verify:

  • All tests pass (pnpm test:run)
  • Linting passes (pnpm lint)
  • Type checking passes (pnpm typecheck)
  • Build succeeds (pnpm build)
  • New features have tests
  • Documentation updated (README, AGENTS.md, code comments)
  • No console.log or debugger statements
  • Sensitive data not committed (API keys, tokens)
  • Commit messages follow format

Getting Help

Documentation

Troubleshooting

See TROUBLESHOOTING.md for common issues and solutions.

Questions?

  • Open an issue for bugs or feature requests
  • Start a discussion for questions
  • Check existing issues and discussions first

Thank you for contributing to Flynn!