# Repository: multica-ai/multica # Stars: 15186 ## CLAUDE.md # CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Context Multica is an AI-native task management platform — like Linear, but with AI agents as first-class citizens. - Agents can be assigned issues, create issues, comment, and change status - Supports local (daemon) and cloud agent runtimes - Built for 2-10 person AI-native teams ## Architecture **Go backend + monorepo frontend (pnpm workspaces + Turborepo) with shared packages.** - `server/` — Go backend (Chi router, sqlc for DB, gorilla/websocket for real-time) - `apps/web/` — Next.js frontend (App Router) - `apps/desktop/` — Electron desktop app (electron-vite) - `packages/core/` — Headless business logic (zero react-dom, all-platform reuse) - `packages/ui/` — Atomic UI components (zero business logic) - `packages/views/` — Shared business pages/components (zero next/* imports, zero react-router imports) - `packages/tsconfig/` — Shared TypeScript configuration ### Key Architectural Decisions **Internal Packages pattern** — all shared packages export raw `.ts`/`.tsx` files (no pre-compilation). The consuming app's bundler compiles them directly. This gives zero-config HMR and instant go-to-definition. **Dependency direction:** `views/ → core/ + ui/`. Core and UI are independent of each other. No package imports from `next/*`, `react-router-dom`, or app-specific code. **Platform bridge:** `packages/core/platform/` provides `CoreProvider` — initializes API client, auth/workspace stores, WS connection, and QueryClient. Each app wraps its root with `` and provides its own `NavigationAdapter` for routing. **pnpm catalog** — `pnpm-workspace.yaml` defines `catalog:` for version pinning. All shared deps use `catalog:` references to guarantee a single version across all packages. When adding new shared deps (including test deps), add to catalog first. ### State Management The architecture relies on a strict split between server state and client state. Mixing them is the most common way to break it. - **TanStack Query owns all server state.** Issues, users, workspaces, inbox — anything fetched from the API lives in the Query cache. WS events keep it fresh via invalidation; no polling, no `staleTime` workarounds. - **Zustand owns all client state.** UI selections, filters, drafts, modal state, navigation history. Stores live in `packages/core/` (never in `packages/views/`) so both apps share them. - **React Context** is reserved for cross-cutting platform plumbing — `WorkspaceIdProvider`, `NavigationProvider`. Don't reach for it for general state. - **Auth and workspace stores are the only stores allowed to call `api.*` directly**, because they manage critical state that must exist before queries can run. They're created via factory + injected dependencies, registered by the platform layer. **Hard rules — these are how the architecture stays coherent:** - **Never duplicate server data into Zustand.** If it came from the API, it belongs in the Query cache. Copying it into a store creates two sources of truth and they will drift. - **Workspace-scoped queries must key on `wsId`.** This is what makes workspace switching automatic — the cache key changes, the right data appears, no manual invalidation needed. - **Mutations are optimistic by default.** Apply the change locally, send the request, roll back on failure, invalidate on settle. The user shouldn't wait for the server. - **WS events invalidate queries — they never write to stores directly.** This keeps the cache as the single source of truth and avoids race conditions. - **Persist what's worth preserving across restarts** (user preferences, drafts, tab layout). **Don't persist ephemeral UI state** (modal open/close, transient selections) or server data. **Common Zustand footguns to avoid:** - Selectors must return stable references. Returning a freshly built object or array on every call (e.g. `s => ({ a: s.a, b: s.b })` or `s => s.items.map(...)`) triggers infinite re-renders. Either select primitives separately or use shallow comparison. - Hooks that need workspace context should accept `wsId` as a parameter, not call `useWorkspaceId()` internally — this lets them work outside the `WorkspaceIdProvider` (e.g. in a sidebar that renders before workspace is loaded). ## Commands ```bash # One-command dev (auto-setup + start everything) make dev # Auto-creates env, installs deps, starts DB, migrates, launches app # Explicit setup & run (if you prefer separate steps) make setup # First-time: ensure shared DB, create app DB, migrate make start # Start backend + frontend together make stop # Stop app processes for the current checkout make db-down # Stop the shared PostgreSQL container # Frontend (all commands go through Turborepo) pnpm install pnpm dev:web # Next.js dev server (port 3000) pnpm dev:desktop # Electron dev (electron-vite, HMR) pnpm build # Build all frontend apps pnpm typecheck # TypeScript check (all packages + apps via turbo) pnpm lint # ESLint pnpm test # TS tests (Vitest, all packages + apps via turbo) # Backend (Go) make server # Run Go server only (port 8080) make daemon # Run local daemon make build # Build server + CLI binaries to server/bin/ make cli ARGS="..." # Run multica CLI (e.g. make cli ARGS="config") make test # Go tests 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 TS test (works for any package with a test script) pnpm --filter @multica/views exec vitest run auth/login-page.test.tsx pnpm --filter @multica/core exec vitest run runtimes/version.test.ts pnpm --filter @multica/web exec vitest run app/\(auth\)/login/page.test.tsx # Run a single Go test cd server && go test ./internal/handler/ -run TestName # Run a single E2E test (requires backend + frontend running) pnpm exec playwright test e2e/tests/specific-test.spec.ts # Desktop build & package pnpm --filter @multica/desktop build # Compile TS → JS (reads .env.production) pnpm --filter @multica/desktop package # Package into .app/.dmg/.exe (current platform only) # shadcn — config lives in packages/ui/components.json (Base UI variant, base-nova style) pnpm ui:add badge # Adds component to packages/ui/components/ui/ # Infrastructure make db-up # Start shared PostgreSQL (pgvector/pg17 image) make db-down # Stop shared PostgreSQL ``` ### CI Requirements CI runs on Node 22 and Go 1.26.1 with a `pgvector/pgvector:pg17` PostgreSQL service. See `.github/workflows/ci.yml`. ### Worktree Support All checkouts share one PostgreSQL container. Isolation is at the database level — each worktree gets its own DB name and unique ports via `.env.worktree`. Main checkouts use `.env`. `make dev` auto-detects worktrees and handles everything. For explicit control: ```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). - Keep comments in code **English only**. - Prefer existing patterns/components over introducing parallel abstractions. - Unless the user explicitly asks for backwards compatibility, do **not** add compatibility layers, fallback paths, dual-write logic, legacy adapters, or temporary shims. - If a flow or API is being replaced and the product is not yet live, prefer removing the old path instead of preserving both old and new behavior. - Avoid broad refactors unless required by the task. - New global (pre-workspace) routes MUST use a single word (`/login`, `/inbox`) or a `/{noun}/{verb}` pair (`/workspaces/new`). NEVER add hyphenated word-group root routes (`/new-workspace`, `/create-team`) — they collide with common user workspace names and force endless reserved-slug audits. Reserving the noun (`workspaces`) automatically protects the entire `/workspaces/*` subtree. ### Package Boundary Rules These are hard constraints. Violating them breaks the cross-platform architecture: - `packages/core/` — zero react-dom, zero localStorage (use StorageAdapter), zero process.env, zero UI libraries. **All shared Zustand stores live here**, even view-related ones (filters, view modes) — stores are pure state, not UI. - `packages/ui/` — zero `@multica/core` imports (pure UI, no business logic). - `packages/views/` — zero `next/*` imports, zero `react-router-dom` imports, zero stores. Use `NavigationAdapter` for all routing. - `apps/web/platform/` — the only place for Next.js APIs (`next/navigation`). - `apps/desktop/src/renderer/src/platform/` — the only place for react-router-dom navigation wiring. ### The No-Duplication Rule **If the same logic exists in both apps, it must be extracted to a shared package.** This applies to everything: components, hooks, guards, providers, utility functions. The decision process: 1. Does this code depend on Next.js or Electron APIs? → Keep in the respective app. 2. Does it depend on `react-router-dom` or `next/navigation`? → Keep in app's `platform/` layer. 3. Everything else → belongs in `packages/core/` (headless logic) or `packages/views/` (UI components). When the two apps need different behavior for the same concept (e.g., different loading UI), extract the shared logic into a component with props/slots for the differences. Don't duplicate the logic. ### Cross-Platform Development Rules When adding a new page or feature: 1. **New page component** → add to `packages/views//`. Never import from `next/*` or `react-router-dom`. 2. **Wire it in both apps** → add a route in `apps/web/app/` (Next.js page file) AND in the desktop router. **Exception**: pre-workspace transition flows (create workspace, accept invite) are NOT routes on desktop — they're `WindowOverlay` state. See *Desktop-specific Rules → Route categories*. 3. **Navigation** → use `useNavigation().push()` or ``. Never use framework-specific link/router APIs in shared code. 4. **Shared guards/providers** → use `DashboardGuard` from `packages/views/layout/`. Don't create separate guard logic per app. 5. **Platform-specific UI** → if a feature is web-only or desktop-only, keep it in the respective app. Use props slots (`extra`, `topSlot`) on shared layout components to inject platform-specific UI. 6. **New hooks that need workspace context** → accept `wsId` as parameter instead of reading from `useWorkspaceId()` Context, so they work both inside and outside `WorkspaceIdProvider`. ### CSS Architecture Both apps share the same CSS foundation from `packages/ui/styles/`. - **Design tokens** → use semantic tokens (`bg-background`, `text-muted-foreground`). Never use hardcoded Tailwind colors (`text-red-500`, `bg-gray-100`). - **Shared styles** → `packages/ui/styles/`. Never duplicate scrollbar styling, keyframes, or base layer rules in app CSS. - **`@source` directives** → both apps scan shared packages so Tailwind sees all class names. ## Desktop-specific Rules These rules apply to `apps/desktop/` only. Web has different constraints (URL bar, SSR, no tabs) and doesn't share these concerns. Every rule in this section was added after a concrete bug — treat them as enforced, not suggestions. ### Route categories Every path in the desktop app falls into exactly one category. Choosing the wrong one reproduces bugs we've already fixed. - **Session routes** — workspace-scoped pages (`/:slug/issues`, `/:slug/settings`). Rendered by the per-tab memory router under `WorkspaceRouteLayout`. These are legitimate tab destinations. - **Transition flows** — pre-workspace / one-shot actions (create workspace, accept invite). **NOT routes.** They live as `WindowOverlay` state, dispatched when the navigation adapter sees `push('/workspaces/new')` or `push('/invite/')`. The shared view (`NewWorkspacePage`, `InvitePage`) is the content; the overlay wrapper supplies platform chrome. - **Error / stale states** — "workspace not available", tabs pointing at a revoked workspace. **NOT pages.** `WorkspaceRouteLayout` auto-heals by dropping the stale tab group from the store; the user never lands on an explicit error screen. Web keeps `NoAccessPage` (shareable URL makes the error state meaningful); desktop has no URL bar so stale = heal silently. **Adding a new pre-workspace flow on desktop**: register a new `WindowOverlay` type in `stores/window-overlay-store.ts`. Do NOT add it to `routes.tsx`. If a shared view needs the flow on both platforms, add the route on web (`apps/web/app/(auth)/...`) AND the overlay type on desktop — the shared view component is identical. ### Workspace identity singleton `setCurrentWorkspace(slug, uuid)` in `@multica/core/platform` is the single source of truth for "which workspace is active right now". Three consumers depend on it: 1. API client's `X-Workspace-Slug` header. 2. Zustand per-workspace storage namespace. 3. Chrome gating (`{slug && }` on desktop, similar on web). Normally set by `WorkspaceRouteLayout` when its route mounts. Critically: **unmount does NOT clear it.** Any code that leaves workspace context (leave workspace, delete workspace, force navigation to overlay) must call `setCurrentWorkspace(null, null)` explicitly — otherwise the realtime `workspace:deleted` handler races the mutation, chrome gating stays truthy while the workspace is gone from cache, and `useWorkspaceId` throws. ### Workspace destructive operations Leave / Delete workspace flows must follow this order: 1. Read destination from cached workspace list (no extra fetch). 2. `setCurrentWorkspace(null, null)`. 3. `navigation.push(destination)` — switch to next workspace or open new-workspace overlay. 4. THEN `await mutation.mutateAsync(workspaceId)`. Reversing step 4 with steps 1–3 (mutate first, navigate after) causes a three-way race between the mutation's `onSettled` invalidate, the explicit `navigateAway`, and the realtime handler's `relocateAfterWorkspaceLoss` — all refetching the same `workspaces` query concurrently. One gets cancelled, bubbles as `CancelledError`, and triggers `window.location.assign` → full renderer reload / white screen. ### Tab isolation Tabs are grouped per workspace in `stores/tab-store.ts`. The TabBar shows only the active workspace's tabs; cross-workspace tab leakage is impossible by construction (no flat global tabs array). Cross-workspace `push(path)` is detected by the navigation adapter (`platform/navigation.tsx`) and translated into `switchWorkspace(slug, targetPath)` — NOT a navigation within the current tab's router. Don't bypass the adapter; always go through `useNavigation()` from shared code. ### Drag region (macOS window-move) Every full-window desktop view (login, overlay, any page that covers the native title bar) needs a top drag strip so users can move the window. On macOS the traffic lights are hidden via `useImmersiveMode` in overlay-style contexts, so the drag strip also gives back that corner for pointer-drag. **Pattern**: flex child at top, not absolute overlay. ```tsx
{/* page content — interactive elements need their own "no-drag" */}
``` Why flex, not absolute: the absolute-strip + `z-index` approach relies on stacking-context hit-testing, which isn't reliable for `-webkit-app-region`. A real flex row with no siblings at that pixel is unambiguous. Height matches `MainTopBar` (48px / `h-12`) for consistency. Canonical examples: `components/window-overlay.tsx`, `pages/login.tsx`. ### UX vs platform chrome UX affordances (Back button, Log out button, welcome copy, invite card) belong in `packages/views/` so web and desktop render identical content. Platform chrome (drag strip, `useImmersiveMode`, tab system interaction, traffic-light accommodation) lives in desktop-only code. Violating this split always produces platform divergence — if a button exists on desktop but not on web for the same flow, it's a signal the UX escaped into platform code. ## UI/UX Rules - Prefer shadcn components over custom implementations. Install via `pnpm ui:add ` from project root — adds to `packages/ui/components/ui/`. All components use Base UI primitives (`@base-ui/react`), not Radix. - Use shadcn design tokens for styling. Avoid hardcoded color values. - Do not introduce extra state (useState, context, reducers) unless explicitly required by the design. - Pay close attention to **overflow** (truncate long text, scrollable containers), **alignment**, and **spacing** consistency. - **If a component is identical between web and desktop, it belongs in a shared package.** Do not copy-paste between apps. ## Testing Rules ### Where to write tests Tests follow the code, not the app. This is the most important testing principle in this monorepo: | What you're testing | Where the test lives | Why | |---|---|---| | Shared business logic (stores, queries, hooks) | `packages/core/*.test.ts` | No DOM needed, pure logic | | Shared UI components (pages, forms, modals) | `packages/views/*.test.tsx` | jsdom, no framework mocks | | Platform-specific wiring (cookies, redirects, searchParams) | `apps/web/*.test.tsx` or `apps/desktop/` | Needs framework-specific mocks | | End-to-end user flows | `e2e/*.spec.ts` | Real browser, real backend | **Never test shared component behavior in an app's test file.** If a test requires mocking `next/navigation` or `react-router-dom` to test a component from `@multica/views`, the test is in the wrong place — move it to `packages/views/` and mock `@multica/core` instead. ### Test infrastructure - `packages/core/` — Vitest, Node environment (no DOM) - `packages/views/` — Vitest, jsdom environment, `@testing-library/react` - `apps/web/` — Vitest, jsdom environment, framework-specific mocks - `e2e/` — Playwright - `server/` — Go standard `go test` All test deps are in the pnpm catalog for unified versioning. ### Mocking conventions - Mock `@multica/core` stores with `vi.hoisted()` + `Object.assign(selectorFn, { getState })` pattern (Zustand stores are both callable and have `.getState()`). - Mock `@multica/core/api` for API calls. - In `packages/views/` tests: never mock `next/*` or `react-router-dom` — those don't exist here. - In `apps/web/` tests: mock framework-specific APIs only for platform-specific behavior. ### TDD workflow 1. Write failing test in the **correct package** first. 2. Write implementation. 3. Run `pnpm test` (Turborepo discovers all packages). 4. Green → done. ### Go tests Standard `go test`. Tests should create their own fixture data in a test database. ### E2E tests E2E tests should be self-contained. Use the `TestApiClient` fixture for data setup/teardown: ```typescript import { loginAsDefault, createTestApi } from "./helpers"; import type { TestApiClient } from "./fixtures"; let api: TestApiClient; test.beforeEach(async ({ page }) => { api = await createTestApi(); await loginAsDefault(page); }); test.afterEach(async () => { await api.cleanup(); }); test("example", async ({ page }) => { const issue = await api.createIssue("Test Issue"); await page.goto(`/issues/${issue.id}`); }); ``` ## Commit Rules - Use atomic commits grouped by logical intent. - Conventional format: `feat(scope)`, `fix(scope)`, `refactor(scope)`, `docs`, `test(scope)`, `chore(scope)`. ## Minimum Pre-Push Checks ```bash make check # Runs all checks: typecheck, unit tests, Go tests, E2E ``` Run verification only when the user explicitly asks for it. For targeted checks when requested: ```bash pnpm typecheck # TypeScript type errors only pnpm test # TS unit tests only (Vitest, all packages) make test # Go tests only pnpm exec playwright test # E2E only (requires backend + frontend running) ``` ## AI Agent Verification Loop After writing or modifying code, always run the full verification pipeline: ```bash make check ``` **Workflow:** - Write code to satisfy the requirement - Run `make check` - If any step fails, read the error output, fix the code, and re-run - 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. ## CLI Release **Prerequisite:** A CLI release must accompany every Production deployment. 1. Create a tag on the `main` branch: `git tag v0.x.x` 2. Push the tag: `git push origin v0.x.x` 3. GitHub Actions automatically triggers `release.yml`: runs Go tests → GoReleaser builds multi-platform binaries → publishes to GitHub Releases + Homebrew tap By default, bump the patch version each release (e.g. `v0.1.12` → `v0.1.13`), unless the user specifies a specific version. ## 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). ## README.md

Multica — humans and agents, side by side

Multica # Multica **Your next 10 hires won't be human.** The open-source managed agents platform.
Turn coding agents into real teammates — assign tasks, track progress, compound skills. [![CI](https://github.com/multica-ai/multica/actions/workflows/ci.yml/badge.svg)](https://github.com/multica-ai/multica/actions/workflows/ci.yml) [![GitHub stars](https://img.shields.io/github/stars/multica-ai/multica?style=flat)](https://github.com/multica-ai/multica/stargazers) [Website](https://multica.ai) · [Cloud](https://multica.ai/app) · [X](https://x.com/MulticaAI) · [Self-Hosting](SELF_HOSTING.md) · [Contributing](CONTRIBUTING.md) **English | [简体中文](README.zh-CN.md)**
## What is Multica? Multica turns coding agents into real teammates. Assign issues to an agent like you'd assign to a colleague — they'll pick up the work, write code, report blockers, and update statuses autonomously. No more copy-pasting prompts. No more babysitting runs. Your agents show up on the board, participate in conversations, and compound reusable skills over time. Think of it as open-source infrastructure for managed agents — vendor-neutral, self-hosted, and designed for human + AI teams. Works with **Claude Code**, **Codex**, **OpenClaw**, **OpenCode**, **Hermes**, **Gemini**, **Pi**, and **Cursor Agent**.

Multica board view

## Features Multica manages the full agent lifecycle: from task assignment to execution monitoring to skill reuse. - **Agents as Teammates** — assign to an agent like you'd assign to a colleague. They have profiles, show up on the board, post comments, create issues, and report blockers proactively. - **Autonomous Execution** — set it and forget it. Full task lifecycle management (enqueue, claim, start, complete/fail) with real-time progress streaming via WebSocket. - **Reusable Skills** — every solution becomes a reusable skill for the whole team. Deployments, migrations, code reviews — skills compound your team's capabilities over time. - **Unified Runtimes** — one dashboard for all your compute. Local daemons and cloud runtimes, auto-detection of available CLIs, real-time monitoring. - **Multi-Workspace** — organize work across teams with workspace-level isolation. Each workspace has its own agents, issues, and settings. --- ## Quick Install ### macOS / Linux (Homebrew - recommended) ```bash brew install multica-ai/tap/multica ``` Use `brew upgrade multica-ai/tap/multica` to keep the CLI current. ### macOS / Linux (install script) ```bash curl -fsSL https://raw.githubusercontent.com/multica-ai/multica/main/scripts/install.sh | bash ``` Use this if Homebrew is not available. The script installs the Multica CLI on macOS and Linux by using Homebrew when it is on `PATH`, otherwise it downloads the binary directly. ### Windows (PowerShell) ```powershell irm https://raw.githubusercontent.com/multica-ai/multica/main/scripts/install.ps1 | iex ``` Then configure, authenticate, and start the daemon in one command: ```bash multica setup # Connect to Multica Cloud, log in, start daemon ``` > **Self-hosting?** Add `--with-server` to deploy a full Multica server on your machine: > > ```bash > curl -fsSL https://raw.githubusercontent.com/multica-ai/multica/main/scripts/install.sh | bash -s -- --with-server > multica setup self-host > ``` > > Requires Docker. See the [Self-Hosting Guide](SELF_HOSTING.md) for details. --- ## Getting Started ### 1. Set up and start the daemon ```bash multica setup # Configure, authenticate, and start the daemon ``` The daemon runs in the background and auto-detects agent CLIs (`claude`, `codex`, `openclaw`, `opencode`, `hermes`, `gemini`, `pi`, `cursor-agent`) on your PATH. ### 2. Verify your runtime Open your workspace in the Multica web app. Navigate to **Settings → Runtimes** — you should see your machine listed as an active **Runtime**. > **What is a Runtime?** A Runtime is a compute environment that can execute agent tasks. It can be your local machine (via the daemon) or a cloud instance. Each runtime reports which agent CLIs are available, so Multica knows where to route work. ### 3. Create an agent Go to **Settings → Agents** and click **New Agent**. Pick the runtime you just connected and choose a provider (Claude Code, Codex, OpenClaw, OpenCode, Hermes, Gemini, Pi, or Cursor Agent). Give your agent a name — this is how it will appear on the board, in comments, and in assignments. ### 4. Assign your first task Create an issue from the board (or via `multica issue create`), then assign it to your new agent. The agent will automatically pick up the task, execute it on your runtime, and report progress — just like a human teammate. --- ## Multica vs Paperclip | | Multica | Paperclip | |---|---------|-----------| | **Focus** | Team AI agent collaboration platform | Solo AI agent company simulator | | **User model** | Multi-user teams with roles & permissions | Single board operator | | **Agent interaction** | Issues + Chat conversations | Issues + Heartbeat | | **Deployment** | Cloud-first | Local-first | | **Management depth** | Lightweight (Issues / Projects / Labels) | Heavy governance (Org chart / Approvals / Budgets) | | **Extensibility** | Skills system | Skills + Plugin system | **TL;DR — Multica is built for teams that want to collaborate with AI agents on real projects together.** --- ## CLI The `multica` CLI connects your local machine to Multica — authenticate, manage workspaces, and run the agent daemon. | Command | Description | |---------|-------------| | `multica login` | Authenticate (opens browser) | | `multica daemon start` | Start the local agent runtime | | `multica daemon status` | Check daemon status | | `multica setup` | One-command setup for Multica Cloud (configure + login + start daemon) | | `multica setup self-host` | Same, but for self-hosted deployments | | `multica issue list` | List issues in your workspace | | `multica issue create` | Create a new issue | | `multica update` | Update to the latest version | See the [CLI and Daemon Guide](CLI_AND_DAEMON.md) for the full command reference. --- ## Architecture ``` ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ Next.js │────>│ Go Backend │────>│ PostgreSQL │ │ Frontend │<────│ (Chi + WS) │<────│ (pgvector) │ └──────────────┘ └──────┬───────┘ └──────────────────┘ │ ┌──────┴───────┐ │ Agent Daemon │ runs on your machine └──────────────┘ (Claude Code, Codex, OpenCode, OpenClaw, Hermes, Gemini, Pi, Cursor Agent) ``` | Layer | Stack | |-------|-------| | Frontend | Next.js 16 (App Router) | | Backend | Go (Chi router, sqlc, gorilla/websocket) | | Database | PostgreSQL 17 with pgvector | | Agent Runtime | Local daemon executing Claude Code, Codex, OpenClaw, OpenCode, Hermes, Gemini, Pi, or Cursor Agent | ## Development For contributors working on the Multica codebase, see the [Contributing Guide](CONTRIBUTING.md). **Prerequisites:** [Node.js](https://nodejs.org/) v20+, [pnpm](https://pnpm.io/) v10.28+, [Go](https://go.dev/) v1.26+, [Docker](https://www.docker.com/) ```bash make dev ``` `make dev` auto-detects your environment (main checkout or worktree), creates the env file, installs dependencies, sets up the database, runs migrations, and starts all services. See [CONTRIBUTING.md](CONTRIBUTING.md) for the full development workflow, worktree support, testing, and troubleshooting. ## Star History Star History Chart