Skip to main content

Deterministic DSL-first browser automation platform powered by Playwright heuristics with optional local AI self-healing (Ollama)

Project description

ManulEngine mascot

ManulEngine

PyPI PyPI Downloads Manul Engine Extension Manul Engine Extension (Open VSX) MCP Server MCP Server (Open VSX) ManulAI Local Agent Status: Alpha

Write browser automation in plain English. ManulEngine interprets .hunt files through deterministic DOM heuristics on top of Playwright — no selectors, no cloud APIs, no AI required.

Status: Alpha. Solo-developed, actively battle-tested. Bugs are expected, APIs may evolve, and there are no promises about stability or production readiness. The core claim is transparency: when a step works, you can see exactly why; when it fails, you get the scoring breakdown to diagnose it.

📖 Full Documentation: Overview · Installation · Getting Started · DSL Syntax · Reports & Explainability · Integration


Syntax First

ManulEngine runs .hunt files — plain-English automation scripts that read like manual QA steps. Here is the DSL in action.

A complete flow

@context: Smoke test for a login page
@title: Login Smoke
@var: {email} = admin@example.com
@var: {password} = secret123

STEP 1: Open the app
    NAVIGATE to https://example.com/login
    VERIFY that 'Sign In' is present

STEP 2: Authenticate
    FILL 'Email' field with '{email}'
    VERIFY "Email" field has value "{email}"
    FILL 'Password' field with '{password}'
    CLICK the 'Sign In' button
    VERIFY that 'Dashboard' is present

DONE.

Run it:

manul path/to/login.hunt

Every @var: is declared up front — never hardcode test data inside steps. VERIFY confirms state after every significant action. DONE. closes the mission.

Case-insensitive keywords. All DSL keywords are case-insensitive at runtime — CLICK, Click, and click all work. The canonical form used in documentation and generated files is ALL UPPERCASE.

Element type hints are optional. Words like button, link, field, dropdown placed after the target outside quotes are not required, but they provide a strong heuristic signal that boosts scoring accuracy. CLICK the 'Login' button and CLICK the 'Login' both work — the former is more precise.

Conditional branching

Branch test logic with IF / ELIF / ELSE based on what the page actually contains. Nesting is supported.

STEP 1: Adaptive login
    IF button 'SSO Login' exists:
        CLICK the 'SSO Login' button
        VERIFY that 'SSO Portal' is present
    ELIF text 'Sign In' is present:
        FILL 'Username' field with '{username}'
        CLICK the 'Sign In' button
    ELSE:
        CLICK the 'Create Account' link

Conditions can check element existence, visible text, variable equality, substring containment, or simple truthiness — all evaluated against the live page.

Loops

Repeat actions with REPEAT, iterate data with FOR EACH, or poll dynamic state with WHILE. Loops nest freely with conditionals.

@var: {products} = Laptop, Headphones, Mouse

STEP 1: Add products to cart
    FOR EACH {product} IN {products}:
        FILL 'Search' field with '{product}'
        PRESS Enter
        CLICK the 'Add to Cart' button NEAR '{product}'
        VERIFY that 'Added to cart' is present

STEP 2: Load all reviews
    WHILE button 'Load More' exists:
        CLICK the 'Load More' button
        WAIT 2

STEP 3: Retry checkout
    REPEAT 3 TIMES:
        CLICK the 'Place Order' button
        IF text 'Success' is present:
            VERIFY that 'Order confirmed' is present

REPEAT N TIMES: runs a fixed count. FOR EACH {var} IN {collection}: iterates comma-separated values. WHILE <condition>: repeats until the condition is false (safety limit: 100 iterations). {i} counter is auto-set on every iteration.

Contextual navigation

When a page has repeating controls — multiple "Delete" buttons, "Edit" links in every row — use contextual qualifiers instead of brittle selectors.

CLICK the 'Edit' button NEAR 'John Doe'
CLICK the 'Login' button ON HEADER
CLICK the 'Privacy Policy' link ON FOOTER
CLICK the 'Delete' button INSIDE 'Actions' row with 'John Doe'

NEAR ranks by pixel distance. ON HEADER / ON FOOTER scopes to viewport zones. INSIDE restricts scoring to a resolved row or container subtree.

Variables, data-driven runs, and backend hooks

@var: {email} = admin@example.com
@var: {password} = secret123
@script: {db} = scripts.db_helpers
@data: users.csv
@tags: smoke, auth

