PluginBench
Skill
Fail
Audit score 45

opencli-web-automation

aradotso/trending-skills

Turn any website into a CLI using browser session reuse and AI-powered command discovery

What is opencli-web-automation?

OpenCLI converts websites into command-line interfaces by reusing Chrome's logged-in browser session. It supports 19 sites with 80+ built-in commands and lets you add custom adapters via TypeScript or YAML. Use it to automate web scraping, data extraction, and browser interactions from the command line.

  • Reuse Chrome browser sessions to access authenticated websites via CLI
  • Discover and synthesize new CLI commands from any website using AI exploration
  • Support 19 pre-built site adapters (Twitter, GitHub, YouTube, Reddit, Bilibili, etc.) with 80+ commands
  • Output results in multiple formats: table, JSON, YAML, Markdown, CSV
  • Create custom adapters declaratively with YAML or programmatically with TypeScript
  • Extract data from DOM, APIs, and authenticated endpoints using Playwright

How to install opencli-web-automation

npx skills add https://github.com/aradotso/trending-skills --skill opencli-web-automation
Prerequisites
  • Node.js >= 18.0.0
  • Chrome browser running and logged into target sites
  • Playwright MCP Bridge extension installed in Chrome
  • PLAYWRIGHT_MCP_EXTENSION_TOKEN environment variable set after running opencli setup
Claude Code
Cursor
Windsurf
Cline

How to use opencli-web-automation

  1. 1.Run `npm install -g @jackwener/opencli` to install globally
  2. 2.Run `opencli setup` to discover and configure the Playwright MCP token
  3. 3.Set the PLAYWRIGHT_MCP_EXTENSION_TOKEN in your shell config (~/.zshrc or ~/.bashrc)
  4. 4.Run `opencli list` to see all available commands
  5. 5.Execute built-in commands like `opencli twitter trending` or `opencli github search "query"`
  6. 6.For new websites, run `opencli explore <url> --site <name>` to discover capabilities
  7. 7.Run `opencli synthesize <site>` to generate YAML adapters from explore artifacts
  8. 8.Drop custom YAML or TypeScript adapter files into the `clis/` folder for auto-registration

Use cases

Good for
  • Scrape trending posts from social media sites (Twitter, Bilibili, Xiaohongshu) without browser UI
  • Search and retrieve data from GitHub, YouTube, LinkedIn, and other sites via CLI
  • Automate data extraction from authenticated web applications using stored browser cookies
  • Build custom CLI commands for internal or third-party websites using YAML or TypeScript adapters
  • Integrate website data into shell scripts and automation pipelines
Who it's for
  • DevOps engineers automating web data collection
  • Backend developers building CLI tools for web scraping
  • AI agents and coding assistants needing web automation capabilities
  • Data engineers extracting information from authenticated web services
  • Full-stack developers creating custom command-line interfaces for websites

opencli-web-automation FAQ

Do I need to be logged into websites for all commands?

No. Public API commands (like `opencli hackernews top`) work without login. Browser commands that access authenticated content require Chrome to be logged into that site.

How do I add support for a new website?

Use `opencli explore <url> --site <name>` to discover the site's APIs and capabilities, then `opencli synthesize <site>` to generate a YAML adapter. Alternatively, write a TypeScript adapter directly in the `clis/` folder.

What authentication methods are supported?

OpenCLI supports cookie-based authentication (reusing browser session), header token extraction from localStorage/sessionStorage, and public API endpoints. The cascade command auto-probes PUBLIC → COOKIE → HEADER strategies.

Can I use this with AI agents like Claude or Cursor?

Yes. OpenCLI is designed as an MCP (Model Context Protocol) server. Configure it in your Claude/Cursor config.json to enable agents to run CLI commands and extract web data.

What output formats are available?

All commands support table (default), JSON, YAML, Markdown, and CSV formats via the `-f` or `--format` flag.

Full instructions (SKILL.md)

Source of truth, from aradotso/trending-skills.


name: opencli-web-automation description: Turn any website into a CLI using browser session reuse and AI-powered command discovery triggers:

  • "use opencli to scrape a website"
  • "make a CLI command for a website"
  • "automate browser with opencli"
  • "add a new opencli adapter"
  • "extract data from website using CLI"
  • "opencli explore and synthesize commands"
  • "create yaml adapter for opencli"
  • "opencli browser automation"

OpenCLI Web Automation

Skill by ara.so — Daily 2026 Skills collection.

