Thanks for your interest in Claude Comms. This guide covers everything you need to get a development environment running, understand the codebase, and submit changes.
- Python 3.10+
- Node.js 18+ (for the web UI)
- Git
git clone https://github.com/Aztec03hub/claude-comms.git
cd claude-comms
# Python dependencies (editable install with dev extras)
pip install -e ".[all,dev]"
# Web UI dependencies
cd web
npm install
cd ..# Python tests (746+ tests, runs in < 1s)
pytest
# Web UI dev server
cd web && npx vite --port 5173
# Playwright end-to-end tests (requires dev server running)
cd web && npx playwright testclaude-comms/
├── src/claude_comms/ # Python package
│ ├── broker.py # Embedded amqtt MQTT broker
│ ├── cli.py # Typer CLI application
│ ├── config.py # Config loading (YAML)
│ ├── mcp_server.py # MCP HTTP server
│ ├── mcp_tools.py # 9 comms_* tool implementations
│ ├── message.py # Message model (Pydantic)
│ ├── mention.py # @mention parsing and routing
│ ├── participant.py # Presence tracking
│ ├── log_exporter.py # .log + .jsonl file writer
│ ├── hook_installer.py # PostToolUse hook setup
│ ├── notification_hook.sh # Shell hook script
│ └── tui/ # Textual terminal UI
├── tests/ # Python test suite (pytest)
├── web/ # Svelte 5 web client
│ ├── src/
│ │ ├── App.svelte # Root app component
│ │ ├── components/ # 30+ Svelte components
│ │ └── lib/ # Stores, utilities
│ │ ├── mqtt-store-v2.svelte.js # MQTT state (Svelte 5 runes)
│ │ ├── notifications.svelte.js # Notification store
│ │ └── utils.js # Shared helpers
│ ├── e2e/ # Playwright test suites (30 files)
│ ├── vite.config.js
│ └── playwright.config.js
├── pyproject.toml # Python build config (hatchling)
├── Dockerfile
├── docker-compose.yml
└── README.md
- Branch from
mainwith a descriptive name (fix/phantom-participants,feat/message-search). - Code your changes, following the style guidelines below.
- Test -- run both Python and Playwright tests before committing.
- Commit with a concise message describing the why, not the what.
- Push and open a pull request against
main.
- Linting and formatting: ruff for both lint and format.
- Type checking: pyright for static type analysis.
- Models: Pydantic v2 for data models (
message.py,participant.py,config.py). - Async: Use
asynciothroughout the broker, MCP server, and log exporter.
ruff check src/ tests/
ruff format src/ tests/- Svelte 5 runes -- use
$state,$derived,$effect, not legacy stores. Stores use the.svelte.jsextension. - Overlays (modals, context menus, popovers) -- use bits-ui components.
- Icons -- use Lucide icons via
lucide-svelte. Do not use inline SVGs. - JSDoc -- add JSDoc comments on all components describing their purpose, props, and events.
- CSS -- Tailwind utility classes. The design system is "Carbon Ember" (dark backgrounds, warm ember/amber accents).
The test suite has 746+ tests covering the broker, CLI, config, MCP tools, messages, mentions, participants, log exporter, hook installer, TUI, and API endpoints.
# Run all tests
pytest
# Run a specific test file
pytest tests/test_mcp_tools.py
# Run with verbose output
pytest -vTests use pytest-asyncio for async test cases. Always run the full suite before committing.
End-to-end tests live in web/e2e/ and cover sidebar interactions, chat, panels, modals, context menus, emoji picker, keyboard navigation, theming, responsive layouts, and more.
cd web
# Run all Playwright tests
npx playwright test
# Run a specific suite
npx playwright test e2e/chat.spec.js
# Run with headed browser for debugging
npx playwright test --headedSee docs/dev/testing-context.md for the full data-testid inventory, test template, and known infrastructure issues.
CI enforces these gzipped ceilings on web bundle chunks. The check lives at web/scripts/check-bundle-size.mjs and runs after every npm run build:check:
| Chunk | Ceiling (gzipped) |
|---|---|
vendor-markdown |
130 KB |
vendor-diff |
25 KB |
index |
180 KB |
Run locally:
cd web
npm run build:check # build + bundle-size check
# or separately
npm run build
npm run check:bundle-sizeA failing check exits non-zero and prints which chunk(s) exceeded.
If vendor-markdown exceeds its ceiling, apply this ladder (documented in the plan at §"Version pinning & CI size check"):
- Drop eager-loaded Shiki langs one at a time, starting with the least-used. Order:
python, thenjson, thenbash. Move the dropped lang to a dynamicimport()inside themarkedHighlight()highlightcallback insrc/lib/markdown.js, so it only loads when a code fence with that language is actually encountered. - Drop
typescriptorjavascriptif truly necessary. Chat UX would suffer; consider this a last resort before (3). - Swap Shiki for
highlight.jscommon bundle (~40 KB gzipped) and accept reduced theme fidelity. Set the runtime fallback flagweb.use_legacy_codeblock_highlighter: truein config so operators can opt in without redeploying.
Raising a ceiling requires explicit review. Document the rationale in the PR description and link to the measurement output.
If the Shiki pipeline breaks in production, getHighlighter() in src/lib/markdown.js already falls back to escaped plain text automatically, with no outage, just no colors. The web.use_legacy_codeblock_highlighter flag exists as a harder revert path. For markdown rendering problems that DOMPurify config changes cannot resolve, the escape hatch web.markdown_render_enabled: false causes renderMarkdown() to return escaped plain text (users see raw markdown source).
CI gate override: in rare hotfix cases where the ship-critical work is orthogonal to bundle size, the check can be overridden by including an allow-oversized-bundle git-commit-trailer on the merge commit. Use sparingly and document why.
Every interactive element in the web UI must have a data-testid attribute. This is how Playwright tests locate elements without coupling to CSS classes or DOM structure.
- Static IDs:
data-testid="message-input",data-testid="send-button" - Dynamic IDs:
data-testid="channel-item-{channel.id}",data-testid="message-{message.id}"
See docs/dev/testing-context.md section 2 for the complete inventory.
Use bits-ui for all overlay UI:
- Modals --
ChannelModal.svelte,ConfirmDialog.svelte - Context menus --
ContextMenu.svelte - Popovers --
EmojiPicker.svelte,MentionDropdown.svelte
Use Lucide icons exclusively (lucide-svelte). Do not add inline <svg> elements.
The design follows the Carbon Ember palette:
- Dark backgrounds with warm ember/amber accents
- Light theme variant available via
ThemeToggle.svelte - All theme colors defined in Tailwind config
The mqtt.js library's WebSocket reconnection cycle (every ~3 seconds) blocks the browser event loop, causing Playwright's page.click(), page.fill(), and page.evaluate() to hang indefinitely. Always use the WebSocket mock in test setup via page.addInitScript() before page.goto(). See docs/dev/testing-context.md Issue A for the full mock implementation.
The ConnectionStatus component has a $effect that reads and writes the same reactive state. Without untrack() on the self-referencing read, this creates an infinite reactivity loop. If you modify ConnectionStatus.svelte, verify that untrack() wraps any state reads that the same effect also writes to.
When MQTT message callbacks update Svelte state, the DOM may not reflect changes immediately because MQTT callbacks run outside Svelte's reactivity microtask. If you see stale DOM in tests after an MQTT-driven state change, wrap the state update in flushSync() from svelte to force synchronous DOM reconciliation.
Vite dev server under WSL2 can have intermittent slow page loads (10-40 seconds). Playwright tests use extended timeouts (60 seconds per test) and waitUntil: 'domcontentloaded' to mitigate this. See docs/dev/testing-context.md Issue B.
Multiple dev servers or concurrent test agents must use separate ports:
5173-- Vite default (manual dev)5175-- Playwright config default6001-6010-- Agent-assigned (one per concurrent agent)
Claude Comms is MIT licensed. By contributing, you agree that your contributions will be licensed under the same terms.