From e1ac49e266dba835e8306f0a57a8e31a29c9356d Mon Sep 17 00:00:00 2001 From: Varun Nuthalapati Date: Thu, 28 May 2026 23:22:09 -0700 Subject: [PATCH 1/3] feat: add list-configs subcommand to CLI New users have no way to discover which config files are available without browsing the source tree. Add `python -m webwright.run.cli list-configs` that prints all .yaml filenames from the built-in config directory. Update the README flags table to document the new command. --- README.md | 5 +++-- src/webwright/run/cli.py | 13 ++++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 891bbd7..0da4671 100644 --- a/README.md +++ b/README.md @@ -198,13 +198,14 @@ python -m webwright.run.cli \ ### 🚩 Flags -| Flag | Description | -|------|-------------| +| Flag / Command | Description | +|----------------|-------------| | `-c` | Config file(s) from `src/webwright/config/` (stackable). | | `-t` | Task instruction. | | `--start-url` | Initial page. | | `--task-id` | Output subfolder name. | | `-o` | Output directory. | +| `list-configs` | Print all available built-in config file names (`python -m webwright.run.cli list-configs`). | --- diff --git a/src/webwright/run/cli.py b/src/webwright/run/cli.py index 343ab70..061f589 100644 --- a/src/webwright/run/cli.py +++ b/src/webwright/run/cli.py @@ -8,7 +8,7 @@ from rich.console import Console from webwright.agents import get_agent -from webwright.config import get_config_from_spec, snapshot_config_specs +from webwright.config import builtin_config_dir, get_config_from_spec, snapshot_config_specs from webwright.environments import get_environment from webwright.models import get_model from webwright.utils.serialize import UNSET, recursive_merge @@ -134,6 +134,17 @@ def run_one( return result +@app.command("list-configs") +def list_configs() -> None: + """List all available built-in config files.""" + configs = sorted(builtin_config_dir.glob("*.yaml")) + if not configs: + console.print("No config files found.") + return + for path in configs: + console.print(path.name) + + @app.command() def main( task: str = typer.Option( From 89276cb2f943e567e321e21afeee689856d239ab Mon Sep 17 00:00:00 2001 From: Varun Nuthalapati Date: Tue, 2 Jun 2026 08:17:59 -0700 Subject: [PATCH 2/3] fix(cli): use callback pattern to avoid breaking no-subcommand invocation Registering a second @app.command() forced Typer to require an explicit subcommand, breaking the existing `python -m webwright.run.cli -t "..."` invocation form. Switch `main` to @app.callback(invoke_without_command=True) so the default invocation works without a subcommand while list-configs remains available as an explicit subcommand. Co-Authored-By: Claude Sonnet 4.6 --- src/webwright/run/cli.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/webwright/run/cli.py b/src/webwright/run/cli.py index 061f589..561c4c5 100644 --- a/src/webwright/run/cli.py +++ b/src/webwright/run/cli.py @@ -17,7 +17,7 @@ DEFAULT_CONFIGS = ["base.yaml", "model_openai.yaml"] -app = typer.Typer(no_args_is_help=True) +app = typer.Typer() console = Console(highlight=False) @@ -145,10 +145,11 @@ def list_configs() -> None: console.print(path.name) -@app.command() +@app.callback(invoke_without_command=True) def main( - task: str = typer.Option( - ..., "-t", "--task", help="Natural language task description." + ctx: typer.Context, + task: str | None = typer.Option( + None, "-t", "--task", help="Natural language task description." ), task_id: str | None = typer.Option( None, "--task-id", help="Optional identifier used in the output directory name." @@ -164,6 +165,11 @@ def main( help="Launch headed local Playwright with devtools and keep it open for inspection.", ), ) -> Any: + if ctx.invoked_subcommand is not None: + return + if task is None: + console.print(ctx.get_help()) + raise typer.Exit() return run_one( task=task, task_id=task_id, From b0c9617b847085a4688d3798da4298f4d637e3e8 Mon Sep 17 00:00:00 2001 From: Varun Nuthalapati Date: Thu, 4 Jun 2026 22:07:55 -0700 Subject: [PATCH 3/3] fix: use firefox in doctor screenshot check SKILL.md and local_browser.yaml specify Firefox as the required browser, but check_screenshot launched Chromium. Users who followed the quickstart and only installed Firefox got a false FAIL on the screenshot check. - p.chromium.launch() -> p.firefox.launch() - Renamed check_chromium to check_firefox; updated messages/hints to reference Firefox - Updated CHECKS registry entry label from Chromium to Firefox - Updated test_doctor.py to import and call check_firefox --- src/webwright/run/doctor.py | 14 +++++++------- tests/unit/test_doctor.py | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/webwright/run/doctor.py b/src/webwright/run/doctor.py index d64d5ed..20ed762 100644 --- a/src/webwright/run/doctor.py +++ b/src/webwright/run/doctor.py @@ -27,7 +27,7 @@ def check_playwright(): return False, ("playwright not installed\nFix: pip install playwright") -def check_chromium(): +def check_firefox(): try: result = subprocess.run( ["playwright", "install", "--dry-run"], @@ -36,9 +36,9 @@ def check_chromium(): ) if result.returncode == 0: - return True, "chromium available" + return True, "firefox available" - return False, ("chromium missing\nFix: playwright install chromium") + return False, ("firefox missing\nFix: playwright install firefox") except Exception as e: return False, str(e) @@ -51,7 +51,7 @@ def check_screenshot(): screenshot_path = Path("doctor_test.png") with sync_playwright() as p: - browser = p.chromium.launch(headless=True) + browser = p.firefox.launch(headless=True) page = browser.new_page() @@ -70,8 +70,8 @@ def check_screenshot(): except Exception: return False, ( - "unable to launch Chromium for screenshot validation\n" - "Fix: playwright install" + "unable to launch Firefox for screenshot validation\n" + "Fix: playwright install firefox" ) @@ -108,7 +108,7 @@ def check_plugin_manifests(): CHECKS = [ ("Python", check_python), ("Playwright", check_playwright), - ("Chromium", check_chromium), + ("Firefox", check_firefox), ("Screenshot", check_screenshot), ("OpenAI Key", check_openai_key), ("Plugins", check_plugin_manifests), diff --git a/tests/unit/test_doctor.py b/tests/unit/test_doctor.py index af2cdd1..1c12986 100644 --- a/tests/unit/test_doctor.py +++ b/tests/unit/test_doctor.py @@ -1,7 +1,7 @@ from pathlib import Path from webwright.run.doctor import ( - check_chromium, + check_firefox, check_openai_key, check_playwright, check_plugin_manifests, @@ -24,8 +24,8 @@ def test_check_playwright(): assert isinstance(message, str) -def test_check_chromium(): - ok, message = check_chromium() +def test_check_firefox(): + ok, message = check_firefox() assert isinstance(ok, bool) assert isinstance(message, str)