7.2 KiB
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
# Parametersmust (a) become a function argument and (b) become anargparse --flagwith 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.pywith no arguments must reproduce the task. - Critical Points are still required; they are the verification contract.
final_script.py — required shape
-
One reusable function named after the task domain. Examples:
def search_<domain>(arg_a, arg_b, ...): ...def lookup_<entity>(query, filters): ...
-
Google-style docstring with summary, full
Args:block, andReturns:. EachArgs:entry documents:- the argument name and type,
- what it represents in the task domain,
- accepted format / units / allowed values,
- the default (mirroring the
# Parameterstable).
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>), """ -
argparseCLI underif __name__ == "__main__":. Every function argument has a matching--<arg>flag withtype=,help=(copied from the docstring), anddefault=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) -
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.
-
Action-log parameter echo. The first line written to
final_script_log.txtafter reset MUST be astep 0 params: ...line listing every resolved argument asname=valuepairs, e.g.:step 0 params: arg_a=<value> arg_b=<value> arg_c=<value>so the resolved inputs are visible in any verification pass.
-
Same instrumentation as default mode: viewport 1280×1800, headless local Firefox, no
full_page=True, screenshots saved asfinal_runs/run_<id>/screenshots/final_execution_<step>_<action>.png, final datum appended tofinal_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:
-
Reproduce the task with no arguments. Inside a fresh
final_runs/run_<id>/:cd final_runs/run_<id> && python final_script.pyThe run must succeed end-to-end and produce the expected screenshots and
step 0 params: ...log line. -
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.
-
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). -
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:
plan.mdcontains both# Parameters(with name, type, source phrase, default, allowed/format) and# Critical Pointschecklists.final_script.pydefines exactly one reusable function with a Google-styleArgs:docstring covering every parameter.- Every
# Parametersentry maps 1-to-1 to a function argument and an argparse--flagwhose default equals the concrete task value. - The script is import-safe (smoke test passes).
python final_script.py(no args) insidefinal_runs/run_<id>/reproduced the task; all CPs verified against saved screenshots and the action log.step 0 params: ...line is present infinal_script_log.txt.- The user has seen the final datum and the
--helpoutput 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.