App-agnostic engine that models a web UI as a graph and compiles flows into Playwright-MCP-executable scripts for agents.
Project description
uipilot
App-agnostic engine that models a web UI as a graph and compiles flows into Playwright-MCP-executable scripts for agents.
An agent driving a web UI live pays, per element: snapshot → reason about intent
→ find the ref → act. uipilot turns that into a lookup. You describe your app's
elements, actions, and flows once as structured YAML (a pack); the engine
queries that graph and emits a step-by-step Playwright-MCP script with selectors
pre-resolved and page snapshots inserted only where the DOM actually changes.
The engine ships no domain vocabulary — no "login", "cart", "tenant". It knows only generic concepts (app, element, action, flow, step, param, capture, capability). Everything project-specific lives in a pack, so porting to a new web app means authoring a new pack, not touching the engine.
The engine executes nothing. Every command emits structured data; the agent owns the browser and runs it. That keeps
uipilotsafe to point at money-moving flows.
Install & set up in your project
uv tool install uipilot # install the CLI globally
cd your-project
uipilot init # scaffold a pack + agent instructions here
uipilot init writes a minimal, valid pack skeleton under .uipilot/
(.uipilot/flowmap.config.yaml, .uipilot/data/, .uipilot/capabilities.py)
plus an agent instruction file at the project root — a Claude Code skill
(.claude/skills/uipilot/SKILL.md) and/or an AGENTS.md section (--agent claude|agents, repeatable). Then just ask your agent to run a flow: it
explores the app, fills the pack, and drives it for you. Re-running init
refreshes the skill but never clobbers a pack you've started (use --force to
overwrite).
Because init scaffolds a .uipilot/ pack in the cwd, every later uipilot …
call finds it with no --pack flag (resolution order: $UIPILOT_PACK → a
.uipilot/ pack in the cwd → the bundled examples/demo pack).
From source
uv sync # creates .venv, installs from uv.lock
uv run uipilot --pack examples/demo apps
Built with the uv_build backend; dependencies are locked in uv.lock.
The 60-second tour
# what apps/flows exist?
uipilot --pack examples/demo --format table apps
uipilot --pack examples/demo flows
# compile a flow into an executable Playwright-MCP script
uipilot --pack examples/demo script --flow create_project_with_credential
# find a route through the UI graph
uipilot --pack examples/demo path --from act_cs_view_dashboard --to act_cs_create_credential
# what breaks if I edit this button? (change blast radius)
uipilot --pack examples/demo uses cs_btn_create_project_submit
# is the map self-consistent? (CI gate)
uipilot --pack examples/demo validate
# does the map still match the running app? (read-only live probe)
uipilot --pack examples/demo verify --flow create_project_with_credential
Pack layout
.uipilot/ # the pack dir (scaffolded by `uipilot init`)
├── flowmap.config.yaml # binds apps, tokens, risk taxonomy, capabilities
├── capabilities.py # (optional) named auth adapters the engine calls by key
└── data/
├── <app>.app.yaml # one per app: app header + elements + actions
└── flows.yaml # named multi-app flows (+ shared/API actions)
- Element — a node with a structured selector (
role(name)>label>text>css) and a purpose. Never a raw locator string, so it re-emits to a Playwright-MCP element description or a@playwright/testlocator. - Action — the graph proper. A UI action carries a
route, an orderedstepsrecipe,prev/nextedges,params, andcaptures. An API action (transport: api) binds to an existing test-framework factory viacall:for fast provisioning or backend cross-checks — it has noprev/nextand is excluded from pathfinding. - Flow — an ordered
pathof action ids. Entries can be a bare id, a subflow (use:), or an aliased repeat ({action, as, params}). Reuse is layered L1–L4 (shared actions → subflows → aliased invocations → guards). - Capture — a value one action produces (
from: url|element|response|…) that later steps consume as{{captured.x}}/{{alias.x}}, bridging UI and API transports unchanged.
See examples/demo/ for a complete two-app pack exercising
every feature.
Docs: src/uipilot/templates/skill.md — the
agent guide, shipped and installed by uipilot init (core loop, execution
contract, safety) · docs/USE_CASES.md — business use cases
at a glance · docs/PACK_AUTHORING.md — pack field
reference and porting checklist.
Commands
| Command | Purpose |
|---|---|
apps |
List apps, base URLs, auth entry flows |
actions |
List/filter actions (--app --risk --grep --section --transport) |
elements |
List/filter elements + resolved selectors |
show action <id> / show element <id> |
Full detail, selectors resolved inline |
uses <id> |
Reverse index — change blast radius before you edit |
flows / flow <name> [--params] |
List / show named flows (--params: aggregated param manifest only) |
path --from <a> --to <b> |
BFS a route through the flow graph |
script … |
Emit an executable Playwright-MCP script (see below) |
validate |
Lint the model statically (CI gate) |
verify … |
Emit a read-only probe to detect live UI drift |
emit --format pw-pom|pw-test |
Generate Python POM classes / a pw-test spec |
capabilities [--check] |
List (and import-check) the pack's auth adapters |
init [dir] [--agent claude|agents] [--force] |
Scaffold a pack + agent instructions in a project |
import-md <file> --out <dir> |
One-time: seed a pack from a retired Markdown map |
script
uipilot script (--flow <name> | --from <a> --to <b> | --actions a,b,c)
[--params file.json] [--set key=value ...]
[--skip-auth] [--batch] [--refuse-destructive]
[--format playwright-mcp|steps|json|pw-test|human]
-
--format humanemits a plain-English preview (numbered steps, params, risk, teardown) for a person to review before an agent runs a risky flow. -
Param resolution:
--set>--params> model defaults. Defaults expand pack tokens ({{prefix}},{{seq}}). Unresolved required params are emitted as{{placeholder}}and listed underparams_required. -
Secrets (
type: secret) never appear in the header echo — only in the one step that consumes them. -
--refuse-destructiverefuses to emit if any action on the path carries ariskin the pack'srisk.gatedset. The header always surfacesrisk_max. -
--batchcollapses consecutive form fills into onebrowser_fill_form.
validate vs verify
validate(offline, CI-gating) — internal consistency only: dangling refs, broken edges, undeclared params, capture collisions, unreachable nodes, unbound API calls, coverage bypass. Answers "is the map self-consistent?"verify(live) — emits a read-only probe (navigate + snapshot + assert-each-element-resolves; no mutating clicks) the agent runs against the app. Answers "does the map still match the running UI?"--drivewalks a full flow but refusesrisk.gatedsteps unless--allow-gated.
Lint codes: E_DANGLING_ELEMENT, E_BROKEN_EDGE, E_SELECTOR_AMBIGUOUS,
E_PARAM_UNDECLARED, E_SUBFLOW_CYCLE, E_CAPTURE_COLLISION,
E_API_CALL_UNBOUND, W_NO_STEPS, W_UNREACHABLE, W_NO_CAPTURE,
W_DUPLICATE_RECIPE, W_UI_COVERAGE_BYPASS, W_UNMET_REQUIRES.
Development
uv sync # installs the project + the dev dependency group
uv run pytest -q
Architecture
The package is split into four layers under src/uipilot/, with dependencies
pointing strictly inward (presentation → application → {domain, infrastructure} → domain):
domain/ pure model + business rules (no I/O, no frameworks)
model entities: App, Element, Action, Flow, Step, Param, Capture, Selector, Pack
templating RuntimeContext + {{token}} resolution
flows subflow expansion (single source of truth)
graph BFS pathfinding + reachability
validation the static linter (E_/W_ codes)
compiler flow → CompiledScript
verification read-only drift-probe builder
usage reverse index
infrastructure/ I/O and external concerns
pack_loader YAML → domain model
capabilities dynamic import of pack auth adapters
markdown_importer one-shot MD → seed YAML
application/ use-case orchestration
service open_pack + compile/validate/verify/route/uses/… wiring
presentation/ the CLI and output renderers
cli Typer entrypoint (talks only to application)
renderers json / playwright-mcp / steps / pw-test / pw-pom
The domain layer imports nothing outside the standard library, so the business rules are testable with no YAML, filesystem, or CLI in the loop.
License
MIT.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file uipilot-0.2.0.tar.gz.
File metadata
- Download URL: uipilot-0.2.0.tar.gz
- Upload date:
- Size: 47.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ce9a6c73fe0d4631de9b8d97136daf09f232f99aa3965900d5e5ebd0dfaefbd7
|
|
| MD5 |
91ac2bb0747c5b5af6b30d4bdf596d4d
|
|
| BLAKE2b-256 |
e35a6015c80f592abcc3b66998d8f136e0585a46deffdbbc823a7b252c1242ea
|
Provenance
The following attestation bundles were made for uipilot-0.2.0.tar.gz:
Publisher:
publish-to-pypi.yml on l0kifs/uipilot
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
uipilot-0.2.0.tar.gz -
Subject digest:
ce9a6c73fe0d4631de9b8d97136daf09f232f99aa3965900d5e5ebd0dfaefbd7 - Sigstore transparency entry: 2115507039
- Sigstore integration time:
-
Permalink:
l0kifs/uipilot@50dc42ae73912c78780ec8ee48c9b1b1605c2dca -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/l0kifs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@50dc42ae73912c78780ec8ee48c9b1b1605c2dca -
Trigger Event:
release
-
Statement type:
File details
Details for the file uipilot-0.2.0-py3-none-any.whl.
File metadata
- Download URL: uipilot-0.2.0-py3-none-any.whl
- Upload date:
- Size: 59.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9635b027fa033e4485c4a4dea02b01f6159d31f5265339db12cd2654e8a6572a
|
|
| MD5 |
c52ece91a4a51f267142596fe4341649
|
|
| BLAKE2b-256 |
1648f6bb4d77e9e0c1aa8787d646f354ce47d371a7bda2279172870f7ab6dec5
|
Provenance
The following attestation bundles were made for uipilot-0.2.0-py3-none-any.whl:
Publisher:
publish-to-pypi.yml on l0kifs/uipilot
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
uipilot-0.2.0-py3-none-any.whl -
Subject digest:
9635b027fa033e4485c4a4dea02b01f6159d31f5265339db12cd2654e8a6572a - Sigstore transparency entry: 2115507401
- Sigstore integration time:
-
Permalink:
l0kifs/uipilot@50dc42ae73912c78780ec8ee48c9b1b1605c2dca -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/l0kifs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@50dc42ae73912c78780ec8ee48c9b1b1605c2dca -
Trigger Event:
release
-
Statement type: