[build-system] requires = ["hatchling>=1.27"] build-backend = "hatchling.build" [project] name = "clickcast" version = "0.5.0" description = "Playwright browser automation for AI agents — screenshots, GIF/MP4/WebP recording, JSON feedback sidecar, MCP server." readme = "README.md" license = "MIT" license-files = ["LICENSE"] requires-python = ">=3.10" authors = [{ name = "Alex Kay" }] keywords = [ "playwright", "screenshot", "gif", "browser", "automation", "ai", "agent", "mcp", "model-context-protocol", "e2e", "visual-regression", "demo-video", "web-ui", ] classifiers = [ "Development Status :: 3 - Alpha", "Environment :: Console", "Environment :: Web Environment", "Intended Audience :: Developers", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Topic :: Internet :: WWW/HTTP :: Browsers", "Topic :: Software Development :: Quality Assurance", "Topic :: Software Development :: Testing", "Topic :: Multimedia :: Graphics", "Typing :: Typed", ] dependencies = [ "playwright>=1.44", "Pillow>=10.0", "imageio[ffmpeg]>=2.34", "PyYAML>=6.0", "typer>=0.12", "rich>=13.7", "pydantic>=2.7", "pydantic-settings>=2.3", "platformdirs>=4.2", "tomli>=2.0; python_version < '3.11'", "tomlkit>=0.12", ] [project.optional-dependencies] # `test` is the minimal set every test-matrix job needs. Kept lean so # CI test shards don't pay for mypy / pre-commit / build on every run. test = [ "pytest>=8.0", "pytest-asyncio>=0.23", "pytest-cov>=5.0", ] # `dev` is a superset: `test` plus lint, type-check, hooks, and local # build tooling. Contributors doing pytest work can install `.[test]`; # `.[dev]` remains the one-stop install for maintainers. dev = [ "clickcast[test]", "clickcast[mcp]", "ruff>=0.5", "mypy>=1.10", "pre-commit>=3.7", "types-PyYAML", "build>=1.0", ] # `mcp` — the official Model Context Protocol Python SDK, only needed for # `clickcast mcp` (#191). Kept as its own extra so `pip install clickcast` # doesn't grow for users who never touch MCP. Pinned below `2.0` — the `mcp` # package's 2.x line renamed `FastMCP` to `MCPServer` with a breaking API # (see the package's own ModuleNotFoundError message); `src/clickcast/mcp/` # targets the widely-deployed, well-documented 1.x `FastMCP` API that every # current MCP client config (Claude Code / Claude Desktop) is written against. mcp = [ "mcp>=1.9.0,<2", ] # `publish` is release-only: what the release workflow needs to build # and upload a wheel, without pulling in test or lint tooling. publish = [ "build>=1.0", "twine>=5", ] [project.scripts] clickcast = "clickcast.cli:main" [project.urls] Homepage = "https://github.com/AlexKay28/clickcast" Documentation = "https://github.com/AlexKay28/clickcast/tree/main/docs" Source = "https://github.com/AlexKay28/clickcast" Changelog = "https://github.com/AlexKay28/clickcast/blob/main/CHANGELOG.md" Issues = "https://github.com/AlexKay28/clickcast/issues" [tool.hatch.build.targets.wheel] packages = ["src/clickcast"] [tool.hatch.build.targets.sdist] include = ["src/clickcast", "README.md", "LICENSE", "pyproject.toml"] [tool.ruff] line-length = 100 target-version = "py310" src = ["src", "tests"] [tool.ruff.lint] select = [ "E", "F", "W", # pycodestyle + pyflakes "I", # isort "B", # bugbear "UP", # pyupgrade "SIM", # simplifications "RUF", # ruff-specific ] ignore = ["E501"] # line-length handled by formatter [tool.ruff.lint.flake8-bugbear] # Typer's public API idiom is `arg: T = typer.Argument(...)` / `typer.Option(...)`. extend-immutable-calls = ["typer.Argument", "typer.Option"] [tool.ruff.format] quote-style = "double" [tool.mypy] # python_version is set to 3.12 (not our min-supported 3.10) because numpy's # type stubs use PEP 695 `type` statements, which are 3.12+ syntax. Runtime # 3.10 compatibility is enforced by the CI matrix, not mypy. python_version = "3.12" strict = true files = ["src/clickcast"] warn_unused_ignores = true warn_redundant_casts = true [[tool.mypy.overrides]] module = ["imageio.*", "imageio_ffmpeg", "PIL.*"] ignore_missing_imports = true [tool.pytest.ini_options] minversion = "8.0" addopts = "-ra --strict-markers" testpaths = ["tests"] markers = [ "unit: fast tests with no browser", "integration: tests that launch a browser against the local fixture site", "slow: encoding + full-tour reels", ] asyncio_mode = "auto" [tool.coverage.run] source = ["src/clickcast"] branch = true [tool.coverage.report] show_missing = true skip_covered = true