CLI-Anything-Web Methodology (Phase 2)
Analyze captured traffic, design the CLI command structure, and implement the
complete Python CLI package. This skill owns the core transformation from raw
HTTP traffic to a production-ready CLI.
Copy this checklist and check off items as you complete them:
Phase 2 Progress:
- [ ] Prerequisites: raw-traffic.json exists (+ auth state if the site needs auth)
- [ ] Step A: traffic analyzed, protocol identified, <APP>.md written
- [ ] Step A: api-spec.json written (every endpoint cites raw-traffic.json evidence)
and passes `cli-web-devkit spec validate`
- [ ] Step B.0: scaffolded via scaffold-cli.py (.manifest.json present)
- [ ] Step B: client endpoint methods implemented from the spec
- [ ] Step B: command modules implemented + registered, REPL help in sync
- [ ] Smoke check passed (no protocol leaks), phase-state marked complete
Prerequisites (Hard Gate)
Do NOT start unless:
If raw-traffic.json is missing or has no WRITE operations, invoke the
capture skill first. If Phase 1 state shows failed, follow
skills/shared/RECOVERY.md §phase-state Check Failures before re-running.
Exception for read-only sites: If the site is genuinely read-only (search engine,
dashboard, analytics viewer with no create/update/delete), the trace may contain only
GET requests. In this case, note "read-only site — no write operations" in <APP>.md
and proceed. The generated CLI will have read-only commands (list, get, search) but
no create/update/delete commands. This is valid.
No-auth sites: If the target site requires no authentication (public API,
no login needed), the "Auth state captured" prerequisite does not apply. Note
"no-auth site" in <APP>.md and proceed.
Step A: Analyze (API Discovery)
Goal: Map raw traffic to a structured API model.
Process:
Read traffic-analysis.json first (if it exists alongside raw-traffic.json).
This file is auto-generated by parse-trace.py or mitmproxy-capture.py → analyze-traffic.py and contains
pre-detected protocol type, auth pattern, endpoint grouping, GraphQL operations,
batchexecute RPC IDs, and suggested CLI commands. Use it as a starting point —
verify its findings and fill in anything marked "unknown" by reading raw-traffic.json
manually.
Enhanced analysis (present only when captured via mitmproxy):
request_sequence (timeline-ordered requests with auth-flow detection),
session_lifecycle (cookie inventory, auth-cookie identification, session
pattern), and endpoint_sizes (response-size classification). If these
are missing (has_timestamps: false), the capture came from the default
trace path — rely on manual analysis for sequence/session detail.
If traffic-analysis.json doesn't exist, run the analyzer:
python ${CLAUDE_PLUGIN_ROOT}/scripts/analyze-traffic.py \
<app>/traffic-capture/raw-traffic.json --summary
Parse raw-traffic.json (for details the analyzer couldn't extract)
Group requests by base path (e.g., /api/v1/boards/, /api/v1/items/)
For each endpoint group, identify:
- HTTP method (GET/POST/PUT/DELETE/PATCH)
- URL pattern (extract path parameters like
:id)
- Query parameters and their types
- Request body schema (JSON fields, types, required/optional)
- Response body schema
- Authentication method (Bearer token, cookie, API key)
- Rate limiting signals (429 responses, retry-after headers)
Identify RPC protocol type -- classify the API transport:
| Protocol |
Detection Signal |
Client Pattern |
| REST |
Resource URLs (/api/v1/boards/:id), standard HTTP methods |
client.py with method-per-endpoint |
| GraphQL |
Single /graphql endpoint, query/mutation in body |
client.py with query templates |
| gRPC-Web |
application/grpc-web content type, binary payloads |
Proto-based client |
| Google batchexecute |
batchexecute in URL, f.req= body, )]}'\n prefix |
rpc/ subpackage (see references/google-batchexecute.md) |
| Custom RPC |
Single endpoint, method name in body, proprietary encoding |
Custom codec module |
| Public REST API |
Documented /api/ endpoints, OpenAPI spec, JSON responses |
Standard client.py with httpx |
| Plain HTML (no framework) |
No SPA root, no framework globals, data in <table>/<div> |
client.py with httpx + BeautifulSoup4 |
This determines client architecture in Step B -- REST uses simple client.py,
non-REST protocols need a dedicated rpc/ subpackage with encoder/decoder/types.
Detect data model:
- Entity types (boards, items, users, projects...)
- Relationships (board has many items, item belongs to board)
- ID formats (UUID, numeric, slug)
Detect auth pattern:
- Cookie-based sessions
- Bearer/JWT tokens
- OAuth refresh flow
- API key headers
- Browser-delegated auth: tokens embedded in page JavaScript (e.g.,
WIZ_global_data),
not in HTTP headers. Requires CDP for initial cookies, HTTP for token extraction.
See references/auth-strategies.md "Browser-Delegated Auth" section.
- No auth / public access: fully public API, no login required. CLI may
optionally support API key auth for write operations (e.g., dev.to).
Write <APP>.md -- software-specific SOP document
Write agent-harness/api-spec.json -- the machine-readable API spec.
Every endpoint MUST carry an evidence field citing its captured traffic
entry (raw-traffic.json#<index>) — never invent endpoints (this is the
structural enforcement of the RPC-ID verification rule). Schema and
validator:
cli-web-devkit spec validate <app>/agent-harness/api-spec.json
Downstream consumers: client method implementation (Step B), the
gap-analyzer (cli-web-devkit gaps), and the traffic-fidelity review in
Phase 4 (spec-vs-traffic becomes a deterministic diff).
Output: <APP>.md (human SOP) + api-spec.json (machine spec, validated).
References: traffic-patterns.md, google-batchexecute.md, ssr-patterns.md
Step B: Implement (Code Generation)
Study Existing CLIs First (Critical for Accuracy)
Before implementing, read an existing CLI that uses the same protocol as your
target. These are battle-tested implementations that solved the same problems you'll face.
| Protocol |
Reference CLI |
Key files to read |
| Google batchexecute |
notebooklm/agent-harness/cli_web/notebooklm/ |
core/rpc/encoder.py, core/rpc/decoder.py, core/client.py, core/auth.py |
| GraphQL + WAF |
booking/agent-harness/cli_web/booking/ |
core/client.py (curl_cffi + GraphQL), core/auth.py (WAF tokens) |
| HTML scraping |
futbin/agent-harness/cli_web/futbin/ |
core/client.py (httpx + BS4), commands/players.py |
| Next.js RSC |
producthunt/agent-harness/cli_web/producthunt/ |
core/client.py (curl_cffi + __next_f flight parsing) |
| REST API |
unsplash/agent-harness/cli_web/unsplash/ |
core/client.py, commands/photos.py |
| Simple HTML |
gh-trending/agent-harness/cli_web/gh_trending/ |
Minimal structure example |
How to use reference CLIs:
- Read the reference CLI's
core/client.py — understand the request/response pattern
- Read
core/auth.py — copy the login_browser() pattern exactly for Google apps
- Read
core/rpc/ (for batchexecute) — understand encoder/decoder, DO NOT reinvent
- Read
commands/ — see how Click commands are structured, how --json works
- Read
utils/helpers.py — see handle_errors(), _resolve_cli(), repl patterns
For batchexecute apps specifically, the notebooklm CLI is your bible:
- Copy the encoder/decoder architecture (don't reinvent the batchexecute wire format)
- Copy the auth token extraction pattern (CSRF, session ID, build label)
- Copy the cookie domain priority logic (critical for Israeli/international users)
- Adapt the RPC method IDs and param structures to your target app
The agent implementing the CLI MUST read these files before writing code. Use the
Agent tool to dispatch a research agent that reads
the reference implementation while you design the command structure.
Design Before You Code
Before writing any code, note the command structure in <APP>.md (10 minutes max):
- Map each API endpoint group to a Click command group:
/api/v1/boards/* → boards command group
/api/v1/items/* → items command group
- Map CRUD operations to subcommands (GET list →
list, GET single → get,
POST → create, PUT/PATCH → update, DELETE → delete)
- Note auth design:
auth login, auth status, auth refresh; credentials at
~/.config/cli-web-<app>/auth.json
- Note REPL design: bare command enters REPL, branded banner via
repl_skin.py
Goal: Generate the complete Python CLI package.
Package Structure
See HARNESS.md "Generated CLI Structure" for the complete package template.
Key points: cli_web/ namespace (NO __init__.py), <app>/ sub-package (HAS __init__.py),
core/, commands/, utils/, tests/ directories.
Step B.0: Scaffold Core Modules
Run the scaffold generator script (v2 — Jinja2 templates, requires
pip install jinja2) to create all boilerplate files:
python ${CLAUDE_PLUGIN_ROOT}/scripts/scaffold-cli.py <app>/agent-harness \
--app-name <app> \
--protocol <rest|graphql|html-scraping|batchexecute> \
--http-client <httpx|curl_cffi> \
--auth-type <none|cookie|api-key|google-sso> \
--resource <name> [--resource <name> ...] \
[--has-polling] [--has-context] [--has-partial-ids]
This renders exceptions.py, client.py skeleton, the unified auth.py (google-sso
handled via a template conditional), helpers.py, config.py, output.py, the CLI
entry point with REPL, one commands/<resource>.py per --resource flag,
setup.py, conftest.py, test_e2e.py skeleton, README/SKILL skeletons,
repl_skin.py, and (for batchexecute) the rpc/ subpackage. It also writes
.manifest.json (template version + profile) at the harness root — keep it;
fleet tooling depends on it. See skills/boilerplate/SKILL.md for the
template → output map and per-profile flag recipes.
Fallback: If the script is unavailable, follow
skills/shared/RECOVERY.md §scaffold-cli.py Unavailable — adapt from the
newest generated CLI (e.g., capitoltrades/agent-harness/), do NOT
reconstruct boilerplate from memory.
After scaffolding, review the generated files and customize client.py with actual
endpoint methods from <APP>.md.
Implementation Rules
All rules below are DEFINED in skills/shared/CONVENTIONS.md — this section
tells you when to apply them during implementation.
exceptions.py -- implement first. Required hierarchy and error-code mapping: CONVENTIONS.md §Exception Hierarchy. Complete code: references/exception-hierarchy-example.py.
client.py -- HTTP client with exception mapping and auth retry:
- HTTP library choice:
- Centralized auth header/cookie injection
- Automatic JSON parsing with response body verification
- Status code → exception mapping: 401/403→
AuthError, 404→NotFoundError, 429→RateLimitError, 5xx→ServerError (CONVENTIONS.md §Exception Hierarchy)
- Auth retry (3-attempt auto-refresh): current cookies → reload
auth.json → headless refresh, never more. The full table is CONVENTIONS.md §Auth Rules; the templates generate it by default.
- Exponential backoff for rate limits (CONVENTIONS.md §Exponential Backoff & Polling; code in
references/polling-backoff-example.py)
- For apps with 3+ resource types: split into namespaced sub-clients (
client.notebooks.list(), client.sources.add())
- See
references/client-architecture-example.py for the full pattern
auth.py -- handles token storage, refresh, expiry. Implementation depends on auth type:
For no-auth sites: DO NOT create auth.py, session.py, or auth command groups.
These files are dead code for public APIs and confuse users. The CLI should have
NO auth-related files or commands. The only exception is if the site has optional
auth (e.g., API key for write operations) — in that case, implement a minimal
auth module.
For browser-delegated auth (Google, Microsoft, etc.): Python
sync_playwright() login flow with cookie domain priority for international
users (CONVENTIONS.md §Auth Rules).
Storage, env var, cookie priority, and dual-format handling are defined in
CONVENTIONS.md §Auth Rules; implementation code for each pattern is in
references/auth-strategies.md (read section-addressed).
Anti-bot resilient client construction (when detected in Phase 2):
- Extract session tokens via CDP first (cookies), then HTTP GET + HTML parsing (CSRF, session IDs)
- Never hardcode build labels (
bl), session IDs (f.sid), or CSRF tokens -- extract dynamically at runtime
- Replicate same-origin headers captured during Phase 1 traffic (e.g.,
x-same-domain: 1 for Google apps)
- Implement auto-retry on 401/403: re-fetch homepage -> re-extract tokens -> retry once
- See
references/google-batchexecute.md for the complete Google pattern
RPC codec subpackage (for non-REST protocols like batchexecute):
When the API uses a non-REST protocol, add core/rpc/ with:
types.py -- method ID enum, URL constants
encoder.py -- request encoding (protocol-specific format)
decoder.py -- response decoding (strip prefix, parse chunks, extract results)
The client.py still exists but delegates encoding/decoding to rpc/.
Progress feedback -- Use rich>=13.0 spinners for operations >2s (suppress in --json mode). See references/rich-output-example.py.
JSON error output -- --json mode errors are JSON too, not plain text (CONVENTIONS.md §JSON Envelope). Implement via utils/output.py json_error().
All commands use handle_errors(json_mode) context manager — centralizes error handling, exit codes (1=user, 2=system, 130=interrupt), and JSON errors. See references/helpers-module-example.py.
Generation commands support --wait, --retry N, --output path — CONVENTIONS.md §Exponential Backoff & Polling; code in references/polling-backoff-example.py.
Windows UTF-8 fix — at the top of <app>_cli.py, reconfigure BOTH stdout AND stderr to UTF-8 before any import that prints (CONVENTIONS.md §Windows UTF-8 Fix has the exact snippet).
HTML table parsers MUST extract ALL visible columns — not just name/price,
because missing fields in --json output make the CLI useless for filtering and analysis.
If the site shows version, club, nation, stats, skills, weak foot — parse all of them.
Empty fields in --json output = incomplete parser.
Entry point: cli-web-<app> via setup.py console_scripts (CONVENTIONS.md §Naming Conventions)
Namespace: cli_web.*
utils/repl_skin.py, utils/doctor.py, and utils/mcp_server.py are all
vendored by scaffold-cli.py (canonical source: cli-web-core/cli_web_core/,
synced via cli-web-devkit resync) — never hand-edit the per-CLI copies.
The entry point registers the fleet-standard doctor and mcp-serve
commands from the vendored adapters (register_doctor_command(cli, ...),
register_mcp_command(cli, ...)); both derive from the Click tree, so no
per-command wiring is needed.
utils/helpers.py -- shared CLI helpers (generate for every CLI):
resolve_partial_id(partial, items) — prefix-match UUIDs for get/rename/delete
handle_errors(json_mode) — context manager replacing try/except in all commands
require_notebook(notebook_arg) — gets notebook ID from arg or persistent context
sanitize_filename(name) — safe filenames from artifact titles
poll_until_complete(check_fn) — exponential backoff polling
get_context_value(key) / set_context_value(key, value) — persistent context.json
See references/helpers-module-example.py for the complete module.
Not all helpers apply to every CLI. Include only what the CLI uses:
handle_errors and print_json are always needed. resolve_partial_id only
for UUID-based apps. require_notebook/context helpers only for apps with
persistent context. poll_until_complete only for generation/async operations.
REPL Implementation Rules (Critical)
The four REPL rules are defined with code examples in
CONVENTIONS.md §REPL Rules — apply them as you wire up <app>_cli.py:
- Parse REPL lines with
shlex.split(line), never line.split().
- Propagate
--json by PREPENDING it to the args list passed to
cli.main(args=..., standalone_mode=False) — never **ctx.params.
- Help-sync: every commit that adds a command/option updates
_print_repl_help() in the same commit.
- Required single values are
@click.argument positionals, not
@click.option(..., required=True).
These bugs appear in almost every generated REPL — read the §REPL Rules
section before writing the entry point, not after the REPL breaks.
Parallel Implementation (dispatch independent modules as subagents)
When the CLI has 3+ command groups (e.g., notebooks, sources, chat, artifacts),
dispatch parallel subagents -- one per command module. Each agent gets:
- The
<APP>.md API spec for its resource
- The
client.py and auth.py interfaces it depends on
- Clear scope: "Implement
commands/notebooks.py with list, get, create, delete"
Parallelization opportunities:
| Independent from each other |
Dispatch in parallel |
commands/notebooks.py, commands/sources.py, commands/chat.py |
Yes -- each command file only depends on client.py |
rpc/encoder.py and rpc/decoder.py |
Yes -- encoder doesn't depend on decoder |
auth.py and models.py |
Yes -- no shared logic |
client.py and commands/* |
No -- commands depend on client |
<app>_cli.py (entry point) |
Last -- imports all commands, write after they're done |
Implementation order (with maximum parallelism):
Phase A (sequential): Write core foundation
exceptions.py → client.py → auth.py (if needed) → models.py
Phase B (parallel): Dispatch ALL independent work simultaneously
┌─ Agent 1: commands/notebooks.py
├─ Agent 2: commands/sources.py
├─ Agent 3: commands/chat.py
├─ Agent 4: commands/artifacts.py
├─ Agent 5: rpc/encoder.py + rpc/decoder.py (if non-REST)
└─ Agent 6 (background): test_core.py (unit tests for core modules)
All run concurrently — each only depends on Phase A modules
Phase C (sequential): Wire everything together
utils/helpers.py → <app>_cli.py → __main__.py → setup.py
(repl_skin.py, doctor.py, mcp_server.py were already vendored by
scaffold-cli.py in Step B.0; the entry point registers doctor + mcp-serve)
Key parallelism rules:
- Dispatch independent command modules as parallel subagents (one per
commands/*.py file)
- Start unit test writing as a background agent during command implementation
- Entry point (
<app>_cli.py, setup.py) must come last (depends on all commands)
Mandatory Smoke Check (Before Testing Phase)
Before invoking testing, install (pip install -e .) and verify:
cli-web-<app> --help loads
cli-web-<app> auth status --json shows valid (if auth-required)
cli-web-<app> <resource> list --json returns real data
- One WRITE command works (if applicable)
Red flags — fix before testing: the full table is CONVENTIONS.md
§Protocol-Leak Smoke Check (wrb.fr/af.httprm leaks, empty []/null,
parser index mismatches). One methodology-specific case: a null WRITE response
may mean the operation is client-side — see references/google-batchexecute.md
"Client-Side Operations".
Update phase state:
python ${CLAUDE_PLUGIN_ROOT}/scripts/phase-state.py complete <app> \
--phase methodology --output <app>/agent-harness/
Next Step
When implementation is complete and the smoke check passes, invoke the testing
skill to plan and write tests.
Do NOT skip testing -- every CLI must have comprehensive tests before publishing.
Companion Skills
| Skill |
When it activates |
capture |
Phase 1 -- traffic recording (prerequisite for this skill) |
testing |
Phase 3 -- test writing, documentation |
standards |
Phase 4 -- publish, verify, smoke test |
Integration
| Relationship |
Skill |
| Preceded by |
capture (Phase 1) |
| Followed by |
testing (Phase 3) |
| References |
skills/shared/CONVENTIONS.md (all rules), skills/shared/RECOVERY.md (gate failures), traffic-patterns.md, auth-strategies.md, google-batchexecute.md, ssr-patterns.md, exception-hierarchy-example.py, client-architecture-example.py, polling-backoff-example.py, rich-output-example.py |
Reference Files
references/traffic-patterns.md -- Common API patterns (REST, GraphQL, RPC)
references/auth-strategies.md -- Auth implementation strategies
references/google-batchexecute.md -- Google batchexecute RPC protocol spec
references/ssr-patterns.md -- SSR framework patterns and data extraction strategies
references/exception-hierarchy-example.py -- Complete exception hierarchy with HTTP status mapping
references/client-architecture-example.py -- Namespaced sub-client pattern with auth retry
references/polling-backoff-example.py -- Exponential backoff polling and rate-limit retry
references/rich-output-example.py -- Rich progress bars, JSON error responses, table formatting
1---2name: methodology3description: Analyzes captured HTTP traffic, designs the CLI architecture, and implements the Python CLI package (Phase 2): parse raw-traffic.json, identify the protocol, write api-spec.json, scaffold from templates, and implement endpoint methods and Click command groups. Use after a capture completes and raw-traffic.json exists.4---56# CLI-Anything-Web Methodology (Phase 2)78Analyze captured traffic, design the CLI command structure, and implement the9complete Python CLI package. This skill owns the core transformation from raw10HTTP traffic to a production-ready CLI.1112Copy this checklist and check off items as you complete them:1314```15Phase 2 Progress:16- [ ] Prerequisites: raw-traffic.json exists (+ auth state if the site needs auth)17- [ ] Step A: traffic analyzed, protocol identified, <APP>.md written18- [ ] Step A: api-spec.json written (every endpoint cites raw-traffic.json evidence)19 and passes `cli-web-devkit spec validate`20- [ ] Step B.0: scaffolded via scaffold-cli.py (.manifest.json present)21- [ ] Step B: client endpoint methods implemented from the spec22- [ ] Step B: command modules implemented + registered, REPL help in sync23- [ ] Smoke check passed (no protocol leaks), phase-state marked complete24```2526---2728## Prerequisites (Hard Gate)2930Do NOT start unless:31- [ ] `raw-traffic.json` exists (with WRITE operations, or read-only GET-only traffic)32- [ ] Auth state was captured during Phase 1 (if the site requires auth)3334If raw-traffic.json is missing or has no WRITE operations, invoke the35`capture` skill first. If Phase 1 state shows `failed`, follow36`skills/shared/RECOVERY.md` §phase-state Check Failures before re-running.3738**Exception for read-only sites:** If the site is genuinely read-only (search engine,39dashboard, analytics viewer with no create/update/delete), the trace may contain only40GET requests. In this case, note "read-only site — no write operations" in `<APP>.md`41and proceed. The generated CLI will have read-only commands (list, get, search) but42no create/update/delete commands. This is valid.4344**No-auth sites:** If the target site requires no authentication (public API,45no login needed), the "Auth state captured" prerequisite does not apply. Note46"no-auth site" in `<APP>.md` and proceed.4748---4950## Step A: Analyze (API Discovery)5152**Goal:** Map raw traffic to a structured API model.5354**Process:**55560. **Read `traffic-analysis.json` first** (if it exists alongside `raw-traffic.json`).57 This file is auto-generated by `parse-trace.py` or `mitmproxy-capture.py` → `analyze-traffic.py` and contains58 pre-detected protocol type, auth pattern, endpoint grouping, GraphQL operations,59 batchexecute RPC IDs, and suggested CLI commands. Use it as a starting point —60 verify its findings and fill in anything marked "unknown" by reading `raw-traffic.json`61 manually.6263 **Enhanced analysis (present only when captured via mitmproxy):**64 `request_sequence` (timeline-ordered requests with auth-flow detection),65 `session_lifecycle` (cookie inventory, auth-cookie identification, session66 pattern), and `endpoint_sizes` (response-size classification). If these67 are missing (`has_timestamps: false`), the capture came from the default68 trace path — rely on manual analysis for sequence/session detail.6970 If `traffic-analysis.json` doesn't exist, run the analyzer:71 ```bash72 python ${CLAUDE_PLUGIN_ROOT}/scripts/analyze-traffic.py \73 <app>/traffic-capture/raw-traffic.json --summary74 ```75761. Parse `raw-traffic.json` (for details the analyzer couldn't extract)772. Group requests by base path (e.g., `/api/v1/boards/`, `/api/v1/items/`)783. For each endpoint group, identify:79 - HTTP method (GET/POST/PUT/DELETE/PATCH)80 - URL pattern (extract path parameters like `:id`)81 - Query parameters and their types82 - Request body schema (JSON fields, types, required/optional)83 - Response body schema84 - Authentication method (Bearer token, cookie, API key)85 - Rate limiting signals (429 responses, retry-after headers)86874. **Identify RPC protocol type** -- classify the API transport:8889 | Protocol | Detection Signal | Client Pattern |90 |----------|-----------------|----------------|91 | REST | Resource URLs (`/api/v1/boards/:id`), standard HTTP methods | `client.py` with method-per-endpoint |92 | GraphQL | Single `/graphql` endpoint, `query`/`mutation` in body | `client.py` with query templates |93 | gRPC-Web | `application/grpc-web` content type, binary payloads | Proto-based client |94 | Google batchexecute | `batchexecute` in URL, `f.req=` body, `)]}'\n` prefix | `rpc/` subpackage (see `references/google-batchexecute.md`) |95 | Custom RPC | Single endpoint, method name in body, proprietary encoding | Custom codec module |96 | Public REST API | Documented `/api/` endpoints, OpenAPI spec, JSON responses | Standard `client.py` with httpx |97 | Plain HTML (no framework) | No SPA root, no framework globals, data in `<table>`/`<div>` | `client.py` with httpx + BeautifulSoup4 |9899 This determines client architecture in Step B -- REST uses simple `client.py`,100 non-REST protocols need a dedicated `rpc/` subpackage with encoder/decoder/types.1011025. Detect data model:103 - Entity types (boards, items, users, projects...)104 - Relationships (board has many items, item belongs to board)105 - ID formats (UUID, numeric, slug)1061076. Detect auth pattern:108 - Cookie-based sessions109 - Bearer/JWT tokens110 - OAuth refresh flow111 - API key headers112 - Browser-delegated auth: tokens embedded in page JavaScript (e.g., `WIZ_global_data`),113 not in HTTP headers. Requires CDP for initial cookies, HTTP for token extraction.114 See `references/auth-strategies.md` "Browser-Delegated Auth" section.115 - No auth / public access: fully public API, no login required. CLI may116 optionally support API key auth for write operations (e.g., dev.to).1171187. Write `<APP>.md` -- software-specific SOP document1191208. Write `agent-harness/api-spec.json` -- the machine-readable API spec.121 Every endpoint MUST carry an `evidence` field citing its captured traffic122 entry (`raw-traffic.json#<index>`) — never invent endpoints (this is the123 structural enforcement of the RPC-ID verification rule). Schema and124 validator:125126 ```bash127 cli-web-devkit spec validate <app>/agent-harness/api-spec.json128 ```129130 Downstream consumers: client method implementation (Step B), the131 gap-analyzer (`cli-web-devkit gaps`), and the traffic-fidelity review in132 Phase 4 (spec-vs-traffic becomes a deterministic diff).133134**Output:** `<APP>.md` (human SOP) + `api-spec.json` (machine spec, validated).135136**References:** `traffic-patterns.md`, `google-batchexecute.md`, `ssr-patterns.md`137138---139140## Step B: Implement (Code Generation)141142### Study Existing CLIs First (Critical for Accuracy)143144Before implementing, **read an existing CLI that uses the same protocol** as your145target. These are battle-tested implementations that solved the same problems you'll face.146147| Protocol | Reference CLI | Key files to read |148|----------|--------------|-------------------|149| **Google batchexecute** | `notebooklm/agent-harness/cli_web/notebooklm/` | `core/rpc/encoder.py`, `core/rpc/decoder.py`, `core/client.py`, `core/auth.py` |150| **GraphQL + WAF** | `booking/agent-harness/cli_web/booking/` | `core/client.py` (curl_cffi + GraphQL), `core/auth.py` (WAF tokens) |151| **HTML scraping** | `futbin/agent-harness/cli_web/futbin/` | `core/client.py` (httpx + BS4), `commands/players.py` |152| **Next.js RSC** | `producthunt/agent-harness/cli_web/producthunt/` | `core/client.py` (curl_cffi + `__next_f` flight parsing) |153| **REST API** | `unsplash/agent-harness/cli_web/unsplash/` | `core/client.py`, `commands/photos.py` |154| **Simple HTML** | `gh-trending/agent-harness/cli_web/gh_trending/` | Minimal structure example |155156**How to use reference CLIs:**1571581. Read the reference CLI's `core/client.py` — understand the request/response pattern1592. Read `core/auth.py` — copy the login_browser() pattern exactly for Google apps1603. Read `core/rpc/` (for batchexecute) — understand encoder/decoder, DO NOT reinvent1614. Read `commands/` — see how Click commands are structured, how --json works1625. Read `utils/helpers.py` — see handle_errors(), _resolve_cli(), repl patterns163164**For batchexecute apps specifically**, the notebooklm CLI is your bible:165- Copy the encoder/decoder architecture (don't reinvent the batchexecute wire format)166- Copy the auth token extraction pattern (CSRF, session ID, build label)167- Copy the cookie domain priority logic (critical for Israeli/international users)168- Adapt the RPC method IDs and param structures to your target app169170The agent implementing the CLI MUST read these files before writing code. Use the171`Agent` tool to dispatch a research agent that reads172the reference implementation while you design the command structure.173174### Design Before You Code175176Before writing any code, note the command structure in `<APP>.md` (10 minutes max):177178- Map each API endpoint group to a Click command group:179 - `/api/v1/boards/*` → `boards` command group180 - `/api/v1/items/*` → `items` command group181- Map CRUD operations to subcommands (GET list → `list`, GET single → `get`,182 POST → `create`, PUT/PATCH → `update`, DELETE → `delete`)183- Note auth design: `auth login`, `auth status`, `auth refresh`; credentials at184 `~/.config/cli-web-<app>/auth.json`185- Note REPL design: bare command enters REPL, branded banner via `repl_skin.py`186187**Goal:** Generate the complete Python CLI package.188189### Package Structure190191See HARNESS.md "Generated CLI Structure" for the complete package template.192Key points: `cli_web/` namespace (NO `__init__.py`), `<app>/` sub-package (HAS `__init__.py`),193`core/`, `commands/`, `utils/`, `tests/` directories.194195### Step B.0: Scaffold Core Modules196197Run the scaffold generator script (v2 — Jinja2 templates, requires198`pip install jinja2`) to create all boilerplate files:199200```bash201python ${CLAUDE_PLUGIN_ROOT}/scripts/scaffold-cli.py <app>/agent-harness \202 --app-name <app> \203 --protocol <rest|graphql|html-scraping|batchexecute> \204 --http-client <httpx|curl_cffi> \205 --auth-type <none|cookie|api-key|google-sso> \206 --resource <name> [--resource <name> ...] \207 [--has-polling] [--has-context] [--has-partial-ids]208```209210This renders exceptions.py, client.py skeleton, the unified auth.py (google-sso211handled via a template conditional), helpers.py, config.py, output.py, the CLI212entry point with REPL, one `commands/<resource>.py` per `--resource` flag,213setup.py, conftest.py, test_e2e.py skeleton, README/SKILL skeletons,214repl_skin.py, and (for batchexecute) the rpc/ subpackage. It also writes215`.manifest.json` (template version + profile) at the harness root — keep it;216fleet tooling depends on it. See `skills/boilerplate/SKILL.md` for the217template → output map and per-profile flag recipes.218219> **Fallback**: If the script is unavailable, follow220> `skills/shared/RECOVERY.md` §scaffold-cli.py Unavailable — adapt from the221> newest generated CLI (e.g., `capitoltrades/agent-harness/`), do NOT222> reconstruct boilerplate from memory.223224After scaffolding, review the generated files and customize `client.py` with actual225endpoint methods from `<APP>.md`.226227### Implementation Rules228229All rules below are DEFINED in `skills/shared/CONVENTIONS.md` — this section230tells you when to apply them during implementation.231232- **`exceptions.py`** -- implement first. Required hierarchy and error-code mapping: CONVENTIONS.md §Exception Hierarchy. Complete code: `references/exception-hierarchy-example.py`.233234- **`client.py`** -- HTTP client with exception mapping and auth retry:235 - **HTTP library choice:**236 - `httpx` (default) — for most sites (REST, GraphQL, batchexecute)237 - `curl_cffi` — for Cloudflare-protected sites. Uses Chrome TLS fingerprint238 impersonation to bypass bot detection without cookies or auth:239 ```python240 from curl_cffi import requests as curl_requests241 resp = curl_requests.get(url, impersonate="chrome")242 ```243 Use `curl_cffi` when Phase 1 detects Cloudflare (`cf-ray` header, challenge page).244 Add `curl_cffi, beautifulsoup4` to `setup.py` instead of `httpx`.245 - Centralized auth header/cookie injection246 - Automatic JSON parsing with response body verification247 - **Status code → exception mapping**: 401/403→`AuthError`, 404→`NotFoundError`, 429→`RateLimitError`, 5xx→`ServerError` (CONVENTIONS.md §Exception Hierarchy)248 - **Auth retry (3-attempt auto-refresh)**: current cookies → reload `auth.json` → headless refresh, never more. The full table is CONVENTIONS.md §Auth Rules; the templates generate it by default.249 - Exponential backoff for rate limits (CONVENTIONS.md §Exponential Backoff & Polling; code in `references/polling-backoff-example.py`)250 - For apps with 3+ resource types: split into namespaced sub-clients (`client.notebooks.list()`, `client.sources.add()`)251 - See `references/client-architecture-example.py` for the full pattern252253- **`auth.py`** -- handles token storage, refresh, expiry. Implementation depends on auth type:254255 **For no-auth sites:** DO NOT create `auth.py`, `session.py`, or auth command groups.256 These files are dead code for public APIs and confuse users. The CLI should have257 NO auth-related files or commands. The only exception is if the site has optional258 auth (e.g., API key for write operations) — in that case, implement a minimal259 auth module.260261 **For browser-delegated auth (Google, Microsoft, etc.):** Python262 `sync_playwright()` login flow with cookie domain priority for international263 users (CONVENTIONS.md §Auth Rules).264265 Storage, env var, cookie priority, and dual-format handling are defined in266 CONVENTIONS.md §Auth Rules; implementation code for each pattern is in267 `references/auth-strategies.md` (read section-addressed).268269- **Anti-bot resilient client construction** (when detected in Phase 2):270 - Extract session tokens via CDP first (cookies), then HTTP GET + HTML parsing (CSRF, session IDs)271 - **Never hardcode** build labels (`bl`), session IDs (`f.sid`), or CSRF tokens -- extract dynamically at runtime272 - Replicate same-origin headers captured during Phase 1 traffic (e.g., `x-same-domain: 1` for Google apps)273 - Implement auto-retry on 401/403: re-fetch homepage -> re-extract tokens -> retry once274 - See `references/google-batchexecute.md` for the complete Google pattern275276- **RPC codec subpackage** (for non-REST protocols like batchexecute):277 When the API uses a non-REST protocol, add `core/rpc/` with:278 - `types.py` -- method ID enum, URL constants279 - `encoder.py` -- request encoding (protocol-specific format)280 - `decoder.py` -- response decoding (strip prefix, parse chunks, extract results)281 The `client.py` still exists but delegates encoding/decoding to `rpc/`.282283- **Progress feedback** -- Use `rich>=13.0` spinners for operations >2s (suppress in --json mode). See `references/rich-output-example.py`.284285- **JSON error output** -- `--json` mode errors are JSON too, not plain text (CONVENTIONS.md §JSON Envelope). Implement via `utils/output.py` json_error().286287- **All commands use `handle_errors(json_mode)` context manager** — centralizes error handling, exit codes (1=user, 2=system, 130=interrupt), and JSON errors. See `references/helpers-module-example.py`.288289- **Generation commands support `--wait`, `--retry N`, `--output path`** — CONVENTIONS.md §Exponential Backoff & Polling; code in `references/polling-backoff-example.py`.290291- **Windows UTF-8 fix** — at the top of `<app>_cli.py`, reconfigure BOTH stdout AND stderr to UTF-8 before any import that prints (CONVENTIONS.md §Windows UTF-8 Fix has the exact snippet).292- **HTML table parsers MUST extract ALL visible columns** — not just name/price,293 because missing fields in `--json` output make the CLI useless for filtering and analysis.294 If the site shows version, club, nation, stats, skills, weak foot — parse all of them.295 Empty fields in `--json` output = incomplete parser.296- Entry point: `cli-web-<app>` via setup.py console_scripts (CONVENTIONS.md §Naming Conventions)297- Namespace: `cli_web.*`298- `utils/repl_skin.py`, `utils/doctor.py`, and `utils/mcp_server.py` are all299 vendored by scaffold-cli.py (canonical source: `cli-web-core/cli_web_core/`,300 synced via `cli-web-devkit resync`) — never hand-edit the per-CLI copies.301 The entry point registers the fleet-standard `doctor` and `mcp-serve`302 commands from the vendored adapters (`register_doctor_command(cli, ...)`,303 `register_mcp_command(cli, ...)`); both derive from the Click tree, so no304 per-command wiring is needed.305- **`utils/helpers.py`** -- shared CLI helpers (generate for every CLI):306 - `resolve_partial_id(partial, items)` — prefix-match UUIDs for get/rename/delete307 - `handle_errors(json_mode)` — context manager replacing try/except in all commands308 - `require_notebook(notebook_arg)` — gets notebook ID from arg or persistent context309 - `sanitize_filename(name)` — safe filenames from artifact titles310 - `poll_until_complete(check_fn)` — exponential backoff polling311 - `get_context_value(key)` / `set_context_value(key, value)` — persistent context.json312 See `references/helpers-module-example.py` for the complete module.313314> **Not all helpers apply to every CLI.** Include only what the CLI uses:315> `handle_errors` and `print_json` are always needed. `resolve_partial_id` only316> for UUID-based apps. `require_notebook`/context helpers only for apps with317> persistent context. `poll_until_complete` only for generation/async operations.318319### REPL Implementation Rules (Critical)320321The four REPL rules are defined with code examples in322**CONVENTIONS.md §REPL Rules** — apply them as you wire up `<app>_cli.py`:3233241. Parse REPL lines with `shlex.split(line)`, never `line.split()`.3252. Propagate `--json` by PREPENDING it to the args list passed to326 `cli.main(args=..., standalone_mode=False)` — never `**ctx.params`.3273. Help-sync: every commit that adds a command/option updates328 `_print_repl_help()` in the same commit.3294. Required single values are `@click.argument` positionals, not330 `@click.option(..., required=True)`.331332These bugs appear in almost every generated REPL — read the §REPL Rules333section before writing the entry point, not after the REPL breaks.334335### Parallel Implementation (dispatch independent modules as subagents)336337When the CLI has 3+ command groups (e.g., notebooks, sources, chat, artifacts),338dispatch parallel subagents -- one per command module. Each agent gets:339- The `<APP>.md` API spec for its resource340- The `client.py` and `auth.py` interfaces it depends on341- Clear scope: "Implement `commands/notebooks.py` with list, get, create, delete"342343**Parallelization opportunities:**344345| Independent from each other | Dispatch in parallel |346|----------------------------|---------------------|347| `commands/notebooks.py`, `commands/sources.py`, `commands/chat.py` | Yes -- each command file only depends on `client.py` |348| `rpc/encoder.py` and `rpc/decoder.py` | Yes -- encoder doesn't depend on decoder |349| `auth.py` and `models.py` | Yes -- no shared logic |350| `client.py` and `commands/*` | **No** -- commands depend on client |351| `<app>_cli.py` (entry point) | **Last** -- imports all commands, write after they're done |352353**Implementation order (with maximum parallelism):**354355```356Phase A (sequential): Write core foundation357 exceptions.py → client.py → auth.py (if needed) → models.py358359Phase B (parallel): Dispatch ALL independent work simultaneously360 ┌─ Agent 1: commands/notebooks.py361 ├─ Agent 2: commands/sources.py362 ├─ Agent 3: commands/chat.py363 ├─ Agent 4: commands/artifacts.py364 ├─ Agent 5: rpc/encoder.py + rpc/decoder.py (if non-REST)365 └─ Agent 6 (background): test_core.py (unit tests for core modules)366 All run concurrently — each only depends on Phase A modules367368Phase C (sequential): Wire everything together369 utils/helpers.py → <app>_cli.py → __main__.py → setup.py370 (repl_skin.py, doctor.py, mcp_server.py were already vendored by371 scaffold-cli.py in Step B.0; the entry point registers doctor + mcp-serve)372```373374**Key parallelism rules:**375- Dispatch independent command modules as parallel subagents (one per `commands/*.py` file)376- Start unit test writing as a background agent during command implementation377- Entry point (`<app>_cli.py`, `setup.py`) must come last (depends on all commands)378379---380381## Mandatory Smoke Check (Before Testing Phase)382383Before invoking testing, install (`pip install -e .`) and verify:3841. `cli-web-<app> --help` loads3852. `cli-web-<app> auth status --json` shows valid (if auth-required)3863. `cli-web-<app> <resource> list --json` returns real data3874. One WRITE command works (if applicable)388389**Red flags — fix before testing:** the full table is CONVENTIONS.md390§Protocol-Leak Smoke Check (`wrb.fr`/`af.httprm` leaks, empty `[]`/`null`,391parser index mismatches). One methodology-specific case: a null WRITE response392may mean the operation is client-side — see `references/google-batchexecute.md`393"Client-Side Operations".394395Update phase state:396```bash397python ${CLAUDE_PLUGIN_ROOT}/scripts/phase-state.py complete <app> \398 --phase methodology --output <app>/agent-harness/399```400401## Next Step402403When implementation is complete and the smoke check passes, invoke the `testing`404skill to plan and write tests.405406Do NOT skip testing -- every CLI must have comprehensive tests before publishing.407408---409410## Companion Skills411412| Skill | When it activates |413|-------|------------------|414| `capture` | Phase 1 -- traffic recording (prerequisite for this skill) |415| `testing` | Phase 3 -- test writing, documentation |416| `standards` | Phase 4 -- publish, verify, smoke test |417418---419420## Integration421422| Relationship | Skill |423|-------------|-------|424| **Preceded by** | `capture` (Phase 1) |425| **Followed by** | `testing` (Phase 3) |426| **References** | `skills/shared/CONVENTIONS.md` (all rules), `skills/shared/RECOVERY.md` (gate failures), `traffic-patterns.md`, `auth-strategies.md`, `google-batchexecute.md`, `ssr-patterns.md`, `exception-hierarchy-example.py`, `client-architecture-example.py`, `polling-backoff-example.py`, `rich-output-example.py` |427428---429430## Reference Files431432- **`references/traffic-patterns.md`** -- Common API patterns (REST, GraphQL, RPC)433- **`references/auth-strategies.md`** -- Auth implementation strategies434- **`references/google-batchexecute.md`** -- Google batchexecute RPC protocol spec435- **`references/ssr-patterns.md`** -- SSR framework patterns and data extraction strategies436- **`references/exception-hierarchy-example.py`** -- Complete exception hierarchy with HTTP status mapping437- **`references/client-architecture-example.py`** -- Namespaced sub-client pattern with auth retry438- **`references/polling-backoff-example.py`** -- Exponential backoff polling and rate-limit retry439- **`references/rich-output-example.py`** -- Rich progress bars, JSON error responses, table formatting