# 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 # Parameters | name | type | source phrase from task | default | allowed / format | |---------|------|-------------------------|-------------|-------------------------| | | str | "..." | "" | | | | int | "..." | | | | | str | "..." | "" | | # 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_(arg_a, arg_b, ...): ...` - `def lookup_(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_(arg_a: str, arg_b: int, arg_c: str) -> dict: """. Args: arg_a: ; . Default: "". arg_b: ; . Default: . arg_c: ; . Default: "". Returns: dict with keys ```` (), ```` (), """ ``` 3. **`argparse` CLI** under `if __name__ == "__main__":`. Every function argument has a matching `--` 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_.__doc__.splitlines()[0]) parser.add_argument("--arg-a", dest="arg_a", type=str, default="", help="") parser.add_argument("--arg-b", dest="arg_b", type=int, default=, help="") parser.add_argument("--arg-c", dest="arg_c", type=str, default="", help="") 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= arg_b= arg_c= ``` 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_/screenshots/final_execution__.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_/`: ```bash cd final_runs/run_ && 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_/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__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_/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_/` 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_/`, and re-verify.