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.
Files changed (30) hide show
  1. uipilot-0.1.0/PKG-INFO +227 -0
  2. uipilot-0.1.0/README.md +214 -0
  3. uipilot-0.1.0/pyproject.toml +74 -0
  4. uipilot-0.1.0/src/uipilot/__init__.py +22 -0
  5. uipilot-0.1.0/src/uipilot/application/__init__.py +11 -0
  6. uipilot-0.1.0/src/uipilot/application/service.py +220 -0
  7. uipilot-0.1.0/src/uipilot/domain/__init__.py +17 -0
  8. uipilot-0.1.0/src/uipilot/domain/compiler.py +520 -0
  9. uipilot-0.1.0/src/uipilot/domain/errors.py +32 -0
  10. uipilot-0.1.0/src/uipilot/domain/flows.py +58 -0
  11. uipilot-0.1.0/src/uipilot/domain/graph.py +115 -0
  12. uipilot-0.1.0/src/uipilot/domain/model.py +362 -0
  13. uipilot-0.1.0/src/uipilot/domain/templating.py +101 -0
  14. uipilot-0.1.0/src/uipilot/domain/usage.py +68 -0
  15. uipilot-0.1.0/src/uipilot/domain/validation.py +324 -0
  16. uipilot-0.1.0/src/uipilot/domain/verification.py +140 -0
  17. uipilot-0.1.0/src/uipilot/infrastructure/__init__.py +14 -0
  18. uipilot-0.1.0/src/uipilot/infrastructure/capabilities.py +103 -0
  19. uipilot-0.1.0/src/uipilot/infrastructure/markdown_importer.py +171 -0
  20. uipilot-0.1.0/src/uipilot/infrastructure/pack_loader.py +334 -0
  21. uipilot-0.1.0/src/uipilot/infrastructure/scaffold.py +122 -0
  22. uipilot-0.1.0/src/uipilot/presentation/__init__.py +11 -0
  23. uipilot-0.1.0/src/uipilot/presentation/cli.py +570 -0
  24. uipilot-0.1.0/src/uipilot/presentation/renderers.py +311 -0
  25. uipilot-0.1.0/src/uipilot/templates/__init__.py +0 -0
  26. uipilot-0.1.0/src/uipilot/templates/capabilities.py +18 -0
  27. uipilot-0.1.0/src/uipilot/templates/data/app.app.yaml +15 -0
  28. uipilot-0.1.0/src/uipilot/templates/data/flows.yaml +3 -0
  29. uipilot-0.1.0/src/uipilot/templates/flowmap.config.yaml +14 -0
  30. 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.
@@ -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.