This guide covers setting up a development environment and contributing to Horizon.
- Prerequisites
- Getting Started
- Project Structure
- Development Workflow
- Code Style
- Testing
- Debugging
- Common Tasks
| Tool | Version | Purpose |
|---|---|---|
| Bun | >= 1.3.6 | Runtime and package manager |
| Node.js | >= 20 | Alternative runtime |
| Docker | Latest | Qdrant, Redis containers |
| Git | Latest | Version control |
# Windows (PowerShell)
powershell -c "irm bun.sh/install.ps1 | iex"
# macOS/Linux
curl -fsSL https://bun.sh/install | bashgit clone https://github.com/your-org/horizon.git
cd horizon
bun install# Start Qdrant for memory features
docker run -d --name qdrant -p 6333:6333 qdrant/qdrant
# Optional: Start Redis for sessions
docker run -d --name redis -p 6379:6379 redis:alpine# Agent environment
cp apps/agent/.env.example apps/agent/.envEdit apps/agent/.env:
# Choose your LLM provider
MODEL_PROVIDER=groq
MODEL_NAME=meta-llama/llama-4-scout-17b-16e-instruct
# API Keys (at least one required)
GROQ_API_KEY=your-key-here
# OPENAI_API_KEY=your-key-here
# ANTHROPIC_API_KEY=your-key-here
# JWT secret for authentication
JWT_SECRET=dev-secret-change-in-production# Start both frontend and agent
bun dev
# Or start individually
bun dev:web # Frontend only (port 3000)
bun dev:agent # Agent only (port 2024)- Frontend: http://localhost:3000
- Backend API: http://localhost:2024
- Qdrant Dashboard: http://localhost:6333/dashboard
This is a Turborepo monorepo with pnpm workspaces.
Horizon/
├── apps/
│ ├── web/ # Next.js 16 frontend
│ │ ├── app/ # App Router pages
│ │ ├── components/ # React components
│ │ ├── lib/ # Utilities, stores, hooks
│ │ └── data/ # Local JSON database
│ │
│ └── agent/ # TypeScript LangGraph agent server
│ ├── src/
│ │ ├── agent/ # Graph, nodes, tools
│ │ ├── assistants/ # Assistant management
│ │ └── lib/ # Config, utilities
│ └── langgraph.json # LangGraph config
│ │
│ └── backend-py-legacy/ # Python FastAPI (reference)
│
├── packages/
│ ├── ui/ # Shared shadcn/ui components
│ ├── agent-memory/ # Qdrant memory system
│ ├── agent-web/ # Web scraping tools
│ └── shell/ # Shell execution
│
├── config/ # Configuration files
│ ├── config.schema.json
│ ├── horizon.example.json
│ └── horizon.json # Your local config (gitignored)
│
├── docs/ # Documentation
├── turbo.json # Turborepo config
└── pnpm-workspace.yaml # Workspace definition
| Command | Description |
|---|---|
bun dev |
Start all dev servers |
bun dev:web |
Start frontend only |
bun dev:agent |
Start agent only |
bun build |
Build all packages |
bun lint |
Check code style |
bun lint:fix |
Fix code style issues |
bun typecheck |
Type check all packages |
Turbo manages task execution across the monorepo:
# Run dev in all packages with dependencies
turbo dev
# Build only changed packages
turbo build --filter=...[origin/main]
# Run lint in parallel
turbo lint --parallel- Create a branch:
git checkout -b feature/my-feature - Make changes: Edit relevant files
- Run checks:
bun lint && bun typecheck - Test: Verify functionality
- Commit:
git commit -m "feat: add my feature"
This project uses Ultracite (Biome) for linting and formatting.
# Check for issues
bun lint
# Auto-fix issues
bun lint:fix- TypeScript: Explicit types, avoid
any, useunknownwhen needed - React: Function components, hooks at top level, proper key props
- Imports: Grouped (React → external → workspace → local)
- Naming: PascalCase components, camelCase functions/variables
- Formatting: 2 spaces, single quotes, semicolons required
See .kilocode/rules/ultracite.md for detailed rules.
VS Code:
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "biomejs.biome",
"[typescript]": {
"editor.defaultFormatter": "biomejs.biome"
}
}# Run all tests
bun test
# Run specific test file
bun test path/to/test.tscd apps/backend-py-legacy
# Run all tests
pytest
# Run with coverage
pytest --cov=src# Enable debug logging
DEBUG=langgraph:* bun dev:agentOpen browser DevTools. Key areas:
- React components: React DevTools extension
- State: Zustand devtools middleware
- Network: Check
/api/requests and SSE streams
// Agent graph execution
console.log("[Agent] State:", state);
// Tool execution
console.log("[Tool] Calling:", toolName, args);
// Config loading
console.log("[Config] Loaded from:", configPath);- Define tool in
apps/agent/src/agent/tools/:
export const myTool = tool({
name: "my_tool",
description: "Does something useful",
schema: z.object({
input: z.string(),
}),
func: async ({ input }) => {
// Implementation
return "result";
},
});- Add to tools array
- Create UI renderer in
apps/web/components/chat/if needed
- Create in
packages/ui/src/components/ui/if shared - Or in
apps/web/components/if app-specific - Export from appropriate
index.ts
- Update state in
apps/agent/src/agent/state.ts - Modify nodes in
apps/agent/src/agent/nodes/ - Update graph edges in
apps/agent/src/agent/graph.ts
- Add to
apps/agent/src/lib/config.tsschema - Document in
.env.example - Update
docs/configuration.md
# Clear caches
rm -rf .turbo apps/*/.next apps/*/dist
bun install# Find process using port
lsof -i :3000
lsof -i :2024
# Kill process
kill -9 <PID># Check Qdrant is running
docker ps | grep qdrant
# Restart Qdrant
docker restart qdrant# Rebuild packages
bun build
# Or just typecheck
bun typecheck