Skip to main content
Contributions are welcome! The plugin system makes it straightforward to add support for new agents, runtimes, trackers, and notification channels.

Getting Started

Prerequisites

  • Node.js 20+
  • pnpm 9.15+
  • Git 2.30+

First-Time Setup

1

Clone the repository

git clone https://github.com/ComposioHQ/agent-orchestrator.git
cd agent-orchestrator
2

Install dependencies

pnpm install
3

Build all packages

pnpm build
Building is required before running the dev server.
4

Copy example config

cp agent-orchestrator.yaml.example agent-orchestrator.yaml
5

Configure your settings

$EDITOR agent-orchestrator.yaml

Running the Dev Server

The web dashboard depends on built packages. Always build before running the dev server.
# Build all packages
pnpm build

# Start dev server
cd packages/web
pnpm dev

# Open http://localhost:3000 (or your configured port)

Project Structure

agent-orchestrator/
├── packages/
│   ├── core/              # Core types, services, config
│   ├── cli/               # CLI tool (ao command)
│   ├── web/               # Next.js dashboard
│   ├── plugins/           # All plugins
│   │   ├── runtime-*/     # Runtime plugins (tmux, docker, k8s)
│   │   ├── agent-*/       # Agent adapters (claude-code, codex, aider)
│   │   ├── workspace-*/   # Workspace providers (worktree, clone)
│   │   ├── tracker-*/     # Issue trackers (github, linear)
│   │   ├── scm-github/    # SCM adapter
│   │   ├── notifier-*/    # Notification channels
│   │   └── terminal-*/    # Terminal UIs
│   └── integration-tests/ # Integration tests
├── agent-orchestrator.yaml.example
├── .gitleaks.toml         # Secret scanning config
├── .husky/                # Git hooks
└── docs/                  # Documentation

Development Workflow

Making Changes

1

Create a feature branch

git checkout -b feat/your-feature
2

Make your changes

  • Follow CLAUDE.md conventions
  • Add tests for new features
  • Update documentation
3

Build and test

pnpm build
pnpm test
pnpm lint
pnpm typecheck
4

Commit

git add .
git commit -m "feat: add your feature"
5

Push and open PR

git push origin feat/your-feature

Code Conventions

TypeScript

  • ESM modules - .js extensions in imports
  • node: prefix for builtins (node:fs, node:path)
  • Strict mode enabled
  • type imports for type-only imports
  • No any - use unknown + type guards
  • Semicolons, double quotes, 2-space indent - enforced by Prettier
// Good
import type { Runtime } from "@composio/ao-core";
import { readFileSync } from "node:fs";
import { foo } from "./bar.js";

// Bad
import { Runtime } from "@composio/ao-core"; // Should be type import
import { readFileSync } from "fs"; // Missing node: prefix
import { foo } from "./bar"; // Missing .js extension

Shell Commands

Security critical: Always follow these rules to prevent shell injection vulnerabilities.
  • Always use execFile (or spawn) - NEVER exec
  • Always add timeouts - { timeout: 30_000 }
  • Never interpolate user input - pass as array args
  • Do NOT use JSON.stringify for shell escaping
// GOOD
import { execFile } from "node:child_process";
import { promisify } from "node:util";
const execFileAsync = promisify(execFile);
const { stdout } = await execFileAsync("git", ["branch", "--show-current"], { 
  timeout: 30_000 
});

// BAD - shell injection risk
exec(`git checkout ${branchName}`); // branchName could contain ; rm -rf /

Plugin Development

Every plugin implements one of the core interfaces defined in packages/core/src/types.ts.

Plugin Pattern

import type { PluginModule, Runtime } from "@composio/ao-core";

export const manifest = {
  name: "my-plugin",
  slot: "runtime" as const,
  description: "My plugin",
  version: "0.1.0",
};

export function create(): Runtime {
  return {
    name: "my-plugin",
    async create(config) {
      /* ... */
    },
    // ... implement interface
  };
}

export default { manifest, create } satisfies PluginModule<Runtime>;

Adding a New Plugin

1

Create plugin package

mkdir -p packages/plugins/runtime-myplugin
cd packages/plugins/runtime-myplugin
2

Set up package.json

{
  "name": "@composio/ao-runtime-myplugin",
  "version": "0.1.0",
  "type": "module",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "scripts": {
    "build": "tsc",
    "typecheck": "tsc --noEmit",
    "test": "vitest"
  },
  "dependencies": {
    "@composio/ao-core": "workspace:*"
  }
}
3

Create src/index.ts

Implement the appropriate interface (Runtime, Agent, Workspace, Tracker, etc.).
4

Register in core

Add to packages/core/src/services/plugin-registry.ts.
5

Add tests

Create src/index.test.ts with test cases.
6

Build and test

pnpm --filter @composio/ao-runtime-myplugin build
pnpm --filter @composio/ao-runtime-myplugin test

Plugin Slots

Eight slots available for plugins:
SlotInterfaceDefaultExamples
RuntimeRuntimetmuxdocker, k8s, process
AgentAgentclaude-codecodex, aider, opencode
WorkspaceWorkspaceworktreeclone
TrackerTrackergithublinear
SCMSCMgithub
NotifierNotifierdesktopslack, composio, webhook
TerminalTerminaliterm2web
Lifecycle(core)core
All interfaces are defined in packages/core/src/types.ts - read this file first!

Testing

# Run all tests
pnpm test

# Run tests for specific package
pnpm --filter @composio/ao-core test

# Run tests in watch mode
pnpm --filter @composio/ao-core test -- --watch

# Run integration tests
pnpm test:integration

Test Coverage

The project has 3,288 test cases ensuring reliability and correctness.

Security

Secret Scanning

A pre-commit hook automatically scans for secrets:
🔒 Scanning staged files for secrets...
 No secrets detected
If secrets are detected:
  1. Remove the secret from the file
  2. Use environment variables: ${SECRET_NAME}
  3. Add to .env.local (in .gitignore)
  4. Update example configs with placeholders

What Triggers the Scanner

  • API keys: lin_api_*, ghp_*, gho_*, sk-*, AKIA*
  • Tokens: xoxb-*, xoxa-*, etc.
  • Webhooks: https://hooks.slack.com/*, https://discord.com/api/webhooks/*
  • Private keys: -----BEGIN PRIVATE KEY-----
  • Database URLs: postgres://user:pass@host
  • Generic patterns: api_key=..., token=..., password=...

False Positives

If you get a false positive:
1

Verify it's not a real secret

Double-check the detected pattern is actually safe.
2

Update allowlist

Edit .gitleaks.toml:
[allowlist]
regexes = [
  '''your-pattern-here''',
]
3

Commit the change

git add .gitleaks.toml
git commit -m "chore: update gitleaks allowlist"

Working with Worktrees

If using git worktrees for parallel development:
# Create worktree
git worktree add ../ao-feature-x feat/feature-x
cd ../ao-feature-x

# Install and build
pnpm install
pnpm build

# Copy config
cp ../agent-orchestrator/agent-orchestrator.yaml .

# Start dev server
cd packages/web
pnpm dev

Debugging

Enable Verbose Logging

DEBUG=* pnpm dev

Attach to tmux Session

tmux attach -t session-name
# Detach: Ctrl-b d

Inspect Session Metadata

cat ~/.agent-orchestrator/my-app-3

Check Session Status

curl http://localhost:3000/api/sessions/my-app-3

Submission Guidelines

Pull Request Process

1

Ensure all tests pass

pnpm build && pnpm test && pnpm lint && pnpm typecheck
2

Write a clear description

  • What does this PR do?
  • Why is it needed?
  • How does it work?
  • Any breaking changes?
3

Link related issues

Use Fixes #123 or Closes #123 in the PR description.
4

Request review

Tag relevant maintainers or wait for automatic review assignment.

Commit Message Format

Use Conventional Commits:
feat: add support for Docker runtime
fix: resolve memory leak in session cleanup
chore: update dependencies
docs: improve plugin development guide
test: add integration tests for Linear tracker

Code Review

Expect feedback on:
  • Code quality and style
  • Test coverage
  • Documentation updates
  • Security considerations
  • Performance implications

Resources

Getting Help

Need help contributing?
  1. Check existing issues and discussions
  2. Review the troubleshooting guide
  3. Ask questions in GitHub Discussions
  4. Join the community channels
Thank you for contributing to Agent Orchestrator!

Build docs developers (and LLMs) love