Thank you for your interest in contributing to AgentForge! This document provides guidelines and information for contributors.
- Development Setup
- Code Style
- Testing Guidelines
- Pull Request Process
- Project Structure
- Adding Features
- Node.js >= 18.0.0
- pnpm (recommended) or npm
- Git
- Clone the repository:
git clone https://github.com/wkyleg/agentforge.git
cd agentforge- Install dependencies:
pnpm install- Build the project:
pnpm build- Run tests to verify setup:
pnpm test- Try the CLI:
pnpm sim:toy| Script | Description |
|---|---|
pnpm build |
Compile TypeScript to JavaScript |
pnpm build:watch |
Watch mode compilation |
pnpm dev |
Run CLI in development mode with watch |
pnpm dev:cli |
Run CLI directly without watch |
pnpm test |
Run all tests |
pnpm test:watch |
Run tests in watch mode |
pnpm test:coverage |
Run tests with coverage report |
pnpm test:ui |
Open Vitest UI |
pnpm lint |
Check code with Biome |
pnpm lint:fix |
Auto-fix linting issues |
pnpm format |
Format code with Biome |
pnpm typecheck |
Type-check without building |
pnpm clean |
Remove build artifacts |
pnpm sim:toy |
Run toy simulation |
pnpm sim:doctor |
Run environment check |
We use Biome for linting and formatting, not ESLint/Prettier.
- Single quotes for strings
- Semicolons at end of statements
- ES5 trailing commas in arrays and objects
- Imports organized and sorted automatically
- No unused variables (errors, not warnings)
- Template literals preferred over string concatenation
# Check for issues
pnpm lint
# Auto-fix issues
pnpm lint:fix
# Format code
pnpm format- Strict mode is enabled - no implicit any, null checks required
- Exact optional properties - undefined must be explicit
- No unchecked indexed access - array access may return undefined
- Prefer
unknownoverany - Use explicit return types for public functions
- Export types using
export typewhen possible
For simulation-kernel, replay, gossip, query, and LLM-policy work, follow this sequence:
- Add or update tests first (
test/unitandtest/integration). - Run the smallest relevant test subset.
- Implement code until tests pass.
- Run full validation:
pnpm typecheckpnpm testpnpm lint
This keeps deterministic and replay guarantees from regressing.
test/
├── unit/ # Unit tests for individual modules
│ ├── rng.test.ts
│ ├── scheduler.test.ts
│ ├── agent.test.ts
│ ├── engine.test.ts
│ └── ...
├── integration/ # Integration tests for CLI and full runs
│ ├── cli-doctor.test.ts
│ ├── cli-run-toy.test.ts
│ └── ...
└── mocks/ # Shared mock utilities
├── mockPack.ts
├── mockAgent.ts
├── mockLogger.ts
├── mockRpc.ts
└── index.ts
Use Vitest for all tests:
import { describe, expect, it, beforeEach, afterEach } from 'vitest';
describe('MyModule', () => {
beforeEach(() => {
// Setup
});
afterEach(() => {
// Cleanup
});
it('should do something', () => {
expect(result).toBe(expected);
});
it('should handle edge case', async () => {
await expect(asyncFn()).resolves.toBe(expected);
});
});import {
createMockPack,
createMockLogger,
MockAgent,
} from '../mocks/index.js';
const mockPack = createMockPack({
initialMetrics: { value: 100 },
});
const logger = createMockLogger();- Lines: 80%
- Functions: 80%
- Branches: 70%
- Statements: 80%
Run coverage report:
pnpm test:coverageBy default, LLM integration tests use mocks. To run the live OpenAI test explicitly:
RUN_OPENAI_INTEGRATION_TEST=1 OPENAI_API_KEY=... pnpm test -- test/integration/llm-openai.optional.test.tsThis test is intentionally skipped unless both env variables are set.
-
Create a branch from
main:git checkout -b feature/my-feature
-
Make your changes with clear, atomic commits
-
Ensure all checks pass:
pnpm lint pnpm typecheck pnpm test pnpm build -
Add/update tests for your changes
-
Update documentation if needed
Use clear, descriptive commit messages:
type(scope): short description
Longer description if needed.
- Bullet points for multiple changes
- Reference issues: #123
Types:
feat: New featurefix: Bug fixdocs: Documentation onlystyle: Code style changes (formatting, etc.)refactor: Code change that neither fixes a bug nor adds a featuretest: Adding or updating testschore: Build process or auxiliary tool changes
Include:
- Summary of changes
- Motivation for the change
- Testing done
- Screenshots if applicable
- Breaking changes if any
- Create PR against
main - CI checks must pass
- At least one approval required
- Squash and merge preferred
agentforge/
├── src/
│ ├── index.ts # Main library exports
│ ├── cli/ # CLI implementation
│ │ ├── index.ts # Entry point
│ │ ├── commands/ # Command implementations
│ │ └── ui/ # Output formatting
│ ├── core/ # Core library
│ │ ├── engine.ts # SimulationEngine
│ │ ├── agent.ts # BaseAgent
│ │ ├── types.ts # Type definitions
│ │ ├── rng.ts # Deterministic RNG
│ │ ├── scheduler.ts # Agent scheduling
│ │ ├── metrics.ts # Metrics collection
│ │ ├── artifacts.ts # Output writing
│ │ ├── logging.ts # Pino logging
│ │ ├── scenario.ts # Scenario loading
│ │ ├── schemas.ts # Zod validation
│ │ ├── errors.ts # Error types
│ │ └── preconditions.ts # Action preconditions
│ ├── adapters/ # External integrations
│ │ ├── anvil.ts # Anvil control
│ │ ├── foundry.ts # Foundry detection
│ │ └── viem.ts # Viem helpers
│ └── toy/ # Built-in toy simulation
│ ├── toyPack.ts
│ ├── toyAgents.ts
│ ├── toyScenario.ts
│ └── index.ts
├── test/
├── examples/
├── package.json
├── tsconfig.json
├── biome.json
└── vitest.config.ts
- Create command file in
src/cli/commands/:
import { Command } from 'commander';
export const myCommand = new Command('mycommand')
.description('Description')
.option('-f, --flag', 'Flag description')
.action(async (options) => {
// Implementation
});- Register in
src/cli/index.ts:
import { myCommand } from './commands/mycommand.js';
program.addCommand(myCommand);- Add tests in
test/integration/cli-mycommand.test.ts
- Create module in
src/core/ - Export from
src/index.ts - Add unit tests in
test/unit/ - Update documentation
- Modify
src/core/agent.ts - Update tests in
test/unit/agent.test.ts - Add examples in
examples/ - Update README agent authoring guide
- Open an issue for bugs or feature requests
- Start a discussion for questions or ideas
- Contact the maintainers
Thank you for contributing!