Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

## What is Deuce

Deuce is an open source shared workspace for AI-assisted development. Sessions are like Slack channels where multiple team members and AI agents with specific roles collaborate in real-time, backed by isolated DevPod workspaces. Inspired by GitHub's ACE research prototype.
Deuce is an open source shared workspace for AI-assisted development. Sessions are like Slack channels where team members and the single built-in agent ("deuce", invoked via @deuce) collaborate in real-time, backed by isolated DevPod workspaces. Inspired by GitHub's ACE research prototype.

## Development Commands

Expand All @@ -14,7 +14,8 @@ Deuce is an open source shared workspace for AI-assisted development. Sessions a
npm run dev # Dev server on :4000 (proxies /api and /ws to :8080)
npm run build # Production build to dist/
npm run lint # ESLint
npx tsc --noEmit # TypeScript type check (no test suite yet)
npm test # Vitest (pure-logic suites: reducer, visibility)
npx tsc -b --force # TypeScript type check (solution-style tsconfig — bare tsc --noEmit checks nothing)
```

### Backend (Go, runs from server/)
Expand Down Expand Up @@ -54,7 +55,7 @@ cd server && make migrate # Run migrations + seed data
- **Database**: PostgreSQL via pgx/v5, queries generated by sqlc from `server/internal/db/queries/*.sql`
- **WebSocket hub**: `server/internal/ws/hub.go` — manages per-session subscriptions, broadcasts events
- **Auth**: Two modes selected by `DEUCE_AUTH_MODE`. `dev` (default) injects a fixed user ID from `DEUCE_USER_ID` — localhost-only, do not bind on a non-loopback interface. `forge-proxy` trusts `X-Forge-*` headers from the [forge-proxy](https://github.com/forgeutah/forge-proxy) reverse proxy (validates the shared secret in constant time, checks a single required role from CSV, auto-provisions users by `forge_user_id`). See `server/internal/auth/forge_proxy.go`.
- **Agent simulation**: Server-side canned responses with random delay when messages contain @mentions (`server/internal/handler/messages.go`)
- **Agent runtime**: One persistent Pi (`pi --mode rpc`) process per session inside its DevPod container, driven over JSONL (`server/internal/agent/`). `@deuce` mentions are detected server-side (`server/internal/handler/messages.go`) and enqueue tasks on the session's serial queue; `GET/PUT /api/agent` reads/edits deuce's global system prompt. The agent's fixed UUID is `agent.DeuceAgentID` (mirrored by `DEUCE` in `src/lib/deuce.ts`); the nil UUID is the system-notice author sentinel.
- **DevPod**: Workspace manager shells out to `devpod` CLI via os/exec (`server/internal/workspace/manager.go`)

### Adding a New API Endpoint
Expand All @@ -71,11 +72,11 @@ Create `server/internal/db/migrations/NNN_description.sql` with `-- +goose Up` /

## Key Conventions

- **Design system**: Dark mode only. GitHub Primer color tokens defined as CSS variables in `src/styles/globals.css`. Agent roles have assigned colors (Coder=blue, Reviewer=purple, Planner=green, Tester=yellow, Designer=pink).
- **Design system**: Dark mode only. GitHub Primer color tokens defined as CSS variables in `src/styles/globals.css`. The agent accent color comes from the `DEUCE` constant (`src/lib/deuce.ts`), fed into the `--ac` custom property the super-threads styles consume.
- **Component library**: shadcn/ui (New York style) with Radix primitives, Tailwind v4, Lucide icons. Components live in `src/components/ui/`.
- **TypeScript types**: All shared types in `src/types/index.ts`. Backend response structs use camelCase JSON tags to match frontend types.
- **Error responses**: `{ "error": { "code": "ERROR_CODE", "message": "..." } }` — use `writeError()` helper in handlers.
- **WebSocket events**: Defined in `server/internal/ws/events.go`. Client sends `join`/`leave`/`mark_read`. Server sends `new_message`, `agent_status`, `typing_indicator`, `activity_update`, `session_update`, `unread_update`.
- **WebSocket events**: Defined in `server/internal/ws/events.go`. Client sends `join`/`leave`/`mark_read`/`steer`. Server sends `new_message`, `activity_update`, `session_update`, `unread_update`, `workspace_log`, and the AgentRunEvent family (`task_enqueued`/`task_started`/`task_awaiting_input`/`task_completed`, `action_started`/`action_completed`) applied client-side by seq.
- **Message deduplication**: The Zustand store deduplicates by ID in `addMessage` and `addActivity` to handle race conditions between REST responses and WebSocket broadcasts.
- **Path alias**: `@/` maps to `./src/` (configured in vite.config.ts and tsconfig.app.json).

Expand All @@ -89,18 +90,17 @@ GITHUB_TOKEN= # GitHub PAT for repo listing (optional)
DEVPOD_BIN=devpod # DevPod binary path
DEVPOD_PROVIDER= # DevPod provider (empty = default)

# Agent harness. "pi" (default) runs Pi (pi.dev) in --mode rpc inside each
# session's DevPod container, driven over a persistent JSONL channel. "claude"
# is the legacy claude -p executor, kept as an emergency rollback. PiProvider/
# PiModel select the Pi backend (v1 runs Claude models through Pi).
DEUCE_AGENT_HARNESS=pi
# Agent backend. Pi (pi.dev) runs in --mode rpc inside each session's DevPod
# container, driven over a persistent JSONL channel — one process per session.
# PiProvider/PiModel select the Pi backend (v1 runs Claude models through Pi).
DEUCE_PI_PROVIDER=anthropic
DEUCE_PI_MODEL=claude-haiku-4-5

# Global system prompt prepended to every agent's own system_prompt on the Pi
# path (applied via --append-system-prompt at launch). Empty uses the built-in
# default (agent.DefaultBaseSystemPrompt) that steers agents to the ask_user
# tool when blocked on a human decision; set it to override.
# Global system prompt prepended to deuce's configurable system_prompt
# (applied via --append-system-prompt at launch). Empty uses the built-in
# default (agent.DefaultBaseSystemPrompt) that steers the agent to the
# ask_user tool when blocked on a human decision; set it to override. The
# per-instance half is editable at runtime via PUT /api/agent.
DEUCE_AGENT_SYSTEM_PROMPT=

# Auth mode (default "dev"; set to "proxy" when running behind a header-trust
Expand Down
24 changes: 10 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ Heavily inspired by Maggie Appleton's [Zero Alignment](https://maggieappleton.co

Each **session** is like a Slack channel for one piece of work:

- Humans and agents post messages in the same thread.
- Humans and **deuce** — the built-in agent — post messages in the same thread; `@deuce` brings it into the work.
- Every session is backed by an **isolated [DevPod](https://devpod.sh) workspace** — a real dev container with the repo checked out, the shell available, and the build/test tooling installed.
- Agents (currently Claude Code) execute *inside* that container, so what they propose is what would actually run.
- The agent runs *inside* that container (a persistent [Pi](https://pi.dev) process), so what it proposes is what would actually run.
- The session carries the whole arc: a plan tab, a chat, a file browser, a live terminal, and workspace logs — visible to everyone in the room.

---
Expand Down Expand Up @@ -95,7 +95,7 @@ See [CLAUDE.md](CLAUDE.md) for the full developer guide (commands, conventions,
| Backend | Go, chi v5, pgx/v5, sqlc, coder/websocket |
| Database | Postgres 17 (migrations via goose) |
| Workspaces | DevPod (Docker provider by default) |
| Agents | Claude Code, invoked headless inside the dev container via `devpod ssh` |
| Agent | Pi (`pi --mode rpc`), one persistent process per session inside the dev container via `devpod ssh` |

Real-time updates use a single WebSocket hub with per-session subscriptions. The frontend keeps state in a single Zustand store and lazy-loads messages/activities per session.

Expand All @@ -122,17 +122,13 @@ Organized by the three tracks in [STRATEGY.md](STRATEGY.md). Anything unchecked

The "how agents think and act as teammates" layer.

- [x] Multi-agent presence in a session (DB model, UI, @mentions)
- [x] Simulated agent responses (canned, with delays) — placeholder
- [x] Agent settings dialog skeleton
- [x] Agent system-prompt column + migration
- [ ] **Real Claude Code execution inside the DevPod container** (in progress — see [plan](docs/plans/2026-05-08-001-feat-real-agents-devcontainer-plan.md))
- [ ] Stream agent output to chat with expandable tool-call details
- [ ] Sequential agent queue per session, with cancel
- [ ] Full agent CRUD (create, edit, delete, soft-delete with history preserved)
- [ ] Agent session continuity (`--resume` + chat-history context injection)
- [ ] Per-agent provider/model selection (Anthropic, OpenAI, local)
- [ ] Concurrent agents on isolated git branches
- [x] One built-in agent (**deuce**) in every session — @mention to invoke, serial task queue with cancel, live task/action cards
- [x] Real agent execution inside the DevPod container (persistent Pi process per session)
- [x] Interactive agent questions (`ask_user` → typed prompts in the thread drawer)
- [x] Global system-prompt editing for deuce
- [ ] Skills: modulate deuce's behavior per task (`/skill:` expansion) instead of role agents
- [ ] Subagents as the parallelism tier (bounded workers reporting back as deuce)
- [ ] Agent session continuity across server restarts
- [ ] Autonomous agent behaviors (proactive review on commits, etc.)

### Track 2 — Chat & Presence
Expand Down
Loading