Flow2Skill
Watch the 50-second narrated proof · Studio screenshot · Replay evidence
Browser agents improvise. Flow2Skill preserves what already worked.
Flow2Skill is a local-first compiler for demonstrated browser workflows. Record a successful flow with Playwright codegen. Flow2Skill turns it into an inspectable contract, a portable agent skill, and an executable regression test.
human demonstration → strict AST compiler → protected workflow → agent skill + pytest proof
It exports:
flow.jsonandflow.yaml: fingerprinted workflow contracts;SKILL.md: reviewable instructions for Hermes, Codex, Claude Code, Cursor, and other agents;test_<workflow>.py: a standalone Playwright/pytest proof;README.md: run instructions for the generated bundle.
Flow2Skill is not an autonomous browser agent. It preserves a workflow that already worked.
Why use it
Browser agents are useful when the path is unknown. They are expensive and inconsistent when the path is already known. Flow2Skill converts a successful run into a reusable asset with stable selectors, protected inputs, explicit review gates, and mechanical success criteria.
- Local-first: Studio only binds to loopback.
- Parse, never execute: recordings are inspected through Python's AST and are never imported or run.
- Protected by default: typed values and text/value assertions become environment variables.
- Echo-aware: a protected input repeated in later selector or result text is replaced with the same runtime variable.
- Fail closed: dynamic values, unsupported locator operations, control flow, multiple tests, missing assertions, unknown selector modifiers, and unsupported calls stop compilation.
- Review before mutation: all clicks, key presses, fills, selections, and check operations are review-gated. Publish, send, buy, submit, delete, and similar actions are approval-gated.
- Proof required: a workflow without an executable assertion is rejected.
- Portable: the generated pytest does not depend on Flow2Skill at runtime.
Install
Prerequisites:
- Python 3.10–3.13;
- Node.js with
npx, required only for recording; - Chromium installed through Playwright, required for default replay.
python -m pip install "flow2skill[test]"
python -m playwright install chromium
flow2skill doctor
flow2skill studio
The test extra installs pytest for the standalone regression proofs. For compiling,
Studio, or CLI replay without pytest, install the base flow2skill package.
From a source checkout:
python -m venv .venv
# Windows
.venv\Scripts\python.exe -m pip install -e ".[dev]"
.venv\Scripts\python.exe -m playwright install chromium
.venv\Scripts\flow2skill.exe doctor
.venv\Scripts\flow2skill.exe studio
Windows source checkouts also include launch-flow2skill.bat. Installed wheels use the flow2skill console command; the batch launcher is not installed by the wheel.
Recording uses Playwright-managed Chromium by default, matching doctor and the browser
installation above. To use a separately installed Google Chrome instead, pass --channel chrome
to flow2skill record. Studio uses managed Chromium.
Studio opens at http://127.0.0.1:8765 and writes bundles to ~/Flow2SkillWorkspaces unless another root is supplied.
Diagnose setup
flow2skill doctor
flow2skill doctor --mode compile
flow2skill doctor --mode record
flow2skill doctor --mode replay --json
The default checks recording and replay prerequisites. Compile mode does not require
Node.js or a browser; replay mode does not require Node.js. Each failed check includes
a repair instruction. Missing pytest is a warning for exported proofs, because CLI
replay itself does not use pytest. Exit code 2 means a required prerequisite is
missing or mismatched; 0 means the selected prerequisite checks passed.
Doctor inspects installation state without launching a browser. Run the demo proof to verify real browser execution; a successful setup check is not workflow evidence.
Thirty-second proof
Generate an entirely local sample bundle:
flow2skill demo --out ./flow2skill-demo
Run it against Flow2Skill's packaged local fixture:
# macOS/Linux
F2S_LABEL_API_TOKEN_1="runtime-demo-token" \
FLOW2SKILL_LIVE=1 \
FLOW2SKILL_ALLOW_SIDE_EFFECTS=1 \
python -m pytest -q ./flow2skill-demo/test_agent_release_gate.py
Windows Command Prompt:
set F2S_LABEL_API_TOKEN_1=runtime-demo-token
set FLOW2SKILL_LIVE=1
set FLOW2SKILL_ALLOW_SIDE_EFFECTS=1
python -m pytest -q flow2skill-demo\test_agent_release_gate.py
The side-effect flag is required because the proof fills a synthetic API-token field and clicks a validation button, even though the packaged fixture is local and harmless. The captured synthetic value is absent from the generated bundle.
Check that a regression is detected
The scoped trial runner
uses a local page with two Save buttons. With Flow2Skill 0.2.0 and Chromium installed,
save the script in a writable folder and run python scoped_trial.py. It compiles a
synthetic recording, requires a passing standalone test, changes the page's success
message and requires a failure, then restores the page and requires a pass again.
It retains the output in trial-output/evidence.txt and never opens an external site.
The script is also included in the source archive under examples/.
The recording is hand-written. This checks scope preservation and regression detection;
it does not verify interactive Inspector recording. The runner overwrites its own
trial-output files on reruns. Review the script before enabling execution on your machine.
Record a workflow
flow2skill record https://example.com ^
--name "Documentation search" ^
--success-text "Results"
Complete the flow in Playwright Inspector, capture an assertion or provide --success-text, and close the recorder window. Flow2Skill compiles the capture and deletes its temporary raw codegen and recorder log.
Recording uses the pinned Playwright codegen version declared by this package. flow2skill doctor verifies version alignment and prerequisites.
Compile existing codegen
flow2skill compile recording.py \
--name "Documentation search" \
--intent "Search the docs and prove results appear." \
--out ./compiled-flow
Protection is enabled by default. --no-protect-inputs preserves non-sensitive literal values and should only be used for known-public test data. Sensitive fields remain protected.
Input-file contract: flow2skill compile never deletes the file you explicitly provide. Playwright codegen files can contain credentials or typed data; store and remove that source according to your own retention policy.
Inspect and replay
Dry-run without opening a browser:
flow2skill inspect ./compiled-flow/flow.json
Run the generated standalone proof:
FLOW2SKILL_LIVE=1 pytest -q ./compiled-flow/test_documentation_search.py
Live CLI replay and exported tests check all protected runtime variables before opening the browser. Missing variables are reported together; an explicitly empty value is allowed. Dry-run inspection does not require values.
Use FLOW2SKILL_HEADED=1 to watch. Any workflow containing review or approval actions also requires FLOW2SKILL_ALLOW_SIDE_EFFECTS=1. Set it only after reviewing the exact generated plan. Use FLOW2SKILL_CHANNEL=chrome to replay with an installed Chrome channel instead of Playwright's managed Chromium.
Supported capture surface
- one synchronous pytest-style test function;
- the exact session fixture emitted by
--block-service-workers; other context fixtures are rejected; - direct Playwright call statements only;
page.goto;- role, label, placeholder, text, test-id, title, alt-text, and CSS selectors, including direct chains scoped inside another locator;
.firstand nonnegative integer.nth(...)modifiers on element locators;- locator
click,fill,press,select_option,check,uncheck, andhover; expect(...).to_be_visible();- exact
to_have_text()and substringto_contain_text()assertions; expect(page).to_have_url();expect(...).to_have_value().
Recorder, CLI replay, and exported proofs block service workers consistently. Arbitrary
context options and user fixture code are not executed. Navigation and URL assertions
require page; element actions and assertions require locators. Test-id and CSS
selectors do not accept exact. Invalid targets and options stop compilation.
For example, this keeps both the dialog scope and the selected row:
page.get_by_role("dialog", name="Profile").first.locator(".row").nth(1).get_by_role("button", name="Save").click()
Scope is retained in the manifest, skill instructions, replay, and standalone test. Protected values and approval classification include parent selectors. New manifests use schema 1.1; schema 1.0 manifests retain their original fingerprints when loaded and exported. Older Flow2Skill versions cannot read schema 1.1 manifests, but exported Python tests remain standalone. Locator filters, frame locators, and dynamic scopes are still unsupported.
Unsupported calls, dynamic expressions, assignments, loops, branches, context managers, nested functions, and multiple tests are rejected. They are never flattened or silently omitted.
Security and privacy model
Recording boundary
During live recording, Playwright briefly writes raw Python to a hidden workspace file. Flow2Skill deletes this file and the recorder log on completion, cancellation, launch failure, or compile failure. On startup it removes abandoned raw captures older than one hour. A hard power loss can therefore leave a temporary file until the next cleanup window. Do not record real production credentials when synthetic test credentials are available.
Manifest integrity
flow.json carries a SHA-256 fingerprint. Loading rejects unknown fields, malformed selector types, missing fingerprints, content tampering, stale slugs, and risk-classification downgrades. The fingerprint detects accidental or local modification; it is not a cryptographic signature from a trusted publisher.
Runtime variables are restricted to the compiler-owned F2S_* namespace. Captured ${VARIABLE} syntax is rejected instead of being allowed to read arbitrary host environment variables. Navigation containing protected values is approval-gated because it can transmit the value to the destination.
Common credential-bearing URL parameters, including JWT, token, signature, session, authentication, and password fields, are replaced with runtime variables. Recompiling into an existing managed bundle directory removes obsolete generated test_*.py files so stale executable proofs cannot survive unnoticed.
Studio boundary
Studio enforces:
- loopback-only binding;
- strict local
Hostvalidation against DNS rebinding; - same-origin checks;
- a random per-process request token;
- a one-megabyte JSON body limit;
- strict request field types;
- workspace and artifact path containment;
- no-store and browser hardening headers.
Approval boundary
Flow2Skill compiles instructions; it does not grant permission. Generated skills call out approval-gated actions. Generated tests are disabled unless FLOW2SKILL_LIVE=1 is present. Once live execution is requested, a missing side-effect flag fails the test rather than reporting a successful skip.
See SECURITY.md for the threat model and reporting process.
Development
python -m pip install -e ".[dev]"
ruff format --check src tests scripts
ruff check src tests scripts
PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 pytest -q
python -m build
python -m twine check dist/*
The release gate also installs the wheel into a clean environment, runs the packaged demo against a real browser, checks source/wheel contents, and scans tracked files for high-confidence secrets and private paths.
Intentional boundaries
Flow2Skill does not include cloud sync, a browser extension, shared secret storage, autonomous planning, or LLM selector healing. Those features create a platform before the deterministic compiler has earned it. The current product does one job: preserve a successful browser workflow as a reviewable procedure and executable proof.
License
MIT
Bundle recovery
An export replaces the generated files in its output directory; use a different output
directory to retain a separate version. All five artifacts are rendered and staged
before replacement. A failed replacement attempts to restore the previous files,
including its generated test. Unrelated files are preserved. Exports to the same
directory are serialized with .flow2skill-write.lock; wait for the current export
to finish before reading or executing the bundle. The set of files is not an atomic
directory snapshot for concurrent readers.
A killed process or power loss can leave the lock and a .flow2skill-stage-* directory.
Do not remove a lock while an export is running. Check the PID recorded in it and
confirm that export has stopped. Keep a copy of the whole output directory before
recovery. Staging old/ contains backups of files that existed before replacement;
new/ contains any staged files that were not yet published. Restore the old files
and remove newly introduced generated files, or regenerate the bundle into a fresh
directory from the original recording. Do not execute a mixed or unverified bundle.
Remove the stale lock only after recovery; retained staging directories can then be
removed. Incomplete rollback reports its recovery directory and keeps the lock.
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 flow2skill-0.2.0.tar.gz.
File metadata
- Download URL: flow2skill-0.2.0.tar.gz
- Upload date:
- Size: 807.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c049ecd83b1b03744a8044aec8ac9a293394f0a05f2ed1cca8413a9c03ca2741
|
|
| MD5 |
f439816e263d4c14602caf3cd993bc9a
|
|
| BLAKE2b-256 |
b608153a2c93a77a056ff016e51f4823fca994aabcbb3fae5a9ad64e8b198cb0
|
Provenance
The following attestation bundles were made for flow2skill-0.2.0.tar.gz:
Publisher:
release.yml on godhiraj-code/flow2skill
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
flow2skill-0.2.0.tar.gz -
Subject digest:
c049ecd83b1b03744a8044aec8ac9a293394f0a05f2ed1cca8413a9c03ca2741 - Sigstore transparency entry: 2736522103
- Sigstore integration time:
-
Permalink:
godhiraj-code/flow2skill@a88b15683e7fcff09c19f3f6025a3b4f32d6ba63 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/godhiraj-code
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a88b15683e7fcff09c19f3f6025a3b4f32d6ba63 -
Trigger Event:
release
-
Statement type:
File details
Details for the file flow2skill-0.2.0-py3-none-any.whl.
File metadata
- Download URL: flow2skill-0.2.0-py3-none-any.whl
- Upload date:
- Size: 48.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6db1c9cd0e045239a3ef981a45b9a95ed35e25a5cabd384fbc7ee8614ad8922a
|
|
| MD5 |
a0788aac4b8a45915715c7c87ae91445
|
|
| BLAKE2b-256 |
007ef5fb2ef2bf4b18a60cb7ee90994a3d38250bc2fedb8f3d654884aec18cf6
|
Provenance
The following attestation bundles were made for flow2skill-0.2.0-py3-none-any.whl:
Publisher:
release.yml on godhiraj-code/flow2skill
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
flow2skill-0.2.0-py3-none-any.whl -
Subject digest:
6db1c9cd0e045239a3ef981a45b9a95ed35e25a5cabd384fbc7ee8614ad8922a - Sigstore transparency entry: 2736522444
- Sigstore integration time:
-
Permalink:
godhiraj-code/flow2skill@a88b15683e7fcff09c19f3f6025a3b4f32d6ba63 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/godhiraj-code
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a88b15683e7fcff09c19f3f6025a3b4f32d6ba63 -
Trigger Event:
release
-
Statement type: