uipilot 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- uipilot-0.1.0/PKG-INFO +227 -0
- uipilot-0.1.0/README.md +214 -0
- uipilot-0.1.0/pyproject.toml +74 -0
- uipilot-0.1.0/src/uipilot/__init__.py +22 -0
- uipilot-0.1.0/src/uipilot/application/__init__.py +11 -0
- uipilot-0.1.0/src/uipilot/application/service.py +220 -0
- uipilot-0.1.0/src/uipilot/domain/__init__.py +17 -0
- uipilot-0.1.0/src/uipilot/domain/compiler.py +520 -0
- uipilot-0.1.0/src/uipilot/domain/errors.py +32 -0
- uipilot-0.1.0/src/uipilot/domain/flows.py +58 -0
- uipilot-0.1.0/src/uipilot/domain/graph.py +115 -0
- uipilot-0.1.0/src/uipilot/domain/model.py +362 -0
- uipilot-0.1.0/src/uipilot/domain/templating.py +101 -0
- uipilot-0.1.0/src/uipilot/domain/usage.py +68 -0
- uipilot-0.1.0/src/uipilot/domain/validation.py +324 -0
- uipilot-0.1.0/src/uipilot/domain/verification.py +140 -0
- uipilot-0.1.0/src/uipilot/infrastructure/__init__.py +14 -0
- uipilot-0.1.0/src/uipilot/infrastructure/capabilities.py +103 -0
- uipilot-0.1.0/src/uipilot/infrastructure/markdown_importer.py +171 -0
- uipilot-0.1.0/src/uipilot/infrastructure/pack_loader.py +334 -0
- uipilot-0.1.0/src/uipilot/infrastructure/scaffold.py +122 -0
- uipilot-0.1.0/src/uipilot/presentation/__init__.py +11 -0
- uipilot-0.1.0/src/uipilot/presentation/cli.py +570 -0
- uipilot-0.1.0/src/uipilot/presentation/renderers.py +311 -0
- uipilot-0.1.0/src/uipilot/templates/__init__.py +0 -0
- uipilot-0.1.0/src/uipilot/templates/capabilities.py +18 -0
- uipilot-0.1.0/src/uipilot/templates/data/app.app.yaml +15 -0
- uipilot-0.1.0/src/uipilot/templates/data/flows.yaml +3 -0
- uipilot-0.1.0/src/uipilot/templates/flowmap.config.yaml +14 -0
- uipilot-0.1.0/src/uipilot/templates/skill.md +131 -0
uipilot-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: uipilot
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: App-agnostic engine that models a web UI as a graph and compiles flows into Playwright-MCP-executable scripts for agents.
|
|
5
|
+
Keywords: playwright,mcp,ui-testing,automation,agents,web-ui
|
|
6
|
+
Author: Sergei Konovalov
|
|
7
|
+
License: MIT
|
|
8
|
+
Requires-Dist: typer>=0.12
|
|
9
|
+
Requires-Dist: rich>=13.7
|
|
10
|
+
Requires-Dist: pyyaml>=6.0
|
|
11
|
+
Requires-Python: >=3.12
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# uipilot
|
|
15
|
+
|
|
16
|
+
**App-agnostic engine that models a web UI as a graph and compiles flows into
|
|
17
|
+
Playwright-MCP-executable scripts for agents.**
|
|
18
|
+
|
|
19
|
+
An agent driving a web UI live pays, per element: snapshot → reason about intent
|
|
20
|
+
→ find the ref → act. `uipilot` turns that into a lookup. You describe your app's
|
|
21
|
+
elements, actions, and flows once as structured YAML (a **pack**); the engine
|
|
22
|
+
queries that graph and emits a step-by-step Playwright-MCP script with selectors
|
|
23
|
+
pre-resolved and page snapshots inserted only where the DOM actually changes.
|
|
24
|
+
|
|
25
|
+
The engine ships **no domain vocabulary** — no "login", "cart", "tenant". It
|
|
26
|
+
knows only generic concepts (**app, element, action, flow, step, param,
|
|
27
|
+
capture, capability**). Everything project-specific lives in a pack, so porting
|
|
28
|
+
to a new web app means authoring a new pack, not touching the engine.
|
|
29
|
+
|
|
30
|
+
> **The engine executes nothing.** Every command *emits structured data*; the
|
|
31
|
+
> agent owns the browser and runs it. That keeps `uipilot` safe to point at
|
|
32
|
+
> money-moving flows.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Install & set up in your project
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
uv tool install uipilot # install the CLI globally
|
|
40
|
+
cd your-project
|
|
41
|
+
uipilot init # scaffold a pack + agent instructions here
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`uipilot init` writes a minimal, valid pack skeleton (`flowmap.config.yaml`,
|
|
45
|
+
`data/`, `capabilities.py`) plus an agent instruction file — a Claude Code skill
|
|
46
|
+
(`.claude/skills/uipilot/SKILL.md`) and/or an `AGENTS.md` section (`--agent
|
|
47
|
+
claude|agents`, repeatable). Then just **ask your agent to run a flow**: it
|
|
48
|
+
explores the app, fills the pack, and drives it for you. Re-running `init`
|
|
49
|
+
refreshes the skill but never clobbers a pack you've started (use `--force` to
|
|
50
|
+
overwrite).
|
|
51
|
+
|
|
52
|
+
Because `init` scaffolds into the cwd, every later `uipilot …` call finds the
|
|
53
|
+
pack with **no `--pack` flag** (resolution order: `$UIPILOT_PACK` → a
|
|
54
|
+
`flowmap.config.yaml` in the cwd → the bundled `examples/demo` pack).
|
|
55
|
+
|
|
56
|
+
### From source
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
uv sync # creates .venv, installs from uv.lock
|
|
60
|
+
uv run uipilot --pack examples/demo apps
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Built with the `uv_build` backend; dependencies are locked in `uv.lock`.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## The 60-second tour
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# what apps/flows exist?
|
|
71
|
+
uipilot --pack examples/demo --format table apps
|
|
72
|
+
uipilot --pack examples/demo flows
|
|
73
|
+
|
|
74
|
+
# compile a flow into an executable Playwright-MCP script
|
|
75
|
+
uipilot --pack examples/demo script --flow create_project_with_credential
|
|
76
|
+
|
|
77
|
+
# find a route through the UI graph
|
|
78
|
+
uipilot --pack examples/demo path --from act_cs_view_dashboard --to act_cs_create_credential
|
|
79
|
+
|
|
80
|
+
# what breaks if I edit this button? (change blast radius)
|
|
81
|
+
uipilot --pack examples/demo uses cs_btn_create_project_submit
|
|
82
|
+
|
|
83
|
+
# is the map self-consistent? (CI gate)
|
|
84
|
+
uipilot --pack examples/demo validate
|
|
85
|
+
|
|
86
|
+
# does the map still match the running app? (read-only live probe)
|
|
87
|
+
uipilot --pack examples/demo verify --flow create_project_with_credential
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Pack layout
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
your_pack/
|
|
96
|
+
├── flowmap.config.yaml # binds apps, tokens, risk taxonomy, capabilities
|
|
97
|
+
├── capabilities.py # (optional) named auth adapters the engine calls by key
|
|
98
|
+
└── data/
|
|
99
|
+
├── <app>.app.yaml # one per app: app header + elements + actions
|
|
100
|
+
└── flows.yaml # named multi-app flows (+ shared/API actions)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
* **Element** — a node with a structured selector (`role(name)` > `label` >
|
|
104
|
+
`text` > `css`) and a purpose. Never a raw locator string, so it re-emits to a
|
|
105
|
+
Playwright-MCP element description *or* a `@playwright/test` locator.
|
|
106
|
+
* **Action** — the graph proper. A UI action carries a `route`, an ordered
|
|
107
|
+
`steps` recipe, `prev`/`next` edges, `params`, and `captures`. An **API
|
|
108
|
+
action** (`transport: api`) binds to an existing test-framework factory via
|
|
109
|
+
`call:` for fast provisioning or backend cross-checks — it has no `prev`/`next`
|
|
110
|
+
and is excluded from pathfinding.
|
|
111
|
+
* **Flow** — an ordered `path` of action ids. Entries can be a bare id, a
|
|
112
|
+
subflow (`use:`), or an aliased repeat (`{action, as, params}`). Reuse is
|
|
113
|
+
layered L1–L4 (shared actions → subflows → aliased invocations → guards).
|
|
114
|
+
* **Capture** — a value one action produces (`from: url|element|response|…`)
|
|
115
|
+
that later steps consume as `{{captured.x}}` / `{{alias.x}}`, bridging UI and
|
|
116
|
+
API transports unchanged.
|
|
117
|
+
|
|
118
|
+
See [`examples/demo/`](examples/demo/) for a complete two-app pack exercising
|
|
119
|
+
every feature.
|
|
120
|
+
|
|
121
|
+
**Docs:** [`src/uipilot/templates/skill.md`](src/uipilot/templates/skill.md) — the
|
|
122
|
+
agent guide, shipped and installed by `uipilot init` (core loop, execution
|
|
123
|
+
contract, safety) · [`docs/USE_CASES.md`](docs/USE_CASES.md) — business use cases
|
|
124
|
+
at a glance · [`docs/PACK_AUTHORING.md`](docs/PACK_AUTHORING.md) — pack field
|
|
125
|
+
reference and porting checklist.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Commands
|
|
130
|
+
|
|
131
|
+
| Command | Purpose |
|
|
132
|
+
|---|---|
|
|
133
|
+
| `apps` | List apps, base URLs, auth entry flows |
|
|
134
|
+
| `actions` | List/filter actions (`--app --risk --grep --section --transport`) |
|
|
135
|
+
| `elements` | List/filter elements + resolved selectors |
|
|
136
|
+
| `show action <id>` / `show element <id>` | Full detail, selectors resolved inline |
|
|
137
|
+
| `uses <id>` | Reverse index — change blast radius before you edit |
|
|
138
|
+
| `flows` / `flow <name> [--params]` | List / show named flows (`--params`: aggregated param manifest only) |
|
|
139
|
+
| `path --from <a> --to <b>` | BFS a route through the flow graph |
|
|
140
|
+
| `script …` | **Emit an executable Playwright-MCP script** (see below) |
|
|
141
|
+
| `validate` | Lint the model statically (CI gate) |
|
|
142
|
+
| `verify …` | Emit a read-only probe to detect live UI drift |
|
|
143
|
+
| `emit --format pw-pom\|pw-test` | Generate Python POM classes / a pw-test spec |
|
|
144
|
+
| `capabilities [--check]` | List (and import-check) the pack's auth adapters |
|
|
145
|
+
| `init [dir] [--agent claude\|agents] [--force]` | Scaffold a pack + agent instructions in a project |
|
|
146
|
+
| `import-md <file> --out <dir>` | One-time: seed a pack from a retired Markdown map |
|
|
147
|
+
|
|
148
|
+
### `script`
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
uipilot script (--flow <name> | --from <a> --to <b> | --actions a,b,c)
|
|
152
|
+
[--params file.json] [--set key=value ...]
|
|
153
|
+
[--skip-auth] [--batch] [--refuse-destructive]
|
|
154
|
+
[--format playwright-mcp|steps|json|pw-test|human]
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
* **`--format human`** emits a plain-English preview (numbered steps, params,
|
|
158
|
+
risk, teardown) for a person to review before an agent runs a risky flow.
|
|
159
|
+
|
|
160
|
+
* **Param resolution:** `--set` > `--params` > model defaults. Defaults expand
|
|
161
|
+
pack tokens (`{{prefix}}`, `{{seq}}`). Unresolved **required** params are
|
|
162
|
+
emitted as `{{placeholder}}` and listed under `params_required`.
|
|
163
|
+
* **Secrets** (`type: secret`) never appear in the header echo — only in the one
|
|
164
|
+
step that consumes them.
|
|
165
|
+
* **`--refuse-destructive`** refuses to emit if any action on the path carries a
|
|
166
|
+
`risk` in the pack's `risk.gated` set. The header always surfaces `risk_max`.
|
|
167
|
+
* **`--batch`** collapses consecutive form fills into one `browser_fill_form`.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## `validate` vs `verify`
|
|
172
|
+
|
|
173
|
+
* **`validate`** (offline, CI-gating) — internal consistency only: dangling
|
|
174
|
+
refs, broken edges, undeclared params, capture collisions, unreachable nodes,
|
|
175
|
+
unbound API calls, coverage bypass. Answers *"is the map self-consistent?"*
|
|
176
|
+
* **`verify`** (live) — emits a read-only probe (navigate + snapshot +
|
|
177
|
+
assert-each-element-resolves; no mutating clicks) the agent runs against the
|
|
178
|
+
app. Answers *"does the map still match the running UI?"* `--drive` walks a
|
|
179
|
+
full flow but **refuses `risk.gated` steps** unless `--allow-gated`.
|
|
180
|
+
|
|
181
|
+
Lint codes: `E_DANGLING_ELEMENT`, `E_BROKEN_EDGE`, `E_SELECTOR_AMBIGUOUS`,
|
|
182
|
+
`E_PARAM_UNDECLARED`, `E_SUBFLOW_CYCLE`, `E_CAPTURE_COLLISION`,
|
|
183
|
+
`E_API_CALL_UNBOUND`, `W_NO_STEPS`, `W_UNREACHABLE`, `W_NO_CAPTURE`,
|
|
184
|
+
`W_DUPLICATE_RECIPE`, `W_UI_COVERAGE_BYPASS`, `W_UNMET_REQUIRES`.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Development
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
uv sync # installs the project + the dev dependency group
|
|
192
|
+
uv run pytest -q
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Architecture
|
|
196
|
+
|
|
197
|
+
The package is split into four layers under `src/uipilot/`, with dependencies
|
|
198
|
+
pointing strictly **inward** (`presentation → application → {domain,
|
|
199
|
+
infrastructure} → domain`):
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
domain/ pure model + business rules (no I/O, no frameworks)
|
|
203
|
+
model entities: App, Element, Action, Flow, Step, Param, Capture, Selector, Pack
|
|
204
|
+
templating RuntimeContext + {{token}} resolution
|
|
205
|
+
flows subflow expansion (single source of truth)
|
|
206
|
+
graph BFS pathfinding + reachability
|
|
207
|
+
validation the static linter (E_/W_ codes)
|
|
208
|
+
compiler flow → CompiledScript
|
|
209
|
+
verification read-only drift-probe builder
|
|
210
|
+
usage reverse index
|
|
211
|
+
infrastructure/ I/O and external concerns
|
|
212
|
+
pack_loader YAML → domain model
|
|
213
|
+
capabilities dynamic import of pack auth adapters
|
|
214
|
+
markdown_importer one-shot MD → seed YAML
|
|
215
|
+
application/ use-case orchestration
|
|
216
|
+
service open_pack + compile/validate/verify/route/uses/… wiring
|
|
217
|
+
presentation/ the CLI and output renderers
|
|
218
|
+
cli Typer entrypoint (talks only to application)
|
|
219
|
+
renderers json / playwright-mcp / steps / pw-test / pw-pom
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
The domain layer imports nothing outside the standard library, so the business
|
|
223
|
+
rules are testable with no YAML, filesystem, or CLI in the loop.
|
|
224
|
+
|
|
225
|
+
## License
|
|
226
|
+
|
|
227
|
+
MIT.
|
uipilot-0.1.0/README.md
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# uipilot
|
|
2
|
+
|
|
3
|
+
**App-agnostic engine that models a web UI as a graph and compiles flows into
|
|
4
|
+
Playwright-MCP-executable scripts for agents.**
|
|
5
|
+
|
|
6
|
+
An agent driving a web UI live pays, per element: snapshot → reason about intent
|
|
7
|
+
→ find the ref → act. `uipilot` turns that into a lookup. You describe your app's
|
|
8
|
+
elements, actions, and flows once as structured YAML (a **pack**); the engine
|
|
9
|
+
queries that graph and emits a step-by-step Playwright-MCP script with selectors
|
|
10
|
+
pre-resolved and page snapshots inserted only where the DOM actually changes.
|
|
11
|
+
|
|
12
|
+
The engine ships **no domain vocabulary** — no "login", "cart", "tenant". It
|
|
13
|
+
knows only generic concepts (**app, element, action, flow, step, param,
|
|
14
|
+
capture, capability**). Everything project-specific lives in a pack, so porting
|
|
15
|
+
to a new web app means authoring a new pack, not touching the engine.
|
|
16
|
+
|
|
17
|
+
> **The engine executes nothing.** Every command *emits structured data*; the
|
|
18
|
+
> agent owns the browser and runs it. That keeps `uipilot` safe to point at
|
|
19
|
+
> money-moving flows.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Install & set up in your project
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
uv tool install uipilot # install the CLI globally
|
|
27
|
+
cd your-project
|
|
28
|
+
uipilot init # scaffold a pack + agent instructions here
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`uipilot init` writes a minimal, valid pack skeleton (`flowmap.config.yaml`,
|
|
32
|
+
`data/`, `capabilities.py`) plus an agent instruction file — a Claude Code skill
|
|
33
|
+
(`.claude/skills/uipilot/SKILL.md`) and/or an `AGENTS.md` section (`--agent
|
|
34
|
+
claude|agents`, repeatable). Then just **ask your agent to run a flow**: it
|
|
35
|
+
explores the app, fills the pack, and drives it for you. Re-running `init`
|
|
36
|
+
refreshes the skill but never clobbers a pack you've started (use `--force` to
|
|
37
|
+
overwrite).
|
|
38
|
+
|
|
39
|
+
Because `init` scaffolds into the cwd, every later `uipilot …` call finds the
|
|
40
|
+
pack with **no `--pack` flag** (resolution order: `$UIPILOT_PACK` → a
|
|
41
|
+
`flowmap.config.yaml` in the cwd → the bundled `examples/demo` pack).
|
|
42
|
+
|
|
43
|
+
### From source
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
uv sync # creates .venv, installs from uv.lock
|
|
47
|
+
uv run uipilot --pack examples/demo apps
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Built with the `uv_build` backend; dependencies are locked in `uv.lock`.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## The 60-second tour
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# what apps/flows exist?
|
|
58
|
+
uipilot --pack examples/demo --format table apps
|
|
59
|
+
uipilot --pack examples/demo flows
|
|
60
|
+
|
|
61
|
+
# compile a flow into an executable Playwright-MCP script
|
|
62
|
+
uipilot --pack examples/demo script --flow create_project_with_credential
|
|
63
|
+
|
|
64
|
+
# find a route through the UI graph
|
|
65
|
+
uipilot --pack examples/demo path --from act_cs_view_dashboard --to act_cs_create_credential
|
|
66
|
+
|
|
67
|
+
# what breaks if I edit this button? (change blast radius)
|
|
68
|
+
uipilot --pack examples/demo uses cs_btn_create_project_submit
|
|
69
|
+
|
|
70
|
+
# is the map self-consistent? (CI gate)
|
|
71
|
+
uipilot --pack examples/demo validate
|
|
72
|
+
|
|
73
|
+
# does the map still match the running app? (read-only live probe)
|
|
74
|
+
uipilot --pack examples/demo verify --flow create_project_with_credential
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Pack layout
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
your_pack/
|
|
83
|
+
├── flowmap.config.yaml # binds apps, tokens, risk taxonomy, capabilities
|
|
84
|
+
├── capabilities.py # (optional) named auth adapters the engine calls by key
|
|
85
|
+
└── data/
|
|
86
|
+
├── <app>.app.yaml # one per app: app header + elements + actions
|
|
87
|
+
└── flows.yaml # named multi-app flows (+ shared/API actions)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
* **Element** — a node with a structured selector (`role(name)` > `label` >
|
|
91
|
+
`text` > `css`) and a purpose. Never a raw locator string, so it re-emits to a
|
|
92
|
+
Playwright-MCP element description *or* a `@playwright/test` locator.
|
|
93
|
+
* **Action** — the graph proper. A UI action carries a `route`, an ordered
|
|
94
|
+
`steps` recipe, `prev`/`next` edges, `params`, and `captures`. An **API
|
|
95
|
+
action** (`transport: api`) binds to an existing test-framework factory via
|
|
96
|
+
`call:` for fast provisioning or backend cross-checks — it has no `prev`/`next`
|
|
97
|
+
and is excluded from pathfinding.
|
|
98
|
+
* **Flow** — an ordered `path` of action ids. Entries can be a bare id, a
|
|
99
|
+
subflow (`use:`), or an aliased repeat (`{action, as, params}`). Reuse is
|
|
100
|
+
layered L1–L4 (shared actions → subflows → aliased invocations → guards).
|
|
101
|
+
* **Capture** — a value one action produces (`from: url|element|response|…`)
|
|
102
|
+
that later steps consume as `{{captured.x}}` / `{{alias.x}}`, bridging UI and
|
|
103
|
+
API transports unchanged.
|
|
104
|
+
|
|
105
|
+
See [`examples/demo/`](examples/demo/) for a complete two-app pack exercising
|
|
106
|
+
every feature.
|
|
107
|
+
|
|
108
|
+
**Docs:** [`src/uipilot/templates/skill.md`](src/uipilot/templates/skill.md) — the
|
|
109
|
+
agent guide, shipped and installed by `uipilot init` (core loop, execution
|
|
110
|
+
contract, safety) · [`docs/USE_CASES.md`](docs/USE_CASES.md) — business use cases
|
|
111
|
+
at a glance · [`docs/PACK_AUTHORING.md`](docs/PACK_AUTHORING.md) — pack field
|
|
112
|
+
reference and porting checklist.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Commands
|
|
117
|
+
|
|
118
|
+
| Command | Purpose |
|
|
119
|
+
|---|---|
|
|
120
|
+
| `apps` | List apps, base URLs, auth entry flows |
|
|
121
|
+
| `actions` | List/filter actions (`--app --risk --grep --section --transport`) |
|
|
122
|
+
| `elements` | List/filter elements + resolved selectors |
|
|
123
|
+
| `show action <id>` / `show element <id>` | Full detail, selectors resolved inline |
|
|
124
|
+
| `uses <id>` | Reverse index — change blast radius before you edit |
|
|
125
|
+
| `flows` / `flow <name> [--params]` | List / show named flows (`--params`: aggregated param manifest only) |
|
|
126
|
+
| `path --from <a> --to <b>` | BFS a route through the flow graph |
|
|
127
|
+
| `script …` | **Emit an executable Playwright-MCP script** (see below) |
|
|
128
|
+
| `validate` | Lint the model statically (CI gate) |
|
|
129
|
+
| `verify …` | Emit a read-only probe to detect live UI drift |
|
|
130
|
+
| `emit --format pw-pom\|pw-test` | Generate Python POM classes / a pw-test spec |
|
|
131
|
+
| `capabilities [--check]` | List (and import-check) the pack's auth adapters |
|
|
132
|
+
| `init [dir] [--agent claude\|agents] [--force]` | Scaffold a pack + agent instructions in a project |
|
|
133
|
+
| `import-md <file> --out <dir>` | One-time: seed a pack from a retired Markdown map |
|
|
134
|
+
|
|
135
|
+
### `script`
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
uipilot script (--flow <name> | --from <a> --to <b> | --actions a,b,c)
|
|
139
|
+
[--params file.json] [--set key=value ...]
|
|
140
|
+
[--skip-auth] [--batch] [--refuse-destructive]
|
|
141
|
+
[--format playwright-mcp|steps|json|pw-test|human]
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
* **`--format human`** emits a plain-English preview (numbered steps, params,
|
|
145
|
+
risk, teardown) for a person to review before an agent runs a risky flow.
|
|
146
|
+
|
|
147
|
+
* **Param resolution:** `--set` > `--params` > model defaults. Defaults expand
|
|
148
|
+
pack tokens (`{{prefix}}`, `{{seq}}`). Unresolved **required** params are
|
|
149
|
+
emitted as `{{placeholder}}` and listed under `params_required`.
|
|
150
|
+
* **Secrets** (`type: secret`) never appear in the header echo — only in the one
|
|
151
|
+
step that consumes them.
|
|
152
|
+
* **`--refuse-destructive`** refuses to emit if any action on the path carries a
|
|
153
|
+
`risk` in the pack's `risk.gated` set. The header always surfaces `risk_max`.
|
|
154
|
+
* **`--batch`** collapses consecutive form fills into one `browser_fill_form`.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## `validate` vs `verify`
|
|
159
|
+
|
|
160
|
+
* **`validate`** (offline, CI-gating) — internal consistency only: dangling
|
|
161
|
+
refs, broken edges, undeclared params, capture collisions, unreachable nodes,
|
|
162
|
+
unbound API calls, coverage bypass. Answers *"is the map self-consistent?"*
|
|
163
|
+
* **`verify`** (live) — emits a read-only probe (navigate + snapshot +
|
|
164
|
+
assert-each-element-resolves; no mutating clicks) the agent runs against the
|
|
165
|
+
app. Answers *"does the map still match the running UI?"* `--drive` walks a
|
|
166
|
+
full flow but **refuses `risk.gated` steps** unless `--allow-gated`.
|
|
167
|
+
|
|
168
|
+
Lint codes: `E_DANGLING_ELEMENT`, `E_BROKEN_EDGE`, `E_SELECTOR_AMBIGUOUS`,
|
|
169
|
+
`E_PARAM_UNDECLARED`, `E_SUBFLOW_CYCLE`, `E_CAPTURE_COLLISION`,
|
|
170
|
+
`E_API_CALL_UNBOUND`, `W_NO_STEPS`, `W_UNREACHABLE`, `W_NO_CAPTURE`,
|
|
171
|
+
`W_DUPLICATE_RECIPE`, `W_UI_COVERAGE_BYPASS`, `W_UNMET_REQUIRES`.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Development
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
uv sync # installs the project + the dev dependency group
|
|
179
|
+
uv run pytest -q
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Architecture
|
|
183
|
+
|
|
184
|
+
The package is split into four layers under `src/uipilot/`, with dependencies
|
|
185
|
+
pointing strictly **inward** (`presentation → application → {domain,
|
|
186
|
+
infrastructure} → domain`):
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
domain/ pure model + business rules (no I/O, no frameworks)
|
|
190
|
+
model entities: App, Element, Action, Flow, Step, Param, Capture, Selector, Pack
|
|
191
|
+
templating RuntimeContext + {{token}} resolution
|
|
192
|
+
flows subflow expansion (single source of truth)
|
|
193
|
+
graph BFS pathfinding + reachability
|
|
194
|
+
validation the static linter (E_/W_ codes)
|
|
195
|
+
compiler flow → CompiledScript
|
|
196
|
+
verification read-only drift-probe builder
|
|
197
|
+
usage reverse index
|
|
198
|
+
infrastructure/ I/O and external concerns
|
|
199
|
+
pack_loader YAML → domain model
|
|
200
|
+
capabilities dynamic import of pack auth adapters
|
|
201
|
+
markdown_importer one-shot MD → seed YAML
|
|
202
|
+
application/ use-case orchestration
|
|
203
|
+
service open_pack + compile/validate/verify/route/uses/… wiring
|
|
204
|
+
presentation/ the CLI and output renderers
|
|
205
|
+
cli Typer entrypoint (talks only to application)
|
|
206
|
+
renderers json / playwright-mcp / steps / pw-test / pw-pom
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
The domain layer imports nothing outside the standard library, so the business
|
|
210
|
+
rules are testable with no YAML, filesystem, or CLI in the loop.
|
|
211
|
+
|
|
212
|
+
## License
|
|
213
|
+
|
|
214
|
+
MIT.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "uipilot"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "App-agnostic engine that models a web UI as a graph and compiles flows into Playwright-MCP-executable scripts for agents."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
license = { text = "MIT" }
|
|
8
|
+
authors = [{ name = "Sergei Konovalov" }]
|
|
9
|
+
keywords = ["playwright", "mcp", "ui-testing", "automation", "agents", "web-ui"]
|
|
10
|
+
dependencies = [
|
|
11
|
+
"typer>=0.12",
|
|
12
|
+
"rich>=13.7",
|
|
13
|
+
"pyyaml>=6.0",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[project.scripts]
|
|
17
|
+
uipilot = "uipilot.presentation.cli:app"
|
|
18
|
+
|
|
19
|
+
[dependency-groups]
|
|
20
|
+
dev = [
|
|
21
|
+
"pytest>=9.1.1",
|
|
22
|
+
"pytest-cov>=5.0",
|
|
23
|
+
"ruff>=0.6",
|
|
24
|
+
"ty>=0.0.1a1",
|
|
25
|
+
"vulture>=2.11",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[build-system]
|
|
29
|
+
requires = ["uv_build>=0.11.0,<0.12.0"]
|
|
30
|
+
build-backend = "uv_build"
|
|
31
|
+
|
|
32
|
+
[tool.uv.build-backend]
|
|
33
|
+
module-name = "uipilot"
|
|
34
|
+
module-root = "src"
|
|
35
|
+
|
|
36
|
+
[tool.pytest.ini_options]
|
|
37
|
+
testpaths = ["tests"]
|
|
38
|
+
addopts = "-ra --cov=uipilot --cov-report=term-missing --cov-fail-under=90"
|
|
39
|
+
|
|
40
|
+
[tool.coverage.run]
|
|
41
|
+
# Templates are inert scaffolding shipped as package data, not code under test.
|
|
42
|
+
omit = ["*/uipilot/templates/*"]
|
|
43
|
+
|
|
44
|
+
# --- Static analysis -------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
[tool.ruff]
|
|
47
|
+
line-length = 100
|
|
48
|
+
target-version = "py312"
|
|
49
|
+
src = ["src", "tests"]
|
|
50
|
+
|
|
51
|
+
[tool.ruff.lint]
|
|
52
|
+
# pyflakes (F), pycodestyle (E/W), isort (I), bugbear (B), pyupgrade (UP),
|
|
53
|
+
# comprehensions (C4).
|
|
54
|
+
select = ["E", "W", "F", "I", "B", "UP", "C4"]
|
|
55
|
+
ignore = [
|
|
56
|
+
"E501", # line length — long strings/tables are readable as-is
|
|
57
|
+
"UP045", # Optional[X] over X | None — deliberate style with __future__ annotations
|
|
58
|
+
"UP007", # Optional[X] / Union — same deliberate style
|
|
59
|
+
"B008", # function-call-in-default — this IS the Typer Option() idiom
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
[tool.ruff.lint.per-file-ignores]
|
|
63
|
+
"tests/*" = ["B011", "B905"]
|
|
64
|
+
|
|
65
|
+
[tool.ty.environment]
|
|
66
|
+
python-version = "3.12"
|
|
67
|
+
|
|
68
|
+
[tool.ty.src]
|
|
69
|
+
include = ["src"]
|
|
70
|
+
|
|
71
|
+
[tool.vulture]
|
|
72
|
+
paths = ["src", "vulture_whitelist.py"]
|
|
73
|
+
min_confidence = 60
|
|
74
|
+
sort_by_size = true
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# uipilot — app-agnostic UI-flow engine.
|
|
2
|
+
#
|
|
3
|
+
# Loads a per-app *pack* (structured YAML + a small config), walks the UI flow
|
|
4
|
+
# graph, substitutes params, and emits Playwright-MCP-executable scripts. The
|
|
5
|
+
# engine ships no domain vocabulary: everything project-specific (apps, elements,
|
|
6
|
+
# actions, flows, risk taxonomy, auth capabilities) lives in a pack.
|
|
7
|
+
#
|
|
8
|
+
# The code is split into four layers, with dependencies pointing strictly inward
|
|
9
|
+
# (presentation -> application -> {domain, infrastructure} -> domain):
|
|
10
|
+
#
|
|
11
|
+
# domain/ pure model + business rules (no I/O, no frameworks)
|
|
12
|
+
# infrastructure/ YAML loading, dynamic imports, file I/O
|
|
13
|
+
# application/ use-case orchestration
|
|
14
|
+
# presentation/ the `uipilot` CLI and output renderers
|
|
15
|
+
#
|
|
16
|
+
# There is intentionally no code here: this file only marks the directory as a
|
|
17
|
+
# package. Import concrete symbols from their owning module, e.g.
|
|
18
|
+
# from uipilot.infrastructure.pack_loader import load_pack
|
|
19
|
+
# from uipilot.application.service import open_pack
|
|
20
|
+
# from uipilot.domain.model import Action, Flow, Selector
|
|
21
|
+
#
|
|
22
|
+
# The distribution version is declared in pyproject.toml (single source).
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Application layer — use-case orchestration.
|
|
2
|
+
#
|
|
3
|
+
# Wires infrastructure (loading, importing, capability resolution) to domain
|
|
4
|
+
# services (compile, validate, verify, pathfind, usage) and returns domain
|
|
5
|
+
# objects or plain data — never rendered strings. Imported by presentation;
|
|
6
|
+
# imports domain + infrastructure.
|
|
7
|
+
#
|
|
8
|
+
# Components:
|
|
9
|
+
# service PackContext plus one function per use case: open_pack,
|
|
10
|
+
# compile_script, validate_pack, verify, route, uses,
|
|
11
|
+
# filter_actions/filter_elements, list_capabilities, import_markdown.
|