Fixture: a TSX mod whose render handler uses JSX.

<img src="doc_en/banner.svg" alt="OpenSquad" width="720" />
<strong>English</strong> | <a href="README_ZH.md">中文</a>
OpenSquad is a local-first multi-agent collaboration framework. Multiple autonomous agents (PM, Coder, QA, etc.) communicate via group chat to coordinate and complete complex tasks.
<img src="doc_en/screenshots/agent_web.png" alt="OpenSquad single-agent web workspace" width="900" />
<img src="doc_shared/screenshots/chat_group.png" alt="OpenSquad group chat" width="900" />
OpenSquad enables multiple AI agents to collaborate like a real team. Each agent runs as an independent process with its own LLM connection, tool set, and memory. Agents communicate through group chat, coordinated by a PM agent, to tackle complex tasks that a single agent cannot handle alone.
A single AI agent has clear limitations when dealing with complex projects:
OpenSquad solves these through multi-agent collaboration: PM handles task decomposition and coordination, Coder focuses on implementation, QA independently verifies — each with a clear responsibility.
flowchart TB
User["User / Web UI"] --> GW["Gateway :9555"]
GW --> LA["Launcher :9600"]
LA --> PM["PM Agent"]
LA --> Dev["Coder Agent"]
LA --> QA["QA Agent"]
PM & Dev & QA --> IM["Group chat / IM"]
PM & Dev & QA --> Plugins["Plugins / MCP / Skills"]
Plugins --> Svc["Service plugins e.g. websearch :9001"]
Details: Architecture · Documentation hub
uv sync (or install.bat / install.sh).api_key in src/model_cards/ (see model cards guide).uv run opensquad init (creates ~/.opensquad/workspace by default).uv run opensquad start.http://127.0.0.1:5173 (or Gateway port from system_config).module.register, and transpiles their .ts / .tsx with a vendored sucrase — so no Node-version-dependent type stripping)Windows
git clone https://github.com/opensquad-ai/opensquad.git && cd opensquad && install.bat
Linux / macOS
git clone https://github.com/opensquad-ai/opensquad.git && cd opensquad && bash install.sh
The script automatically: checks prerequisites → installs dependencies → initializes workspace → starts all services.
Note: After first start, add your LLM API key to a model card under
src/model_cards/.
uv is a fast Python package manager. Recommended.
# Install uv (if not already)
pip install uv
# Clone the project
git clone https://github.com/opensquad-ai/opensquad.git
cd opensquad
# Install dependencies (uses uv.lock for reproducible builds)
uv sync
# Install frontend dependencies
cd src/opensquad/gateway/nexuschat-pro && npm install && cd ../../..
# Initialize and start
uv run opensquad init
uv run opensquad start
From PyPI — runtime, built web UI and the default resources:
pip install opensquad
opensquad init
opensquad start
Since 0.8.48 the wheel is a complete deployment. It ships the default
model_cards/,plugins/,agents/,pymcp/,collab_cards/,role_cards/andskills/next to theopensquadpackage, soopensquad initseeds a runnable workspace andopensquad startfinds every service. One gap remains: a fresh pip workspace has nosystem_config.json(the wheel carries no template), so ports stay on their defaults — gateway 9555, launcher 9600, registry 9720, frontend 5173 — until you save one from the Web UI's Settings.
From a checkout — full deployment, editable install:
git clone https://github.com/opensquad-ai/opensquad.git
cd opensquad
pip install -e .
# Install frontend dependencies
cd src/opensquad/gateway/nexuschat-pro && npm install && cd ../../..
# Initialize and start
opensquad init
opensquad start
The npm package is a thin bootstrap: on first run it installs the matching Python opensquad CLI, then forwards every command to it.
npm install -g opensquad-ai
opensquad init
opensquad start
Requires Node.js 18+ (Mods host: 20.6+) and Python 3.11+. npx opensquad-ai works too, with no global install.
git clone https://github.com/opensquad-ai/opensquad.git
cd opensquad
# Start (auto-builds image)
docker compose up -d
# View logs
docker compose logs -f
After start, visit http://localhost:9555. Data is persisted in Docker volumes.
Custom configuration:
# Edit config
cp src/system_config.example.json src/system_config.json
# Fill in your LLM API Key...
# Mount config and start
docker run -d \
-p 9555:9555 -p 9600:9600 -p 9720:9720 \
-v opensquad-data:/data \
-v ./src/system_config.json:/app/src/system_config.json:ro \
opensquad
Use the row that matches how you installed OpenSquad:
| Installed via | Update with | Notes |
|---|---|---|
| Desktop app (installer) | In-app Check for updates, or opensquad update | Fully automatic: it downloads the installer, installs it silently and relaunches the app. Builds older than v0.8.43 must download the installer once by hand — the relaunch-on-update logic shipped in that version. |
pip install opensquad | pip install --upgrade opensquad | |
npm install -g opensquad-ai | npm install -g opensquad-ai@latest | The wrapper re-installs the matching Python package when the versions differ. |
git clone (uv sync / pip install -e .) | git pull, then re-run the same install command | Re-run npm install under src/opensquad/gateway/nexuschat-pro if the frontend changed; npm run build there to serve the UI statically instead of through Vite. |
| Docker | git pull && docker compose up -d --build | The image is built from the local Dockerfile, not pulled from a registry. |
opensquad update only installs anything for the packaged desktop app. It cannot rewrite a pip / npm / source install, so on those it prints the command from the table and exits — without touching your environment or your git state.
opensquad start launches all 4 services:
| Service | Port | Description |
|---|---|---|
| Gateway Backend | 9555 | FastAPI backend (WebSocket + HTTP) |
| Plugin Registry | 9720 | Plugin store API |
| Frontend Dev | 5173 | Vite React frontend |
| Launcher | 9600 | Agent process manager |
Open http://127.0.0.1:5173 in your browser to create and configure agents via the Web UI.
| Command | Description |
|---|---|
opensquad init [--workspace <path>] | Initialize workspace (default: ~/.opensquad/workspace) |
opensquad start [--port <port>] | Start all services |
opensquad stop | Stop all OpenSquad services (frontend, gateway, launcher, adapters, agent processes) |
opensquad status | Show agent and service status |
opensquad plugin list | List installed plugins |
opensquad plugin install <id> | Install a plugin from the store or Git URL |
opensquad plugin uninstall <id> | Uninstall a plugin |
Run without installing:
python -m opensquad.cli start
Copy system_config.example.json to your workspace or src/system_config.json (see architecture-paths):
{
"hosts": { "gateway": "127.0.0.1" },
"ports": { "gateway": 9555, "launcher": 9600, "websearch": 9001 },
"auth": { "node_secret": "your-secret-here" }
}
Never commit real system_config.json, auth.json, or model cards with API keys.
LLM API keys live in model cards (src/model_cards/*.json). Agents are created via the Web UI at runtime.
Start here: Documentation hub → Getting started (EN)
| Document | Description |
|---|---|
| Architecture | System design and module map |
| Collaboration | Multi-agent workflows |
| Plugin ecosystem | Built-in plugins vs Registry |
| Contributing | How to contribute |
| Releasing | Maintainer release checklist |
Issues and Pull Requests are welcome! Please read the Contributing Guide and Code of Conduct first.
MIT License — see LICENSE for details.
Powered by OpenSquad Core
hooks/register.tsx 29 lines1// Fixture for P6-B2: JSX in a mod module.
2//
3// The element names are local consts from `$.ui.resolve(e)`, exactly like the
4// real `.tsx` mods — so classic JSX compiles to `React.createElement(Box, …)`
5// and the host only has to provide `React`.
6import { atom, read, update } from 'claude-code'
7import type { EngineInterface, Register } from 'claude-code'
8
9const hits = atom({ plugin: 'tsx-demo', key: 'hits' } as const, 0)
10
11export const register: Register = (on: any) => {
12 on('tool.call', { tool: 'demo.jsx' }, async ($: EngineInterface, e: any, next: any) => {
13 update(hits, (v: number) => (v ?? 0) + 1)
14 return { deny: `tsx-demo: jsx works, hits=${read(hits)}` }
15 })
16
17 on('ui.render', { component: 'AbovePrompt' }, ($: EngineInterface, e: any) => {
18 const { Box, Text } = $.ui.resolve(e)
19 const label: string = `tsx-demo hits=${read(hits) ?? 0}`
20 return (
21 <Box flexDirection="row" gap={2}>
22 <Text bold color="cyan">
23 {label}
24 </Text>
25 </Box>
26 )
27 })
28}
29