Linear-style issues, sprints, initiatives, and time tracking — with a
first-class MCP surface so LLM agents are real actors, not afterthoughts.
Docs · Install · Agents & MCP · Shortcuts · DevLog · Roadmap
Most PM tools treat automation as a plugin. Forge was built around the idea
that agents — LLM profiles like victor, mizu — are first-class actors:
they hold ApiKeys with scoped narrowing, get work pushed via webhook, claim
issues from a queue, emit comments, and run time entries like anyone else.
Everything humans can do in the UI, agents can do over MCP.
The result is a minimalist, keyboard-driven surface (warm-earthy tokens, Anthropic-inspired) that feels like Linear, and a plugin plane underneath that lets you compose agents, automations, and LLM loops with real primitives instead of webhook-config stitching.
- Next.js 15 (App Router, server components, typed routes) + TypeScript
- PostgreSQL 16 + Prisma 6 — normalized schema with audit log, event stream, metric rollups, and migrations.
- tRPC 11 with Zod — end-to-end type-safe API for the web UI.
- NextAuth v5 — Credentials + OAuth (GitHub / Google), Prisma adapter.
- Redis — pub/sub for realtime, BullMQ queues, rate limit.
- MinIO (S3-compatible) — polymorphic attachment storage.
- SSE for browser realtime + HMAC-signed webhooks for agent push.
- Tailwind CSS with a warm-earthy design-token system.
- Vitest + Playwright; GitHub Actions for CI.
All tenant-scoped on workspaceId.
| Primitive | What it is |
|---|---|
| Workspace | Tenant. Short key (e.g. AXI, PER, WRK) is the issue prefix and is immutable after create. Slug/name can change freely. |
| Project | Groups issues. Optionally nests under an Initiative. |
| Issue | The unit of work. Optional projectId, optional cycleId, optional human assignees, optional agent assignee. |
| Sprint | Time-boxed iteration. Default length from Workspace.cycleLengthDays. Issues move in/out freely. Stored internally as Cycle; routes (/cycles) and MCP namespace (cycles.*) keep the original name for compatibility. |
| Initiative | Umbrella above projects — quarterly bets, themes. |
| IssueRelation | Directed, typed link (BLOCKS, BLOCKED_BY, DUPLICATES, RELATES_TO). |
| TimeEntry | Per-user duration rows against issues, gated by Workspace.timeTrackingEnabled. |
| Attachment | Polymorphic via targetType + targetId; MinIO-backed. |
| Agent | First-class non-human actor. profileKey (stable handle), capabilities[], webhookUrl, status, maxConcurrent. Receives dispatch via webhook; authenticates via linked ApiKey. |
Forge is the execution layer: approved work lives as issues, backlog items, sprints, assignments, blockers, and done history. Durable notes, raw capture, and private decision logs can stay outside Forge; use issue templates and the Backlog for proposed work that still needs review. Nothing is auto-promoted into an active sprint.
Default issue templates cover dev tasks, agent-ready handoffs, personal tasks, finance follow-ups, side quests, and review items. The agent-ready template asks for objective, system area, acceptance criteria, safety boundaries, and verification path. Shared household or couple-specific workflows are deferred; Forge only ships generic workspace/project/template primitives today.
Forge exposes a dual MCP endpoint at /api/mcp/rpc (JSON-RPC 2.0) and
/api/mcp/:tool (REST alias). 44 tools cover every primitive:
# List all tools
curl -s https://forge.example.com/api/mcp/rpc \
-H "Authorization: Bearer $FORGE_KEY" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# Claim the next queued issue as this agent
curl -s https://forge.example.com/api/mcp/rpc \
-H "Authorization: Bearer $FORGE_KEY" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"issues.claim","arguments":{}}}'
# See what's currently assigned to me
curl -s https://forge.example.com/api/mcp/rpc \
-H "Authorization: Bearer $FORGE_KEY" \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call",
"params":{"name":"issues.assigned","arguments":{}}}'Keys are narrowable: scopes (coarse, a subset of the owning plugin's
manifest) plus optional projectIds / labelIds / initiativeIds arrays
that restrict what the key can see or act on. A per-initiative bot gets
only that initiative; a full-trust agent gets no narrowing.
Auto-dispatch. A workspace can toggle autoDispatch and pick a mode
(ROUND_ROBIN / PRIORITY_MATCH / CAPABILITY_MATCH). Queued issues are
routed to the best-fit agent automatically, with maxConcurrent respected
and AGENT_ASSIGNED events firing into the webhook stream carrying full
decision provenance (payload.dispatch.{mode, candidates, chosen, reason}
— operators can replay why an agent was picked, and which agents were
ineligible, from the event alone).
Dispatch rules run before mode-based selection. Admins configure
(priority, labelId, projectId) → targetAgent rules at
/settings/dispatch-rules; conditions are ANDed with null = wildcard,
first match wins (order-sortable). A matched rule with an ineligible
target falls through to mode-based selection rather than stalling, and
the fallthrough is preserved on the event reason.
Per-agent issue templates. If Agent.templateMarkdown is set, it is
applied to the issue description on assign only when the description is
empty — agents never overwrite human content. Runs in the same
transaction as the assignment for all four assignment paths (auto, manual,
reassign, initial-create-with-assignee).
Heartbeat + auto-offline. Agents ping agent.heartbeat to keep
lastHeartbeatAt fresh. A BullMQ-scheduled sweep (maintenance queue,
every 60s) flips agents to OFFLINE when their heartbeat is older than
Workspace.agentIdleTimeoutMinutes, emitting AGENT_STATUS_CHANGED.
Notification bridge. Built-in plugin at plugins/notification-bridge/
forwards configurable EventKinds to Slack + Discord webhooks with
formatted payloads. Configure per-workspace via Plugin.manifest.config;
supports mentionsOnly filtering and block-lists noisy internal kinds.
Hermes integration — agents configured via a single YAML block:
mcp_servers:
forge:
url: "https://forge.example.com/api/mcp/rpc"
headers:
Authorization: "Bearer forge_sk_..."
timeout: 120
connect_timeout: 60cp .env.example .env # fill in values (S3 vars are pre-filled for the
# bundled MinIO; rotate creds before deploying)
pnpm installThen pick a development mode:
# A. Isolated local stack (recommended for UI iteration) — boots
# docker/docker-compose.yml (Postgres :55432 / Redis :56379 / MinIO
# :59000), migrates, seeds rich demo data, then runs HMR dev. One
# command, no manual bootstrap. Sign in: owner@forge.local / forge-dev.
pnpm dev:local # http://localhost:3000
pnpm dev:local --fresh # wipe + reseed the local DB first
pnpm dev:local --no-seed # skip seeding (e.g. after db:clone-prod)
# B. Live data — HMR dev server pointed at the *deployed* Postgres / Redis
# / MinIO. Instant frontend iteration against real data; edits are real.
pnpm dev # http://localhost:3000
# Optional: clone the deployed DB into the local stack for a safe sandbox
# with real data (full Postgres-level copy; attachment bytes excluded).
pnpm db:clone-prod && pnpm dev:local --no-seedManual DB bootstrap (only needed if you're not using dev:local):
cd docker && docker compose up -d # Postgres + Redis + MinIO
pnpm prisma:generate
pnpm prisma:migrate # applies all migrations
pnpm prisma:seed # rich demo fixtures (workspace, issues,
# sprints, agents, labels, relations)
pnpm worker # optional separate process for queuesMove data between instances from Settings → Admin → Data export / import
(portable per-workspace JSON), or pnpm db:clone-prod for a full replica.
# Docs (optional — VitePress, served at /docs/)
pnpm docs:install # one-time, standalone install in docs/
pnpm docs:dev # http://localhost:5181/docs/
pnpm dev:all # both app and docs in one terminalThe full user docs live in ./docs/ as a VitePress project — Guide,
Concepts, Agents (Hermes / dispatch / SLAs / AI), Automation
(webhooks / plugins / API keys), and a reference for MCP, tRPC,
events, and env. Once the repo flips public, .github/workflows/docs.yml
deploys the site to GitHub Pages.
| Shortcut | Action |
|---|---|
⌘K / / |
Command palette |
⇧C |
Quick-create (pathname-aware) |
? |
Keyboard help overlay |
⌘\ |
Collapse sidebar |
G I |
Go to Inbox |
G D |
Go to Dashboard |
G S |
Go to Issues |
G P |
Go to Projects |
G C |
Go to Sprints |
G A |
Go to Analytics |
G E |
Go to Agents |
⇧A |
Assign agent (issue detail) |
Full table lives in src/lib/shortcuts.ts and is rendered by the ?
overlay.
src/
app/ # App Router pages + API route handlers
components/ # UI (sidebar, topbar, modal primitives, palette)
lib/ # utils, trpc client, keyboard, providers
server/ # db, redis, auth, trpc routers, services
routers/ # workspace, project, issue, cycle, agent, …
services/ # mcp, dispatcher, api-key-auth, storage (MinIO)
prisma/ # schema + migrations + seed
plugins/ # local-runtime plugin handlers (sample)
scripts/ # seed-agents, etc.
docker/ # dev compose (pg + redis + minio)
tests/ # unit (vitest) + e2e (playwright)
- Self-host (recommended):
docker compose up -dbehind Traefik/Caddy. Entrypoint runsprisma migrate deployon boot. ProvideDATABASE_URL,REDIS_URL,AUTH_SECRET,PLUGIN_JWT_SECRET, S3/MinIO creds. - Vercel: tRPC, auth, SSE, and MCP routes are Node-runtime compatible; external Postgres + Redis + S3 required.
MIT.