-
-
Notifications
You must be signed in to change notification settings - Fork 6
Contributing
Thank you for your interest in contributing to TITAN! This guide covers everything you need to submit high-quality pull requests.
# Fork the repo on GitHub, then:
git clone https://github.com/YOUR_USERNAME/TITAN.git
cd TITAN
npm install
git checkout -b feat/my-feature # or fix/, docs/, refactor/Make your changes, then run the full validation suite:
npm run typecheck # TypeScript strict — must pass with 0 errors
npm run lint # ESLint — fix any warnings
npm run test # All 4,655+ tests must pass
npm run build # Backend build (tsup, ESM)
npm run build:ui # React SPA build (Vite)Or use the combined CI command:
npm run ci # Typecheck + full test suitegit commit -m "feat: add my feature" # Use conventional commit style
git push origin feat/my-featureOpen a PR against the main branch. Fill out the PR template completely.
Every PR should include:
| Section | What to write |
|---|---|
| What | Brief description of the change |
| Why | What problem this solves or feature it adds |
| How | Key implementation details, non-obvious decisions |
| Testing | Checklist: typecheck, build, tests, manual testing |
| Checklist | No TS errors, no ESLint warnings, version bumped if release, README updated if user-facing, no secrets in diff |
TITAN uses TypeScript strict mode with these compiler options:
- Target: ES2022
- Module: ESNext (pure ESM — no CommonJS)
-
Strict:
true(all strict checks enabled) -
noImplicitReturns:
true -
noFallthroughCasesInSwitch:
true -
isolatedModules:
true
TITAN is a pure ESM package. Never use require().
// ✅ Correct
import { something } from './module.js';
import type { MyType } from './types.js';
// ❌ Wrong
const something = require('./module');- Use
import.meta.urlinstead of__dirname - All import paths must include the
.jsextension -
"type": "module"is set inpackage.json
All configuration and external input uses Zod schemas for runtime validation. Config defaults are defined in src/config/schema.ts.
Key lint rules enforced:
| Rule | Setting |
|---|---|
@typescript-eslint/no-explicit-any |
warn |
@typescript-eslint/no-unused-vars |
warn (prefix unused args with _) |
@typescript-eslint/consistent-type-imports |
warn |
prefer-const |
error |
no-var |
error |
- Prefer named exports
- Use the TITAN logger for all output — no bare
console.login library code - All async functions should handle errors
- Tool names must be
snake_case - Tool descriptions should be clear and concise (the LLM reads these to decide when to use tools)
- Keep tools focused — one action per tool
TITAN uses Vitest with the following configuration:
// vitest.config.ts
{
globals: true,
environment: 'node',
include: ['tests/**/*.test.ts'],
testTimeout: 30000,
hookTimeout: 25000,
}| Metric | Value |
|---|---|
| Total tests | 4,655+ |
| Test files | 154 |
| E2E tests | 135+ (Playwright, 7 spec files) |
| Line coverage | ~82% |
CI enforces these minimums:
| Metric | Threshold |
|---|---|
| Branches | 75% |
| Functions | 60% |
| Lines | 60% |
| Statements | 60% |
// tests/skills/my_skill.test.ts
import { describe, it, expect, beforeAll } from 'vitest';
describe('my_skill', () => {
it('should do the thing', async () => {
const result = await myFunction();
expect(result).toBe('expected');
});
});Rules:
- All new features must have tests
- All new skills must have a corresponding test file in
tests/ - Tests live in the
tests/directory (not alongside source) - Use
describe/it/expectfrom Vitest - Async tests use
async/await, not callbacks - Mock external API calls — tests should be fast
- Use heavy
vi.mock()patterns where needed (seetests/gateway-extended.test.tsfor examples) - Don't let coverage drop below thresholds
TITAN uses semantic versioning (e.g., 1.1.1). When bumping the version, all 5 files must be updated together:
| # | File | What to change |
|---|---|---|
| 1 | package.json |
"version" field |
| 2 | src/utils/constants.ts |
TITAN_VERSION constant |
| 3 | tests/core.test.ts |
Version assertion string |
| 4 | tests/mission-control.test.ts |
Version references (4 occurrences) |
| 5 | CHANGELOG.md |
Add new entry at top |
CI runs a version consistency check that verifies all 5 locations match. PRs will fail if they're out of sync.
Note: Only the project maintainer (Tony) approves and publishes version bumps. Do not bump the version in feature PRs unless instructed.
| Command | Description |
|---|---|
npm install |
Install dependencies |
npm run build |
Production backend build (tsup → dist/) |
npm run build:ui |
React SPA build (Vite → ui/dist/) |
npm run typecheck |
TypeScript type checking (tsc --noEmit) |
npm run lint |
Run ESLint |
npm run lint:fix |
Auto-fix linting issues |
npm run test |
Run all tests (Vitest) |
npm run test:watch |
Run tests in watch mode |
npm run test:coverage |
Generate coverage report |
npm run ci |
Typecheck + full test suite |
npm run dev |
Start CLI in dev mode (tsx) |
npm run dev:gateway |
Start gateway in dev mode |
npm start |
Run production build |
TITAN uses GitHub Actions (.github/workflows/ci.yml). CI runs on every push to main and every pull request.
-
npm ci— Clean install -
npm run lint— ESLint check -
npm run typecheck— TypeScript strict mode check -
npm run build— Backend build -
npm run build:ui— UI build -
npx vitest run— Full test suite - Coverage report (Node 22 only)
- Version consistency check — Verifies all 5 version locations match
- Doc accuracy check — Validates CLAUDE.md and README.md contain current version
- npm publish dry-run — Ensures package is publishable
- Docker build smoke test — Verifies Dockerfile builds correctly
- Downloads coverage artifact from test job
- Fails if line coverage drops below 60%
-
Minimum: Node.js 20 (
"engines": { "node": ">=20.0.0" }) - CI tested: Node.js 22 and 24
- Node 18 is not supported
-
Create the skill file at
src/skills/builtin/my_skill.ts:
import { registerSkill } from '../registry.js';
export function registerMySkill(): void {
registerSkill({
name: 'my_tool', // snake_case required
description: 'Does something useful',
parameters: {
type: 'object',
properties: {
input: { type: 'string', description: 'The input' }
},
required: ['input']
},
execute: async (args: { input: string }) => {
return `Result: ${args.input}`;
}
});
}-
Register in
src/skills/registry.ts→initBuiltinSkills():
import { registerMySkill } from './builtin/my_skill.js';
registerMySkill();-
Add to build config in
package.json→tsup.entry -
Write tests in
tests/skills/my_skill.test.ts
OpenAI-Compatible (most common): Add an entry to the PROVIDERS config in src/providers/openai_compat.ts:
myprovider: {
name: 'MyProvider',
baseUrl: 'https://api.myprovider.com/v1',
envKey: 'MYPROVIDER_API_KEY',
models: ['myprovider/model-large', 'myprovider/model-small']
}Native Provider (different API):
- Create
src/providers/my_provider.ts - Extend
LLMProviderfrombase.ts - Implement:
chat(),chatStream(),listModels(),healthCheck() - Register in
src/providers/router.ts
- Create
src/channels/my_channel.ts - Extend
ChannelAdapterfrombase.ts - Implement:
connect(),disconnect(),send(),getStatus() - Register in
src/gateway/server.ts
See existing adapters (discord.ts, telegram.ts, slack.ts) for the pattern.
src/
agent/ Core agent loop, multi-agent, orchestrator, goals, Command Post
browsing/ Shared browser pool, CAPTCHA solving
channels/ 15 channel adapters
config/ Zod-validated config schema
context/ ContextEngine plugin system
gateway/ Express HTTP/WS server + Mission Control
mcp/ Model Context Protocol server
memory/ Episodic, learning, relationship, temporal graph
mesh/ P2P mesh networking (mDNS, WebSocket)
providers/ 36 LLM providers (4 native + 32 OpenAI-compatible)
skills/ 100+ skills exposing ~195 tools
utils/ Constants, logger, helpers
voice/ LiveKit WebRTC voice
vram/ GPU VRAM orchestrator
ui/ React 19 SPA (Vite + Tailwind CSS 4)
tests/ 154 vitest test files
e2e/ Playwright E2E tests
By contributing, you agree that your contributions will be licensed under the MIT License.