Files
microsoft--webwright/skills/webwright/reference/cli_tool_mode.md
T
2026-07-13 12:28:42 +08:00

7.2 KiB
Raw Blame History

CLI Tool Mode

Default Webwright runs (/webwright:run, plain prompt) produce a one-shot final_script.py that solves the task for the literal values the user provided. CLI tool mode (/webwright:craft) instead produces a reusable, parameterized CLI tool: the same script can be re-run later with different argument values to perform the same kind of task.

This mode is adapted from webwright/src/webwright/config/crafted_cli.yaml's "Final-Script Shape (CLI Tool, MANDATORY)" contract. The OpenAI-backed self_reflection gate is replaced by your own self-verification against plan.md.

When to use

Trigger CLI tool mode when:

  • the user invokes /webwright:craft …, or
  • the user says "make it reusable", "parameterize", "turn this into a CLI", "I want to call this again with different X", or similar.

Otherwise, stay in default one-shot mode.

plan.md — add a # Parameters section

Before writing the script, identify every requirement the user could plausibly vary and list them in plan.md in addition to the usual # Critical Points checklist:

# Task
<verbatim task description>

# Parameters
| name    | type | source phrase from task | default     | allowed / format        |
|---------|------|-------------------------|-------------|-------------------------|
| <arg_a> | str  | "..."                   | "<value>"   | <format / allowed set>  |
| <arg_b> | int  | "..."                   | <value>     | <range or units>        |
| <arg_c> | str  | "..."                   | "<value>"   | <format>                |

# Critical Points
- [ ] CP1: ...
- [ ] CP2: ...

Rules:

  • Every entry in # Parameters must (a) become a function argument and (b) become an argparse --flag with the listed default.
  • Items that are truly fixed for the site (start URL, site name, selector strategy) are NOT parameters — keep them hard-coded.
  • Defaults reproduce the original task exactly. Running python final_script.py with no arguments must reproduce the task.
  • Critical Points are still required; they are the verification contract.

final_script.py — required shape

  1. One reusable function named after the task domain. Examples:

    • def search_<domain>(arg_a, arg_b, ...): ...
    • def lookup_<entity>(query, filters): ...
  2. Google-style docstring with summary, full Args: block, and Returns:. Each Args: entry documents:

    • the argument name and type,
    • what it represents in the task domain,
    • accepted format / units / allowed values,
    • the default (mirroring the # Parameters table).
    def search_<domain>(arg_a: str, arg_b: int, arg_c: str) -> dict:
        """<One-line summary of what this tool does on the target site>.
    
        Args:
            arg_a: <what it represents>; <format / allowed values>.
                Default: "<value>".
            arg_b: <what it represents>; <range / units>.
                Default: <value>.
            arg_c: <what it represents>; <format>.
                Default: "<value>".
    
        Returns:
            dict with keys ``<key1>`` (<type>), ``<key2>`` (<type>),
        """
    
  3. argparse CLI under if __name__ == "__main__":. Every function argument has a matching --<arg> flag with type=, help= (copied from the docstring), and default= equal to the concrete task value:

    if __name__ == "__main__":
        import argparse
        parser = argparse.ArgumentParser(
            description=search_<domain>.__doc__.splitlines()[0])
        parser.add_argument("--arg-a", dest="arg_a", type=str,
                            default="<value>",
                            help="<copied from docstring>")
        parser.add_argument("--arg-b", dest="arg_b", type=int,
                            default=<value>,
                            help="<copied from docstring>")
        parser.add_argument("--arg-c", dest="arg_c", type=str,
                            default="<value>",
                            help="<copied from docstring>")
        args = parser.parse_args()
        result = asyncio.run(_run(**vars(args)))
        print(result)
    
  4. Side-effect-free at import time. No browser launch, no network call, no file write at module top-level. The reusable function must be importable from another Python process without triggering a run.

  5. Action-log parameter echo. The first line written to final_script_log.txt after reset MUST be a step 0 params: ... line listing every resolved argument as name=value pairs, e.g.:

    step 0 params: arg_a=<value> arg_b=<value> arg_c=<value>
    

    so the resolved inputs are visible in any verification pass.

  6. Same instrumentation as default mode: viewport 1280×1800, headless local Firefox, no full_page=True, screenshots saved as final_runs/run_<id>/screenshots/final_execution_<step>_<action>.png, final datum appended to final_script_log.txt.

Verification (replaces self_reflection)

In addition to the default self-verification (every CP in plan.md ticked with cited screenshot/log evidence), CLI mode requires:

  1. Reproduce the task with no arguments. Inside a fresh final_runs/run_<id>/:

    cd final_runs/run_<id> && python final_script.py
    

    The run must succeed end-to-end and produce the expected screenshots and step 0 params: ... log line.

  2. Import-safety smoke test. From any other directory:

    python -c "import importlib.util, pathlib; \
      spec = importlib.util.spec_from_file_location('fs', 'final_runs/run_<id>/final_script.py'); \
      m = importlib.util.module_from_spec(spec); spec.loader.exec_module(m); \
      print([n for n in dir(m) if not n.startswith('_')])"
    

    This must complete instantly with no browser launch and print the reusable function's name.

  3. Optional second run with a different argument value. Demonstrates parameterization actually works. Run inside final_runs/run_<id>_alt/ (or just save its log/screenshot folder there). Skip only if the alternate value would clearly fail (e.g. an unsupported value on the target site).

  4. Print --help. End by showing the user:

    python final_runs/run_<id>/final_script.py --help
    

Completion gate (CLI mode)

Set the task complete only when all are true:

  1. plan.md contains both # Parameters (with name, type, source phrase, default, allowed/format) and # Critical Points checklists.
  2. final_script.py defines exactly one reusable function with a Google-style Args: docstring covering every parameter.
  3. Every # Parameters entry maps 1-to-1 to a function argument and an argparse --flag whose default equals the concrete task value.
  4. The script is import-safe (smoke test passes).
  5. python final_script.py (no args) inside final_runs/run_<id>/ reproduced the task; all CPs verified against saved screenshots and the action log.
  6. step 0 params: ... line is present in final_script_log.txt.
  7. The user has seen the final datum and the --help output so they know how to call the tool again with different arguments.

If any of those is false, do not declare done — diagnose, fix the script (preserving the CLI shape), re-run inside the next run_<id+1>/, and re-verify.