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

188 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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:
```markdown
# 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).
```python
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:
```python
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>/`:
```bash
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:
```bash
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:
```bash
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.