OpenCLI turns any website into a command-line interface by reusing Chrome's logged-in browser session. It supports 19 sites and 80+ commands out of the box, and lets you add new adapters via TypeScript or YAML dropped into the clis/ folder.


Installation

# Install globally via npm
npm install -g @jackwener/opencli

# One-time setup: discovers Playwright MCP token and distributes to all tools
opencli setup

# Verify everything is working
opencli doctor --live

Prerequisites

  • Node.js >= 18.0.0
  • Chrome browser running and logged into the target site
  • Playwright MCP Bridge extension installed in Chrome

Install from Source (Development)

git clone git@github.com:jackwener/opencli.git
cd opencli
npm install
npm run build
npm link

Environment Configuration

# Required: set in ~/.zshrc or ~/.bashrc after running opencli setup
export PLAYWRIGHT_MCP_EXTENSION_TOKEN="<your-token-from-setup>"

MCP client config (Claude/Cursor/Codex ~/.config/*/config.json):

{
  "mcpServers": {
    "playwright": {
      "command": "npx",
      "args": ["-y", "@playwright/mcp@latest", "--extension"],
      "env": {
        "PLAYWRIGHT_MCP_EXTENSION_TOKEN": "$PLAYWRIGHT_MCP_EXTENSION_TOKEN"
      }
    }
  }
}

Key CLI Commands

Discovery & Registry

opencli list                        # Show all registered commands
opencli list -f yaml                # Output registry as YAML
opencli list -f json                # Output registry as JSON

Running Built-in Commands

# Public API commands (no browser login needed)
opencli hackernews top --limit 10
opencli github search "playwright automation"
opencli bbc news

# Browser commands (must be logged into site in Chrome)
opencli bilibili hot --limit 5
opencli twitter trending
opencli zhihu hot -f json
opencli reddit frontpage --limit 20
opencli xiaohongshu search "TypeScript"
opencli youtube search "browser automation"
opencli linkedin search "senior engineer"

Output Formats

All commands support --format / -f:

opencli bilibili hot -f table     # Rich terminal table (default)
opencli bilibili hot -f json      # JSON (pipe to jq)
opencli bilibili hot -f yaml      # YAML
opencli bilibili hot -f md        # Markdown
opencli bilibili hot -f csv       # CSV export
opencli bilibili hot -v           # Verbose: show pipeline debug steps

AI Agent Workflow (Creating New Commands)

# 1. Deep explore a site — discovers APIs, auth, capabilities
opencli explore https://example.com --site mysite

# 2. Synthesize YAML adapters from explore artifacts
opencli synthesize mysite

# 3. One-shot: explore → synthesize → register in one command
opencli generate https://example.com --goal "hot posts"

# 4. Strategy cascade — auto-probes PUBLIC → COOKIE → HEADER auth
opencli cascade https://api.example.com/data

Explore artifacts are saved to .opencli/explore/<site>/:

  • manifest.json — site metadata
  • endpoints.json — discovered API endpoints
  • capabilities.json — inferred command capabilities
  • auth.json — authentication strategy

Adding a New Adapter

Option 1: YAML Declarative Adapter

Drop a .yaml file into clis/ — auto-registered on next run:

# clis/producthunt.yaml
site: producthunt
commands:
  - name: trending
    description: Get trending products on Product Hunt
    args:
      - name: limit
        type: number
        default: 10
    pipeline:
      - type: navigate
        url: https://www.producthunt.com
      - type: waitFor
        selector: "[data-test='post-item']"
      - type: extract
        selector: "[data-test='post-item']"
        fields:
          name:
            selector: "h3"
            type: text
          tagline:
            selector: "p"
            type: text
          votes:
            selector: "[data-test='vote-button']"
            type: text
          url:
            selector: "a"
            attr: href
      - type: limit
        count: "{{limit}}"

Option 2: TypeScript Adapter

// clis/producthunt.ts
import type { CLIAdapter } from "../src/types";

const adapter: CLIAdapter = {
  site: "producthunt",
  commands: [
    {
      name: "trending",
      description: "Get trending products on Product Hunt",
      options: [
        {
          flags: "--limit <n>",
          description: "Number of results",
          defaultValue: "10",
        },
      ],
      async run(options, browser) {
        const page = await browser.currentPage();
        await page.goto("https://www.producthunt.com");
        await page.waitForSelector("[data-test='post-item']");

        const products = await page.evaluate(() => {
          return Array.from(
            document.querySelectorAll("[data-test='post-item']")
          ).map((el) => ({
            name: el.querySelector("h3")?.textContent?.trim() ?? "",
            tagline: el.querySelector("p")?.textContent?.trim() ?? "",
            votes:
              el
                .querySelector("[data-test='vote-button']")
                ?.textContent?.trim() ?? "",
            url:
              (el.querySelector("a") as HTMLAnchorElement)?.href ?? "",
          }));
        });

        return products.slice(0, Number(options.limit));
      },
    },
  ],
};

export default adapter;

Common Patterns

Pattern: Authenticated API Extraction (Cookie Injection)

// When a site exposes a JSON API but requires login cookies
async run(options, browser) {
  const page = await browser.currentPage();

  // Navigate first to ensure cookies are active
  await page.goto("https://api.example.com");

  const data = await page.evaluate(async () => {
    const res = await fetch("/api/v1/feed?limit=20", {
      credentials: "include", // reuse browser cookies
    });
    return res.json();
  });

  return data.items;
}

Pattern: Header Token Extraction

// Extract auth tokens from browser storage for API calls
async run(options, browser) {
  const page = await browser.currentPage();
  await page.goto("https://example.com");

  const token = await page.evaluate(() => {
    return localStorage.getItem("auth_token") ||
           sessionStorage.getItem("token");
  });

  const data = await page.evaluate(async (tok) => {
    const res = await fetch("/api/data", {
      headers: { Authorization: `Bearer ${tok}` },
    });
    return res.json();
  }, token);

  return data;
}

Pattern: DOM Scraping with Wait

async run(options, browser) {
  const page = await browser.currentPage();
  await page.goto("https://news.ycombinator.com");

  // Wait for dynamic content to load
  await page.waitForSelector(".athing", { timeout: 10000 });

  return page.evaluate((limit) => {
    return Array.from(document.querySelectorAll(".athing"))
      .slice(0, limit)
      .map((row) => ({
        title: row.querySelector(".titleline a")?.textContent?.trim(),
        url: (row.querySelector(".titleline a") as HTMLAnchorElement)?.href,
        score:
          row.nextElementSibling
            ?.querySelector(".score")
            ?.textContent?.trim() ?? "0",
      }));
  }, Number(options.limit));
}

Pattern: Pagination

async run(options, browser) {
  const page = await browser.currentPage();
  const results = [];
  let pageNum = 1;

  while (results.length < Number(options.limit)) {
    await page.goto(`https://example.com/posts?page=${pageNum}`);
    await page.waitForSelector(".post-item");

    const items = await page.evaluate(() =>
      Array.from(document.querySelectorAll(".post-item")).map((el) => ({
        title: el.querySelector("h2")?.textContent?.trim(),
        url: (el.querySelector("a") as HTMLAnchorElement)?.href,
      }))
    );

    if (items.length === 0) break;
    results.push(...items);
    pageNum++;
  }

  return results.slice(0, Number(options.limit));
}

Maintenance Commands

# Diagnose token and config across all tools
opencli doctor

# Test live browser connectivity
opencli doctor --live

# Fix mismatched configs interactively
opencli doctor --fix

# Fix all configs non-interactively
opencli doctor --fix -y

Testing

npm run build

# Run all tests
npx vitest run

# Unit tests only
npx vitest run src/

# E2E tests only
npx vitest run tests/e2e/

# Headless browser mode for CI
OPENCLI_HEADLESS=1 npx vitest run tests/e2e/

Troubleshooting

SymptomFix
Failed to connect to Playwright MCP BridgeEnsure extension is enabled in Chrome; restart Chrome after install
Empty data / UnauthorizedOpen Chrome, navigate to the site, log in or refresh the page
Node API errorsUpgrade to Node.js >= 18
Token not foundRun opencli setup or opencli doctor --fix
Stale login sessionVisit the target site in Chrome and interact with it to prove human presence

Debug Verbose Mode

# See full pipeline execution steps
opencli bilibili hot -v

# Check what explore discovered
cat .opencli/explore/mysite/endpoints.json
cat .opencli/explore/mysite/auth.json

Project Structure (for Adapter Authors)

opencli/
├── clis/               # Drop .ts or .yaml adapters here (auto-registered)
│   ├── bilibili.ts
│   ├── twitter.ts
│   └── hackernews.yaml
├── src/
│   ├── types.ts        # CLIAdapter, Command interfaces
│   ├── browser.ts      # Playwright MCP bridge wrapper
│   ├── loader.ts       # Dynamic adapter loader
│   └── output.ts       # table/json/yaml/md/csv formatters
├── tests/
│   └── e2e/            # E2E tests per site
└── CLI-EXPLORER.md     # Full AI agent exploration workflow