[SETUP]
    CALL PYTHON {db}.seed_user "{email}" "{password}"
[END SETUP]

STEP 1: Login
    NAVIGATE to https://example.com/login
    FILL 'Email' field with '{email}'
    FILL 'Password' field with '{password}'
    CLICK the 'Sign In' button
    VERIFY that 'Dashboard' is present

STEP 2: Fetch and use an OTP
    CLICK the 'Send OTP' button
    CALL PYTHON api_helpers.fetch_otp "{email}" into {otp}
    FILL 'OTP' field with '{otp}'
    CLICK the 'Verify' button
    VERIFY that 'Welcome' is present

[TEARDOWN]
    CALL PYTHON {db}.clean_database "{email}"
[END TEARDOWN]

@data: loops the entire mission over each row in a CSV or JSON file. @tags: lets you filter runs with manul --tags smoke. [SETUP] / [TEARDOWN] run Python outside the browser for data seeding and cleanup. CALL PYTHON ... into {var} captures return values mid-test — ideal for OTPs, tokens, and backend state.

Explicit waits and strict assertions

WAIT FOR 'Submit' to be visible
WAIT FOR 'Loading...' to disappear

VERIFY "Email" field has value "{email}"
VERIFY "Save" button has text "Save Changes"
VERIFY "Search" input has placeholder "Type to search..."

Explicit waits use Playwright's locator.wait_for() instead of hardcoded sleeps. Strict assertions resolve the element through heuristics and compare exact text, value, or placeholder with ==.

Shared libraries and scheduling

@import: Login, Logout from lib/auth.hunt
@export: Checkout
@schedule: every 5 minutes

STEP 1: Setup
    USE Login

STEP 2: Purchase flow
    CLICK the 'Buy Now' button
    VERIFY that 'Order Confirmed' is present

STEP 3: Cleanup
    USE Logout

DONE.

@import: / USE reuses named STEP blocks across files. @export: controls visibility. @schedule: plus manul daemon turns any hunt into a recurring monitor or RPA job.

CLI

manul path/to/hunts/                             # run all .hunt files in a directory
manul --headless path/to/file.hunt               # headless single file
manul --tags smoke path/to/hunts/                # filter by tags
manul --html-report --screenshot on-fail path/   # reports + failure screenshots
manul --explain path/to/file.hunt                # per-step scoring breakdown
manul --debug path/to/file.hunt                  # pause before every step
manul --retries 2 path/to/hunts/                 # retry failed hunts
manul scan https://example.com                   # scan a page → draft.hunt
manul daemon path/to/hunts/ --headless           # run scheduled hunts

Philosophy

Determinism, not prompt variance

The primary resolver is not an LLM. It is a weighted heuristic scorer (DOMScorer) backed by a native JavaScript TreeWalker. Scores are normalized on a 0.0–1.0 confidence scale across five channels: cache, semantics, text, attributes, and proximity. The result is repeatable: same page state plus same step text equals same resolution — no prompt variance, no cloud dependency.

When --explain is enabled, every resolved step prints a per-channel breakdown so you can see exactly which signals drove the decision and which lost:

┌─ EXPLAIN: Target = "Login"
│  Step: Click the 'Login' button
│
│  #1 <button> "Login"
│     total:      0.593
│     text:       0.281
│     attributes: 0.050
│     semantics:  0.225
│     proximity:  0.037
│     cache:      0.000
│
└─ Decision: selected "Login" with score 0.593

Dual-persona workflow

Manual QA writes plain-English .hunt steps — no code required. SDETs extend the same files with Python hooks ([SETUP] / [TEARDOWN], CALL PYTHON), lifecycle orchestration (@before_all / @after_all), and @custom_control handlers for complex widgets. Both personas work on the same artifact.

Optional AI, off by default

"model": null is the recommended default. When a local Ollama model is enabled, it acts as a last-resort fallback for genuinely ambiguous elements. The engine is not AI-powered — it is heuristics-first with an optional AI safety net.


Four Automation Pillars

The same runtime and the same DSL serve four use cases:

Pillar How
QA / E2E testing Write plain-English flows, verify outcomes, attach HTML reports and screenshots. No selectors in the test source.
RPA workflows Log into portals, fill forms, extract values, hand off to Python for backend or filesystem steps.
Synthetic monitoring Pair .hunt files with @schedule: and manul daemon for recurring health checks.
AI agent targets Constrained DSL execution is safer than raw Playwright for external agents — the runtime still owns scoring, retries, and validation.

