diff --git a/.gitignore b/.gitignore
index 34311237..15638010 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@ out
.turbo
build
bin
+dist-electron
*.tsbuildinfo
# env
@@ -22,6 +23,9 @@ coverage
server/bin/
server/tmp/
server/migrate
+server/daemon
+server/multica-cli
+server/multica
# Test artifacts
test-results/
@@ -30,6 +34,9 @@ apps/web/test-results/
# context (agent workspace)
.context
+# local settings
+.claude/
+
# platform specific
*.dmg
*.app
diff --git a/CLAUDE.md b/CLAUDE.md
index f30da4fd..312d6832 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -1,8 +1,8 @@
# CLAUDE.md
-This file gives coding agents high-signal guidance for this repository.
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
-## 1. Project Context
+## Project Context
Multica is an AI-native task management platform — like Linear, but with AI agents as first-class citizens.
@@ -10,15 +10,15 @@ Multica is an AI-native task management platform — like Linear, but with AI ag
- Supports local (daemon) and cloud agent runtimes
- Built for 2-10 person AI-native teams
-## 2. Architecture
+## Architecture
**Polyglot monorepo** — Go backend + TypeScript frontend.
-- `server/` — Go backend (Chi + sqlc + gorilla/websocket)
+- `server/` — Go backend (Chi router, sqlc for DB, gorilla/websocket for real-time)
- `apps/web/` — Next.js 16 frontend (App Router)
-- `packages/` — Shared TypeScript packages (ui, types, sdk, utils)
+- `packages/` — Shared TypeScript packages (ui, types, sdk, store, hooks, utils)
-### 2.1 Web App Structure (`apps/web/`)
+### Web App Structure (`apps/web/`)
The frontend uses a **feature-based architecture** with three layers:
@@ -42,7 +42,7 @@ apps/web/
**`shared/`** — Code used across multiple features. Currently only `api.ts` (SDK singleton).
-### 2.2 State Management
+### State Management
- **Zustand** for global client state (`features/auth/store.ts`, `features/workspace/store.ts`).
- **React Context** only for connection lifecycle (`WSProvider` in `features/realtime/`).
@@ -55,7 +55,7 @@ apps/web/
- Cross-store reads use `useOtherStore.getState()` inside actions (not hooks).
- Dependency direction: `workspace` → `auth`, `realtime` → `auth`, `issues` → `workspace`. Never reverse.
-### 2.3 Import Aliases
+### Import Aliases
Use `@/` alias (maps to `apps/web/`):
```typescript
@@ -68,7 +68,48 @@ import { StatusIcon } from "@/features/issues/components";
Within a feature, use relative imports. Between features or to shared, use `@/`.
-## 3. Core Workflow Commands
+### Data Flow
+
+```
+Browser → ApiClient (SDK) → REST API (Chi handlers) → sqlc queries → PostgreSQL
+Browser ← WSClient (SDK) ← WebSocket ← Hub.Broadcast() ← Handlers/TaskService
+```
+
+### Backend Structure (`server/`)
+
+- **Entry points** (`cmd/`): `server` (HTTP API), `daemon` (local agent runtime), `migrate`, `seed`
+- **Handlers** (`internal/handler/`): One file per domain (issue, comment, agent, auth, daemon, etc.). Each handler holds `Queries`, `DB`, `Hub`, and `TaskService`.
+- **Real-time** (`internal/realtime/`): Hub manages WebSocket clients. Server broadcasts events; inbound WS message routing is still TODO.
+- **Auth** (`internal/auth/` + `internal/middleware/`): JWT (HS256). Middleware sets `X-User-ID` and `X-User-Email` headers. Login creates user on-the-fly if not found.
+- **Task lifecycle** (`internal/service/task.go`): Orchestrates agent work — enqueue → claim → start → complete/fail. Syncs issue status automatically and broadcasts WS events at each transition.
+- **Database**: sqlc generates Go code from SQL in `pkg/db/queries/` → `pkg/db/generated/`. Migrations in `migrations/`.
+- **Routes** (`cmd/server/router.go`): Public routes (auth, health, ws) + protected routes (require JWT) + daemon routes (unauthenticated, separate auth model).
+
+### Frontend Structure (`apps/web/`)
+
+- **App Router layout groups**: `(auth)/` for login, `(dashboard)/` for protected routes
+- **Auth context** (`lib/auth-context.tsx`): Global provider for user, workspace, members, agents. Hydrates from localStorage. Provides actor lookup helpers (`getMemberName`, `getAgentName`, `getActorName`).
+- **WebSocket context** (`lib/ws-context.tsx`): Wraps `WSClient` from SDK. `useWSEvent()` hook auto-subscribes/unsubscribes.
+- **API client** (`lib/api.ts`): Singleton `ApiClient` from `@multica/sdk`, initialized from localStorage.
+- **State**: Zustand stores (`@multica/store`) for issues, agents, inbox. WebSocket events keep stores in sync without re-fetching.
+
+### Key Packages
+
+- **`@multica/sdk`**: `ApiClient` (REST) and `WSClient` (WebSocket) classes. All backend communication goes through here.
+- **`@multica/types`**: Shared domain types + WebSocket event types (issue:created/updated/deleted, task:*, agent:status, comment:*, inbox:new, daemon:*).
+- **`@multica/store`**: Zustand stores — simple arrays with add/update/remove. No persistence; memory only.
+- **`@multica/ui`**: shadcn/ui component library with Radix primitives, Tailwind CSS 4, Shiki syntax highlighting for markdown.
+- **`@multica/hooks`**: `useRealtime()` (WS → store sync), `useIssues()`, `useAgents()`, `useInbox()` (fetch + cache).
+
+### Multi-tenancy
+
+All queries filter by `workspace_id`. Membership checks gate access. `X-Workspace-ID` header routes requests to the correct workspace.
+
+### Agent Assignees
+
+Assignees are polymorphic — can be a member or an agent. `assignee_type` + `assignee_id` on issues. Agents render with distinct styling (purple background, robot icon).
+
+## Commands
```bash
# One-click setup & run
@@ -88,16 +129,34 @@ pnpm test # TS tests (Vitest)
make dev # Run Go server (port 8080)
make daemon # Run local daemon
make test # Go tests
-make sqlc # Regenerate sqlc code
+make sqlc # Regenerate sqlc code after editing SQL in server/pkg/db/queries/
make migrate-up # Run database migrations
make migrate-down # Rollback migrations
+# Run a single Go test
+cd server && go test ./internal/handler/ -run TestName
+
+# Run a single TS test
+pnpm --filter @multica/web exec vitest run src/path/to/file.test.ts
+
+# Run a single E2E test (requires backend + frontend running)
+pnpm exec playwright test e2e/tests/specific-test.spec.ts
+
# Infrastructure
make db-up # Start shared PostgreSQL
make db-down # Stop shared PostgreSQL
```
-## 4. Coding Rules
+### Worktree Support
+
+For isolated feature testing with a separate database:
+```bash
+make worktree-env # Generate .env.worktree with unique DB/ports
+make setup-worktree # Setup using .env.worktree
+make start-worktree # Start using .env.worktree
+```
+
+## Coding Rules
- TypeScript strict mode is enabled; keep types explicit.
- Go code follows standard Go conventions (gofmt, go vet).
@@ -108,7 +167,7 @@ make db-down # Stop shared PostgreSQL
- Treat compatibility code as a maintenance cost, not a default safety mechanism. Avoid "just in case" branches that make the codebase harder to reason about.
- Avoid broad refactors unless required by the task.
-## 5. UI/UX Rules
+## UI/UX Rules
- Prefer `packages/ui` shadcn components over custom implementations.
- **shadcn official components** → `packages/ui/src/components/ui/` — keep this directory clean; install missing components via `npx shadcn add`, do not mix in business code.
@@ -119,12 +178,12 @@ make db-down # Stop shared PostgreSQL
- Pay close attention to **overflow** (truncate long text, scrollable containers), **alignment**, and **spacing** consistency.
- When unsure about interaction or state design, ask — the user will provide direction.
-## 6. Testing Rules
+## Testing Rules
- **TypeScript**: Vitest. Mock external/third-party dependencies only.
- **Go**: Standard `go test`. Tests should create their own fixture data in a test database.
-## 7. Commit Rules
+## Commit Rules
- Use atomic commits grouped by logical intent.
- Conventional format:
@@ -135,7 +194,7 @@ make db-down # Stop shared PostgreSQL
- `test(scope): ...`
- `chore(scope): ...`
-## 8. Verification Commands
+## Minimum Pre-Push Checks
```bash
make check # Runs all checks: typecheck, unit tests, Go tests, E2E
@@ -151,7 +210,30 @@ make test # Go tests only
pnpm exec playwright test # E2E only (requires backend + frontend running)
```
-## 9. E2E Test Patterns
+## AI Agent Verification Loop
+
+After writing or modifying code, always run the full verification pipeline:
+
+```bash
+make check
+```
+
+This runs all checks in sequence:
+1. TypeScript typecheck (`pnpm typecheck`)
+2. TypeScript unit tests (`pnpm test`)
+3. Go tests (`go test ./...`)
+4. E2E tests (auto-starts backend + frontend if needed, runs Playwright)
+
+**Workflow:**
+- Write code to satisfy the requirement
+- Run `make check`
+- If any step fails, read the error output, fix the code, and re-run `make check`
+- Repeat until all checks pass
+- Only then consider the task complete
+
+**Quick iteration:** If you know only TypeScript or Go is affected, run individual checks first for faster feedback, then finish with a full `make check` before marking work complete.
+
+## E2E Test Patterns
E2E tests should be self-contained. Use the `TestApiClient` fixture for data setup/teardown:
diff --git a/Makefile b/Makefile
index f9deb94d..07d3213b 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-.PHONY: dev daemon build test migrate-up migrate-down sqlc clean setup start stop check worktree-env setup-main start-main stop-main check-main setup-worktree start-worktree stop-worktree check-worktree db-up db-down
+.PHONY: dev daemon cli build test migrate-up migrate-down sqlc seed clean setup start stop check worktree-env setup-main start-main stop-main check-main setup-worktree start-worktree stop-worktree check-worktree db-up db-down
MAIN_ENV_FILE ?= .env
WORKTREE_ENV_FILE ?= .env.worktree
@@ -115,11 +115,17 @@ dev:
cd server && go run ./cmd/server
daemon:
- cd server && MULTICA_CODEX_WORKDIR="${MULTICA_CODEX_WORKDIR:-$(abspath .)}" go run ./cmd/daemon
+ cd server && MULTICA_REPOS_ROOT="${MULTICA_REPOS_ROOT:-$(abspath .)}" go run ./cmd/multica daemon
+
+cli:
+ cd server && go run ./cmd/multica $(ARGS)
+
+VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
+COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)
build:
cd server && go build -o bin/server ./cmd/server
- cd server && go build -o bin/daemon ./cmd/daemon
+ cd server && go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT)" -o bin/multica-cli ./cmd/multica
test:
$(REQUIRE_ENV)
diff --git a/apps/web/app/(dashboard)/settings/page.tsx b/apps/web/app/(dashboard)/settings/page.tsx
index ea117c7b..27ecc064 100644
--- a/apps/web/app/(dashboard)/settings/page.tsx
+++ b/apps/web/app/(dashboard)/settings/page.tsx
@@ -104,6 +104,7 @@ export default function SettingsPage() {
const [description, setDescription] = useState(
workspace?.description ?? "",
);
+ const [context, setContext] = useState(workspace?.context ?? "");
const [profileName, setProfileName] = useState(user?.name ?? "");
const [avatarUrl, setAvatarUrl] = useState(user?.avatar_url ?? "");
const [saving, setSaving] = useState(false);
@@ -124,6 +125,7 @@ export default function SettingsPage() {
useEffect(() => {
setName(workspace?.name ?? "");
setDescription(workspace?.description ?? "");
+ setContext(workspace?.context ?? "");
}, [workspace]);
useEffect(() => {
@@ -138,7 +140,8 @@ export default function SettingsPage() {
try {
const updated = await api.updateWorkspace(workspace.id, {
name,
- description: description || undefined,
+ description,
+ context,
});
updateWorkspace(updated);
setSaved(true);
@@ -339,6 +342,19 @@ export default function SettingsPage() {
placeholder="What does this workspace focus on?"
/>
+
+
+