This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is a monorepo containing multiple projects in the Browser Use ecosystem - an AI browser automation framework. The main components are:
Core project folders:
- browser-use: Core Python library for browser automation with LLM agents
- cloud: Full-stack cloud platform (FastAPI backend + Next.js frontend, uses the browser-use library to run AI web agents for users on our servers)
Less important components:
- bubus: Generic mini event bus library for async event dispatching and handling in python, provides pydantic-based
Event
model andEventBus
service used bycloud
andbrowser-use
- web-ui: Gradio-based web interface for
browser-use
library (less important, ignore this code unless explicitly directed to work on it) - workflow-use: Deterministic workflow recording and execution system built as a Chrome extension (less important, ignore this code unless explicitly directed to work on it)
All Python projects use uv
as the package manager:
# Install dependencies
uv sync --dev --all-extras
# Run tests
pytest tests/ # All tests
pytest tests/ci/test_specific.py # Single test file
pytest tests/ci/test_specific.py::test_function # Single test function
# Run linter
uv run pre-commit run --all-files # Run all linters and formatters (ruff)
# cloud/frontend (uses yarn)
yarn dev # Development server
yarn build # Production build
yarn lint # Run linter
yarn type-gen-update # Update TypeScript types from OpenAPI
# workflow-use/ui (uses npm)
npm run dev # Development server
npm run build # Production build
npm run lint # Run linter
npm run type-gen-update # Update types from OpenAPI
- Use async/await patterns throughout, make sure all code is threadsafe and async-optimized
- Use tabs only for indentation in the
browser-use
library andcloud
codebases (not spaces), use spaces in thebubus
project, match the existing style anywhere else - Modern Python 3.12+ typing style e.g. use
str | int
instead ofUnion[str, int]
,dict[str, str]
instead ofDict[str, str]
,list[str]
instead ofList[str]
- Use Pydantic v2 models with strict validation, use
ConfigDict
,model_validator()
, andAnnotated[xyz, AfterValidator(validation_func)]
types to implement as much logic as possible with pydantic features instead of writing helper methods - Keep all logging related code in separate
_log_...()
private methods and functions - Most big subcomponets are each implemented in a
service.py
file, data models and types can usually be found inviews.py
ormodels.py
files - Use runtime assertions for critical invariants and to enforce types, don't rely on unit tests alone
- Prioritize simplicity, elegance/intuitiveness, and correctness for the
browser-use
library over backwards compatibility and performance - When renaming pydantic fields, use
new_field_name = Field(validation_alias=AliasChoices('old_field_name'))
to maintain backwards compatibility with the old field name instead of copying values over manually - Use the
bubus
event bus for any inter-component communication involving writes to shared state or side effects, don't use events if you only need to read a value or mutate a component's own private state
- Write failing tests first before implementing new features
- Use real objects instead of mocks for everything aside from the llm, the LLM can be mocked using helpers in browser-use/tests/ci/mocks.py
- Use pytest-httpserver via pytest fixtures to serve html for tests, never use live URLs in tests
- No need to mark async tests with
@pytest.mark.asyncio
or define fixture scopes manually, pytest is already configured inpyproject.toml
to be in auto async mode, which supports async tests without any mark needed - Try to put all tests related to a specific component in a single test file (look in
tests/ci/test_*.py
), don't create a separate file for every test - Always run tests with a timeout to prevent tests from hanging indefinitely in case of deadlocks
- When finished with a task, do a final pass over the tests and remove or consolidate any duplicated test logic and clean up any dead code
- Check existing
tests/
,examples/
, anddocs/
to understand behavior - If making big changes, create a proposal for a few possible ways to implement it and compare the pros and cons of each before diving in to make changes
- Write failing test for new functionality
- Implement minimal code to pass test
- Run full test suite to make sure nothing else was broken, iterate until all tests are passing
- Don't ignore warnings or skip any tests, everything must be correct and pass, if something is too hard to test easily you can change the implementation to make it easier to test
- Update any relevant
docs/
andexamples/
to cover the new behavior
- Each sub-project is its own git repository, always cd into the relevant sub-project before doing any git operations
- Each sub-project has its own dependencies and build process
- Changing
browser-use
code may affectcloud
code - Changing
bubus
code may affectbrowser-use
andcloud
- Run tests in affected packages when making cross-package changes
- Don't bother updating
web-ui
andworkflow-use
after makingcloud
orbrowser-use
library changes unless explicitly directed to, you can grep for areas that might need to be changed and inform the user about them at the end of a finished task though
- Backend uses FastAPI with SQLAlchemy
- Auto-reload with:
uvicorn main:app --reload
- OpenAPI schemas are auto-generated and consumed by frontends
- Next.js 15 with React 19 for cloud frontend
- Vite + React for workflow-use UI
- TypeScript types generated from backend OpenAPI schemas
- Use Tailwind CSS for styling