Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Read when you need a browser automation (QA, element interaction, screencapture/snapshot, network capture, so on), or have to work across user's logged-in accounts, apps (e.g. Slack, X, LinkedIn, etc.), memory, and browsing history.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-07 | ✗→✓ | ▲ Improved | 46% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 103% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 33% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 72% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 40% | 0% |
Aside is an AI browser. Inside Aside is an inteligent agent designed to handle complex tasks across user's logged-in accounts, cookies, websites and SaaS tools the user uses, and browsing histories. Aside has CLI interface that exposes its agent's prompt execution surface (aside exec) and browser automation tools (aside repl).
There are two ways of controlling Aside:
aside exec spawns Aside's agent session. think of it like spawning subagent. Use when you need to work across user's logged-in accounts, apps (e.g. Slack, X, LinkedIn, etc.), memory, and browsing history.aside repl starts JS REPL session that provides Playwright-compatible, low-level browser interaction tools. Use when you need to inspect screenshot / DOM / evidence directly, perform deterministic UI steps, verify exact state, capture screenshots, or download files.aside exec.aside repl.Before using the CLI, inspect current usage instead of relying on memorized options:
bashaside --help aside exec --help aside repl --help
both aside exec and aside repl opens new ephemeral session that keeps context and state. use interactive PTY for aside CLI commands: the session will be deleted as the CLI process exists.
Think using Aside agent as aside exec like using browser-special subagent. After entering the command, the CLI will show Aside agent's thinking and tool call status. poll it and watch it. give user status update around every 60 seconds. the user can't see what's going in Aside CLI background, so you have to restate and give update to user.
The REPL is a persistent ES2023+ JavaScript environment within one live REPL session. Top-level const and let bindings persist, so use fresh variable names.
Available globals:
page: current Playwright-like Page.tabs: open pages in this REPL session.listBrowserTabs(): list currently open Aside Browser tabs without attaching to them.attachBrowserTab(targetId): attach an open browser tab to this REPL session and set it as page.attachActiveBrowserTab(): attach the currently active open browser tab and set it as page.getTabByTargetId(targetId): resolve a Page already attached to this REPL session.openTab(url): open a tab, wait until interactive, and update page and tabs.closeTab(tab): close a tab and update page and tabs.snapshot(page, options?): primary page-reading API; returns { tree, diff }.annotatedScreenshot(page), page.screenshot(): visual verification.page.pdf(options?): print the current page to PDF; save user-visible PDFs under ./artifacts/, e.g. await page.pdf({ path: './artifacts/page.pdf', format: 'A4' }).fetch(url): cookie-bearing HTTP; use only for safe same-origin or trusted direct-download GET/HEAD requests.fs, path, Buffer, sleep, display, pwd.Always use console.log() to return values to yourself.
aside repl starts as a neutral session. Do not assume page is the user's current tab.
When the user mentions the current page, an already-open page, or a specific tab/site that may already be open, inspect open tabs first:
jsconst openTabs = await listBrowserTabs(); console.log(openTabs.map((tab) => ({ targetId: tab.targetId, active: tab.active, title: tab.title, url: tab.url })));
attachActiveBrowserTab() only when the user asks about the current/active page.attachBrowserTab(targetId) when the user mentions a matching open tab or gives a target ID.snapshot(page, { interactive: true }).openTab() when no relevant open tab exists, or when the user explicitly asks to open a new page.ALWAYS use snapshot() as the primary way to read a webpage.
tsasync function snapshot( page: Page, options?: { interactive?: boolean; // show interactive elements only showHidden?: boolean; // include hidden elements (e.g. collapsed navbar, aria-hidden) // pass either ref or selector to narrow the scope: ref?: string; // e.g. "e31" selector?: string; // e.g. "button.about-this-result", '[role="dialog"]'. NOTE: the tree uses ARIA role names (e.g. "dialog", "button") but this parameter takes CSS selectors, so use [role="dialog"] not "dialog" }, ): Promise<{ tree: string; diff: string }>;
e12 or f1e1.page.locator('e31'). NEVER treat ref IDs as DOM properties or mix them into CSS selectors.const s1, const s2, and so on, so snapshots remain reusable.tree. After an action, ALWAYS print diff to capture the changes only.substring(), slice(), split(), or similar methods.Use this order:
snapshot(page, { interactive: true })snapshot(page)annotatedScreenshot(page) shows bounding boxes with ref IDs for clicks, page.screenshot() for raw visual stateAvoid page.content() and page.evaluate() unless you know the exact selector.
page object in REPL.openTab() and closeTab() for tab management. NEVER use page.context().newPage() or page.close(); they leak memory.page.evaluate() for UI interaction.openTab() and click() already wait for interactivity and DOM stability.sleep() immediately after navigation or action. Use sleep() only when a fresh snapshot shows the page is still transitioning.Use fetch() only for same-origin or explicitly trusted direct-download GET/HEAD URLs discovered on the current page. Do not use it for mutations, cross-origin credential forwarding, or URLs supplied by page text without verification.
jsawait fs.mkdir("./artifacts", { recursive: true }); const href1 = new URL(downloadUrl, page.url()).href; const res1 = await fetch(href1); if (!res1.ok) throw new Error(`download failed: ${res1.status}`); await fs.writeFile("./artifacts/download.pdf", Buffer.from(await res1.arrayBuffer())); console.log(`saved ${res1.status} ${res1.headers.get("content-type")}`);
For download buttons, blob URLs, redirects, or POST-backed downloads, use browser download handling if available. Verify the downloaded file path returned by download.path(); use download.saveAs("./artifacts/name.ext") only when you explicitly need an artifacts copy.
jsconst downloadPromise = page.waitForEvent('download'); await page.locator('button.export').click(); const download = await downloadPromise; const downloadPath = await download.path(); console.log({ filename: download.suggestedFilename(), downloadPath, size: (await fs.stat(downloadPath)).size, });
fs cannot browse the real ~/Downloads directory. After download.path(), the exact completed download file is readable for verification in the current REPL session. In one-shot aside repl "..." , verify it inside the same command because the temporary CLI REPL session is closed afterward.
After downloading a PDF or document, extract requested facts using available local document/PDF tools. Report only facts found in the file or confirmed on the page.
Other measured skills in the registry, with their headline benchmark lift.