your CTO, on autopilot.
Single-chat autopilot for CEOs running outsourced or GCC engineering teams. The CEO describes what they want; ctobrain.ai produces typed artifacts (PRD, ERD, OpenAPI, SQL, WBS, epics, tasks, Gantt, resource plan, headcount, budget, Upwork post, screening rubric, …) and takes draft-then-approve actions across GitHub / Slack / Linear / Vercel / AWS / Azure / GCP / Supabase / Upstash / Sentry. Layered on top of Upwork — humans do the work; v1 hands off to Upwork manually (no Upwork API).
Plan: see the approved plan in ~/.claude/plans/i-want-to-create-ctobrain.ai.md.
- Web — Next.js 14 (App Router) + TypeScript + Tailwind, single chat UI with SSE consumer.
- API — FastAPI (Python 3.11) + Anthropic SDK + SQLAlchemy + sse-starlette.
- DB — Postgres 16 + pgvector (Supabase in prod,
pgvector/pgvector:pg16Docker image locally).
apps/
web/ Next.js chat UI
api/ FastAPI orchestrator + AI services + tool adapters
infra/
docker-compose.yml postgres + api for local dev
supabase/migrations/0001_init.sql authoritative DB schema
PRD_template.md original PRD template (also copied into apps/api/templates)
- Node 20+ and pnpm 9+
- Python 3.11+
- Docker (for local Postgres) — or use a Supabase project URL directly
- An Anthropic API key with access to the Claude Opus 4.7 model
# 1. Clone, install root + web deps
pnpm install
# 2. Set up env
cp .env.example .env
# Edit .env — at minimum set ANTHROPIC_API_KEY.
# Generate a tool credential encryption key:
python3 -c "import nacl.utils,base64;print(base64.b64encode(nacl.utils.random(32)).decode())"
# Paste into TOOL_CREDS_ENCRYPTION_KEY=
cp apps/web/.env.local.example apps/web/.env.local
# Edit if you change the API port
# 3. Install API deps
cd apps/api
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cd ../..
# 4. Boot Postgres locally (auto-applies the migration)
docker compose -f infra/docker-compose.yml up -d postgresTerminal 1 — API:
cd apps/api
source .venv/bin/activate
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000Terminal 2 — Web:
pnpm --filter ctobrain-ai-web devOpen http://localhost:3000.
# health
curl http://localhost:8000/healthz
# chat (SSE stream)
curl -N -X POST http://localhost:8000/api/chat \
-H 'Content-Type: application/json' \
-d '{"message":"Build a localised iOS App Store recommendation engine for tier-2/3/4 India users. Two-month timeline, ₹8L budget."}'You should see SSE events: status → intent (structured JSON) → delta… (streaming reply) → done.
In the browser at http://localhost:3000, send the same message — the Goal · confidence chip and constraint pills appear, followed by the streaming assistant reply.
Backend (FastAPI)
app/llm.py— Anthropic streaming + structured tool-use with system-prompt caching.app/services/schemas.py— comprehensive Pydantic types for every v1 artifact.app/services/registry.py— 29 LLM-backed services registered by name (one prompt + one schema each).app/services/deterministic.py— pure-Python services: critical_path (CPM forward+backward pass), gantt_builder, budget_planner.app/services/pipeline.py— 23-stage canonical pipeline (PRD → ERD → … → Onboarding) executed as an async generator, persisting artifacts as it goes.app/orchestrator.py— turn loop: Intent → confidence gate → pipeline → tool ApprovalCards → MemoryExtractor → streamed summary.app/tools/base.py+app/tools/adapters.py— ToolAdapter Protocol + ten v1 adapters (GitHub / Slack / Linear / Vercel / Sentry / AWS / Azure / GCP / Supabase / Upstash). Adapters returnsimulated=Trueuntil real OAuth lands per phase.app/tools/actions.py— composes ApprovalCards from pipeline output.app/state.py— single-tenant in-memory store for sessions, artifacts, approvals, project memory (DB upgrade is one swap).app/main.py— REST endpoints:/healthz,/api/services,/api/sessions(POST+GET),/api/chat(SSE),/api/sessions/{id}/artifacts,/api/approvals(GET),/api/approvals/{id}/decision(POST),/api/projects/{id}/memory(GET+DELETE).
Prompts (29 total) in apps/api/prompts/:
intent, planner, spec_builder, prd_creator, erd_creator, api_contract, data_model, wbs_builder, epic_creator, task_creator, acceptance_criteria, dependency_mapper, timeline_planner, resource_planner, headcount_planner, rate_card_manager, upwork_post, screening_rubric, vendor_scorer, interview_scripter, onboarding_checklist, risk_register, test_plan, progress_tracker, status_report, blocker_escalator, change_request, memory_extractor, approval_card.
Frontend (Next.js 14)
apps/web/lib/sse.ts— typed SSE client +decideApprovalREST helper.apps/web/components/ChatPane.tsx— split-pane chat; left side streams the conversation with intent chip, constraint pills, stage timeline pills, clarify card, memory chip; right side is the artifact drawer.apps/web/components/ArtifactCard.tsx— renders every artifact kind (PRD/Upwork post markdown; ERD with mermaid + collapsible sections; OpenAPI/SQL/Gantt code blocks; Tasks/Epics/Timeline/Resource/Headcount/RateCard/Budget/Risk tables; Screening/Interview/Onboarding panels).apps/web/components/ApprovalCard.tsx— risk-coloured card with Approve / Reject buttons that call the backend.
Database — infra/supabase/migrations/0001_init.sql covers all tables incl. pgvector indexes for session embeddings + best-practices library.
State store (Redis) — apps/api/app/state/ is a Redis-backed state layer. Every state mutation publishes a JSON event onto a per-session pub/sub channel so the UI streams updates without polling. What's tracked, in detail:
- Session FSM (11 states):
created → extracting_intent → awaiting_clarification | running_pipeline → composing_approvals → awaiting_approvals → executing_approval → extracting_memory → drafting_reply → completed | failed. Each transition is appended to a state log withfrom,to,at, optionalnote. - Per-stage FSM (one per pipeline stage, 23 stages by default):
pending → running → completed | failed | skipped. Each stage record carriesstarted_at,completed_at,duration_ms, per-stagetokens_input/output/cache_read/cache_write,cost_inr,attempts, optionalerrorandartifact_id. - Approval FSM:
pending → approved | rejected → executing → executed | failedwith timestamps forcreated_at,decided_at,executed_at. Backend counters (approval_pending/approved/rejected/executed/failed) are kept in lock-step with state changes. - Aggregated metrics: input/output/cache tokens summed across all Claude calls; running cost in INR + USD using Claude Opus 4.7 list pricing (configurable in
app/state/pricing.py). - Pipeline metrics:
pipeline_started_at,pipeline_completed_at,pipeline_duration_ms,stages_total/completed/failed,current_stage,last_stage,last_error. - Memory state:
facts_count,rules_countper project, bubbled to every session of that project on update. - Static label dictionary at
GET /api/state/labelsreturning the full description for every session/stage/approval state value.
Redis key schema (prefixed zb:) — see apps/api/app/state/keys.py.
Validation against a real Redis (db 15, flushed at start) and a mocked Anthropic client:
# Prerequisites: Redis listening on localhost:6379 (docker compose -f infra/docker-compose.yml up -d redis)
cd apps/api
python3.12 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python3 scripts/validate_e2e.pyExpected output:
=== ctobrain.ai end-to-end validation (Redis) ===
events received from chat: 67
artifacts: 23
approvals: 10 (['github', 'linear', 'sentry', 'slack', 'vercel'])
stages started: 23
delta chunks: 3
Redis snapshot:
session.state: completed
stages_completed: 23 / 23
pipeline_duration_ms: 65
tokens.input: 2280
tokens.output: 4520
tokens.cache_read: 1140
cost_inr: ₹31.12
cost_usd: $0.375
approvals.pending: 10
approvals.executed: 1
memory.facts_count: 1
memory.rules_count: 1
state_log entries: 8
pubsub events: 113
OK — Redis state populated, FSM reached `completed`, tokens + cost aggregated, approval lifecycle works.
The script also exercises the approval FSM by transitioning a GitHub card through pending → approved → executing → executed and re-verifying counters.
Additional checks already run:
python3 -m compileall -q app— all Python sources parse cleanly.pnpm --filter ctobrain-ai-web typecheck— TypeScript clean.pnpm --filter ctobrain-ai-web build— Next.js production build succeeds.uvicorn app.main:app— FastAPI boots with 29 services + 10 tools registered.
- Real OAuth for the 10 integrations. Adapters return
simulated=Truefromexecute(). Wiring real SDK calls is per-phase work (P5/P6/P7) — the Protocol shape and approval flow are stable, so swap-in is mechanical. - DB persistence. State currently lives in
app/state.py(in-memory); the schema is already ininfra/supabase/migrations/0001_init.sqland SQLAlchemy models inapp/db/models.pymirror it. Swap is one file (STORE = StateStore()→STORE = SqlStateStore(get_db)). - pgvector RAG. Tables + indexes exist in the migration;
ContextSearchservice is registered in the planner space but not yet plumbed into the canonical pipeline (will be added in P3 follow-up). - Project memory rollup. Session-scoped memory updates are persisted; the cross-session rollup job is deferred to P7.