Key Features

  • Conditional branching & loopsIF / ELIF / ELSE for adaptive flows; REPEAT, FOR EACH, WHILE for iterating data, retrying actions, and polling dynamic state. Full nesting support.
  • Explainability--explain prints per-channel scoring breakdowns on the CLI. The VS Code extension shows hover tooltips and a title-bar "Explain Current Step" action during debug pauses.
  • What-If Analysis REPL — During a --debug pause, type w to enter an interactive REPL that evaluates hypothetical steps against the live DOM without executing them. Returns a 0–10 confidence score, risk assessment, and highlights the best match on the page.
  • Desktop / Electron — Set executable_path in the config and use OPEN APP instead of NAVIGATE to drive Electron apps with the same DSL.
  • Python API (ManulSession) — Async context manager for pure-Python automation. Routes every call through the full heuristic pipeline.
    from manul_engine import ManulSession
    
    async with ManulSession(headless=True) as session:
        await session.navigate("https://example.com/login")
        await session.fill("Username field", "admin")
        await session.click("Log in button")
        await session.verify("Welcome")
    
  • Smart recorder — Captures semantic intent (e.g., Select 'Option' from 'Dropdown') instead of brittle pointer events.
  • Custom controls@custom_control(page, target) decorator lets SDETs handle complex widgets (datepickers, virtual tables, canvas elements) with raw Playwright while the hunt file keeps a single readable step.
  • Lifecycle hooks@before_all, @after_all, @before_group, @after_group in manul_hooks.py for suite-wide setup and teardown.
  • HTML reports--html-report generates a self-contained dark-themed report with accordions, screenshots, tag filters, and run-session merging across CLI invocations.
  • Docker CI runnerghcr.io/alexbeatnik/manul-engine:0.0.9.29 runs headless in CI with dumb-init, non-root user, and MANUL_* env overrides.

Quickstart

Install

pip install manul-engine==0.0.9.29
playwright install

Optional local AI fallback (not required):

pip install "manul-engine[ai]==0.0.9.29"
ollama pull qwen2.5:0.5b && ollama serve

Configure

Create manul_engine_configuration.json in the workspace root. All keys are optional:

{
  "model": null,
  "browser": "chromium",
  "controls_cache_enabled": true,
  "semantic_cache_enabled": true
}

This is the minimal recommended config — fully heuristics-only, no AI dependency.

Run

manul tests/login.hunt                           # single file
manul tests/                                     # all hunts in a directory
manul --headless --html-report tests/            # CI mode with reports

Configuration reference

Key Default Description
model null Ollama model name. null = heuristics-only.
headless false Hide the browser window.
browser "chromium" chromium, firefox, or webkit.
browser_args [] Extra browser launch flags.
ai_threshold auto Score threshold before LLM fallback.
ai_always false Always invoke LLM picker (requires model).
ai_policy "prior" Heuristic score as prior hint or strict constraint.
controls_cache_enabled true Persistent per-site controls cache.
controls_cache_dir "cache" Cache directory (relative or absolute).
semantic_cache_enabled true In-session semantic cache (+200k score boost).
custom_controls_dirs ["controls"] Directories scanned for @custom_control modules.
timeout 5000 Action timeout (ms).
nav_timeout 30000 Navigation timeout (ms).
workers 1 Max parallel hunt files.
tests_home "tests" Default output for new hunts and scans.
auto_annotate false Insert # 📍 Auto-Nav: comments on URL changes.
channel null Installed browser channel (chrome, msedge).
executable_path null Path to Electron or custom browser executable.
retries 0 Retry failed hunts N times.
screenshot "on-fail" none, on-fail, or always.
html_report false Generate reports/manul_report.html.
explain_mode false Per-channel scoring breakdown in output.

Environment variables (MANUL_HEADLESS, MANUL_BROWSER, MANUL_MODEL, MANUL_WORKERS, etc.) always override JSON config.

Docker

docker run --rm --shm-size=1g \
  -v $(pwd)/hunts:/workspace/hunts:ro \
  -v $(pwd)/reports:/workspace/reports \
  ghcr.io/alexbeatnik/manul-engine:0.0.9.29 \
  --html-report --screenshot on-fail hunts/

Non-root (manul, UID 1000), dumb-init as PID 1, --no-sandbox baked in. A docker-compose.yml ships with manul and manul-daemon services.


