first-tree — the unified CLI and infrastructure for agent teams. It is a pnpm monorepo for the CLI, server, client runtime, web app, docs, and agent-team tooling.
What first-tree is NOT:
- not an LLM agent itself (agent logic lives elsewhere)
- not an orchestration framework
- Server: Fastify / Drizzle ORM / PostgreSQL / Zod
- Client: fetch + ws (Cloud SDK/observability + generic Runtime + built-in Providers)
- Command: Commander.js / @inquirer/prompts (unified CLI)
- Shared: Zod schemas + TypeScript types + config system
- Web: React 19 / Vite
- Tooling: pnpm / Turborepo / Biome / Vitest / tsdown
- Node.js: minimum 22.13, recommended 24
pnpm install
docker compose up -d
pnpm --filter @first-tree/server dev
pnpm --filter @first-tree/web dev
pnpm check && pnpm typecheck
pnpm test
pnpm --filter @first-tree/server db:generate
pnpm --filter @first-tree/server db:migrateFull CLI commands and env vars live in docs/cli-reference.md. Per-package scripts live in each package's package.json.
- HTTP routes, JWT auth, scope helpers, or multi-org behavior: read docs/development/http-path-conventions.md before editing. It is the single source of truth.
- Onboarding kickoff/chat-start behavior: read docs/development/onboarding-kickoff-contract.md before editing
/me/onboarding/kickoff,/orgs/:orgId/context-tree/setup-chat, or the client inbound prompt formatting. - Running an in-tree CLI next to prod/staging: use
scripts/dev-install.shand read docs/development/local-dev-isolation.md.
skills/first-tree-write/SKILL.md— source-driven Context Tree authorshipskills/first-tree-read/SKILL.md— task-scoped Context Tree readingskills/first-tree-seed/SKILL.md— one-time bootstrap for an empty treeskills/first-tree-qa/SKILL.md— risk-tiered professional QA workflow
Operator-only flows such as login, daemon install, and agent create belong in docs/cli-reference.md and docs/onboarding-guide.md, not in skills.
apps/cli/— unified CLI source; CI publishes channel-specific packagesapps/doc-website/— documentation websitepackages/shared/—@first-tree/sharedschemas, types, and configpackages/server/—@first-tree/serverFastify API serverpackages/client/—@first-tree/clientCloud SDK/observability, generic Runtime, and built-in Providerspackages/web/—@first-tree/webReact workspacepackages/skill-evals/— eval tooling for repo-local skillspackages/qa/— internal QA workflow assets for agent-run validationdocs/— user, operator, development, migration, and troubleshooting docsskills/— repo-local skill payloads
- Package boundaries: Server, Client, Command, and Web are independently packaged/deployed and share code through
@first-tree/shared. The CLI is the user-facing command surface and depends only on Client + Shared. Server ships separately as the SaaS Docker image. - Server state: Server is stateless. PostgreSQL is the only persistence/queue/notification backend; do not add Redis or MQ.
- Unified user-JWT auth: A single user JWT authorizes Web/Admin API calls and every agent the user manages on the client WebSocket. Route classification, JWT shape, and scope helpers live in docs/development/http-path-conventions.md. Channel homes live in docs/development/local-dev-isolation.md. Agents bind via
agents.client_id+agent:pinned; R-RUN is re-evaluated at everyagent:bind. Switching users goes throughfirst-tree login <code>and the local-client switch path;logout --purgeretires the current server client and cuts its runtime routes before destructive local cleanup, after which cleared agents can be moved to a new connected runtime from Web. - Inbox boundary: Server writes to Inbox; Client pulls / receives WebSocket notifications. Delivery is at-least-once; Client deduplicates.
- Agent identity: Agents are managed by the server Admin API. Agent profile markdown lives in
agents.profile. Context Tree integration is optional and injected by Client at workspace startup. - Credentials: Sensitive credentials are AES-256-GCM encrypted at the application layer via
services/crypto.ts. - Messages: Message IDs are UUID v7 and messages are immutable after creation.
- Use
unknown+ type narrowing instead ofany. - Avoid
asassertions; when unavoidable for third-party libraries, explain why nearby. - Do not use
enum; useas constobjects and Zod-compatible literals. - Use
import type, prefertypeoverinterfaceunless extension/implementation requires an interface, and give public APIs explicit return types. - Each package's
src/index.tsis its public entry point. - Zod is the source of truth for DTOs; derive TypeScript types with
z.infer<typeof schema>. - Never hand-edit Drizzle migrations; use
drizzle-kit generateanddrizzle-kit migrate. - Services throw exceptions and API layers map them to HTTP status codes; do not use empty
catch {}blocks. - Follow existing naming and Biome formatting.
- English everywhere on GitHub: code, comments, commits, PRs, issues, branch names, and CI logs.
- Run
pnpm check && pnpm typecheckafter changes. Runpnpm testbefore opening a PR unless the change is clearly docs-only.
- Do not make or delegate any database change—including schema, migrations, constraints, indexes, defaults, backfills, data rewrites, or persistence semantics—without first obtaining the human's explicit approval for that specific change.
- Update shared schemas/types first when a change crosses packages.
- Server features usually flow: shared schema -> Drizzle table (if persistent) -> service -> API route -> migration -> tests.
- Client Cloud SDK/observability lives under
src/cloud/(src/cloud/sdk.ts); provider families and shared handler helpers live undersrc/providers/; generic Runtime lives undersrc/runtime/. - CLI business logic belongs in
core/; command files should stay thin and callcore/*. Wire commands incli/index.ts, and export public helpers from bothcore/index.tsandsrc/index.ts. - Config changes belong in
shared/src/config/.
- Route each check to its layer, in the same PR as the behavior: deterministic behavior -> product tests (Vitest per package;
pnpm testbefore a PR); agent-skill regression ->@first-tree/skill-evals; judgment / live / cross-surface validation ->@first-tree/qacases (packages/qa/cases/, prose prompts, not executable specs). If a check can be made stable, it belongs in product tests. - Before a PR, self-check QA risk. Use the lowest tier and narrowest affected scope that can answer the question: localized deterministic changes use matching package/named tests, ordinary live validation starts only affected surfaces plus credible adjacent boundaries, and isolated QA is reserved for release-sensitive, clearly major/high-risk, cross-surface, or explicit requests. Reuse the QA-owned warm environment for the same task and keep compatible infrastructure after the report while resetting task-owned state. If a change touches a cross-surface, runtime, provider/auth, WS/inbox, or boot/health path, find or add a matching case under
packages/qa/cases/and flag any release/major-feature QA need in the PR. Agent-run QA is human-requested, not a CI gate or auto runner; loadskills/first-tree-qa/SKILL.mdand followpackages/qa/AGENTS.mdwhen asked to run it. - When using
@first-tree/skill-evals, agents may run only no-model code checks such aseval:floor; model-backed gate, quality, and periodic cases require an explicit human request. For repo-local skill changes, keep scope and verification minimal: do not add or rewrite eval cases without a confirmed contract change or reproduced regression. Review blocks only on in-scope requirement or constraint violations, regressions, safety risks, deterministic check failures, or contradictions; everything else is a non-blocking follow-up.
- Branching: trunk-based; feature branch -> PR -> squash merge -> main.
- Branch naming: CI accepts only
feat/xxx,fix/xxx,refactor/xxx,test/xxx,docs/xxx,chore/xxx, ormerge/xxx. Do not use agent/person prefixes such ascodex/xxx. - Commit messages: Conventional Commits, e.g.
feat: xxx,fix: xxx,refactor: xxx,test: xxx,docs: xxx. - Do not edit
versionfields in anypackage.json. Version bumps are handled by CI on tag push or by a maintainer cutting a tag.