Ecosystem

Component Role Links
ManulEngine Deterministic automation runtime (Python). Heuristic element resolver, .hunt DSL, CLI runner. PyPI · GitHub
Manul Engine Extension VS Code extension for ManulEngine with debug panel, explain mode, and Test Explorer integration. Marketplace · Open VSX · GitHub
ManulMcpServer MCP bridge that gives Copilot Chat and other agents access to ManulEngine. Marketplace · Open VSX · GitHub
ManulAI Local Agent Autonomous AI agent for browser automation, powered by ManulEngine. Marketplace · Open VSX · GitHub

Contributing and running tests

git clone https://github.com/alexbeatnik/ManulEngine.git
cd ManulEngine
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
playwright install

python run_tests.py                              # synthetic + unit suite
python demo/run_demo.py                          # integration hunts (needs network)
python demo/benchmarks/run_benchmarks.py         # adversarial DOM fixtures

Get Involved

ManulEngine is alpha-stage and solo-developed. If deterministic, explainable browser automation interests you:


What's New in v0.0.9.29

  • Loop constructs (REPEAT / FOR EACH / WHILE): Iterative execution blocks in .hunt files. REPEAT N TIMES: for fixed counts, FOR EACH {var} IN {collection}: for data iteration, WHILE <condition>: for dynamic polling. Full nesting with conditionals, {i} auto-counter, WHILE safety limit (100 iterations), empty body validation. 129-assertion test suite.
  • Complete user guide — new docs/ folder with structured documentation: overview, installation, getting started, full DSL syntax reference, reports & explainability, and integration guides.
v0.0.9.28
  • Conditional branching (IF / ELIF / ELSE): Block-style branching in .hunt files based on element presence, visible text, or variable state. Indentation-based body detection, nesting support, and ConditionalSyntaxError for malformed blocks. 97-assertion test suite.
  • What-If Analysis REPL (ExplainNextDebugger): Interactive debug REPL for hypothetical step evaluation. During a debug pause, type w (terminal) to enter the REPL or e / send explain-next (extension protocol) for one-shot evaluation. Combines DOMScorer heuristic scoring with optional LLM analysis to produce a 0–10 confidence rating, element match info, risk assessment, and corrective suggestions. The best heuristic match is highlighted with a persistent magenta outline on the live page. 112-assertion test suite.
  • What-If execute bug fixes: _execute_step() recursive call now passes strategic_context and step_idx by keyword. Injected What-If steps run through substitute_memory() so {var} placeholders resolve before execution.
  • LLM JSON fence-stripping: _parse_llm_json() now strips markdown code fences before JSON parsing.

License

Version: 0.0.9.29

Apache-2.0.

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

manul_engine-0.0.9.29.tar.gz (176.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

manul_engine-0.0.9.29-py3-none-any.whl (183.1 kB view details)

Uploaded Python 3

File details

Details for the file manul_engine-0.0.9.29.tar.gz.

File metadata

  • Download URL: manul_engine-0.0.9.29.tar.gz
  • Upload date:
  • Size: 176.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for manul_engine-0.0.9.29.tar.gz
Algorithm Hash digest
SHA256 57e0c921afd15e23a3834397f97355a33abb9067d9a9f5674033ef03f7426362
MD5 49c0cd552258d3494c6671145c9c2252
BLAKE2b-256 23fff341ec367357c0c6aceeafaaa0ff53b260e10f0cf48609c78791a680e83c

See more details on using hashes here.

Provenance

The following attestation bundles were made for manul_engine-0.0.9.29.tar.gz:

Publisher: release.yml on alexbeatnik/ManulEngine

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file manul_engine-0.0.9.29-py3-none-any.whl.

File metadata

  • Download URL: manul_engine-0.0.9.29-py3-none-any.whl
  • Upload date:
  • Size: 183.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for manul_engine-0.0.9.29-py3-none-any.whl
Algorithm Hash digest
SHA256 3bca27c7b937850f4a8efdd3e916a00adb37de6838be145e4253f5aa478d37f0
MD5 4be293650eda6c44e832e50af7b6c911
BLAKE2b-256 841bcf70f8980cab4edaf9fc588a3e7422f21fe25cc1f0171b9bea34fbbc3463

See more details on using hashes here.

Provenance

The following attestation bundles were made for manul_engine-0.0.9.29-py3-none-any.whl:

Publisher: release.yml on alexbeatnik/ManulEngine

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page