npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@oppira/cli

v0.11.0

Published

Oppira CLI — terminal control for the adaptive marketing system (competitor intelligence, playbook, studio, ops).

Readme

Oppira CLI

Drive Oppira from your terminal — competitor intelligence, your marketing playbook, battlecards, the content studio, and the Oppira AI agent — with scriptable, JSON-friendly output.

Requirements

  • Node.js 20+
  • A Pro Oppira plan — the CLI is a Pro surface. On lower plans the AI agent, playbook and competitor data are still available in the web app and (read-only) over MCP.

Install

npm install -g @oppira/cli
oppira --help

Quick start

oppira config set-url https://api.oppira.com    # your Oppira API endpoint
oppira auth login                                # log in (approve it in your browser)
oppira comp list                                 # see your tracked competitors
oppira ask "how did my top competitor shift this month?"

Config is stored in ~/.oppira/ (credentials are kept in a separate, locked-down file). You can run against several accounts/environments with profiles (oppira config use <name> or --profile <name>).

Authentication

Pick a method per profile:

  • Login (default):

    oppira auth login

    Opens your browser, where you approve this device with the session you already have — no password in the terminal. On a headless box add --no-browser and approve the printed code from any other machine (a phone works).

  • Password (CI, or no browser anywhere):

    oppira auth login --email [email protected] --password
  • API key (best for scripts / CI): create a key in the Oppira web app, then:

    oppira auth key set ca_xxxxxxxx

    API-key access to the CLI requires a Pro plan. (On other plans the same key still works for read-only MCP/analytics.)

Logging in here does not log you out anywhere else. The terminal, the web app and any other machine each hold their own session:

oppira auth sessions                    # what is signed in, and where
oppira auth sessions revoke <sid>       # end one
oppira auth sessions revoke --others    # end everything except this terminal

oppira auth whoami shows the current account; oppira auth logout ends this session only.

Commands

| Group | What you can do | |-------|-----------------| | oppira ask "…" | Ask the Oppira AI agent — streams the answer, its tool steps and any result cards; --out <dir> saves the images it produced. Reads args or stdin. | | oppira comp | Manage: list, show, add, track, toggle-tracking, insights, alerts, rm · read: posts, top-posts, summary, timeline, digests, digest, compare, tone | | oppira insights | list, summary, generate | | oppira alerts | list, run | | oppira battlecard | show, history, regen | | oppira playbook | show, bootstrap, refresh-section, refresh-cluster, edit-section, suggestions | | oppira reports | list, show, create, update, rm, run, runs — scheduled email reports | | oppira ads | Your own ad accounts: accounts, performance, campaigns, tree, list-ads, ad, analytics, comments · writes: create, boost, pause, resume, bulk-status, ad-pause, ad-resume, budget, ad-update, campaign-update, adset-status, adset-update, duplicate, rm-ad, rm-campaign, audiences, scope | | oppira keywords / oppira kw | Google Search keywords (Pro, Google Ads connected): research, plans, plan, metrics, account, search-terms, search-terms-refresh, decide · plan shaping: plan-new, plan-regroup, plan-add · writes: launch, apply | | oppira discover | profiles, suggestions, dismiss | | oppira studio | Posts: posts, create, update, publish, rm, quick-post, retry, generate-image · assets: assets, asset-upload, asset-import, asset-rm · brand kit: measure, brand-system, formats, format-attach, format-sync, format-recapability, templates, design · plus calendars, calendar-generate, comments, channels, policy, settings | | oppira artifact | create, get, export (reports / decks / diagrams) | | oppira signals | Oppira Signals — the cross-industry market report (admin). One command; --md gives the material an article is written from | | oppira config / oppira auth | profiles, API URL, login, API keys |

Run oppira <group> --help for a group's commands, or oppira <group> <command> --help for details.

The CLI itself requires a Pro plan; within it, every Oppira feature (AI agent, playbook, battlecards, reports, studio) is available. If something isn't available you'll get a clear message.

Examples

# Ask the agent, pipe the answer into your own tooling
oppira ask "summarize my playbook" --json | jq -r .text

# Competitors
oppira comp show Acme
oppira comp add "Globex" --website globex.com --instagram https://instagram.com/globex

# Refresh strategic docs on demand
oppira insights generate
oppira battlecard regen Acme

# Generate a report artifact, then export it
oppira artifact create --prompt "Q2 competitive landscape brief" --title "Q2 landscape"
oppira artifact formats <id>                  # what this one can become
oppira artifact export  <id> --format pdf
oppira artifact export  <id> --format md

# The whole playbook as one document
oppira playbook export --format pdf --out playbook.pdf
oppira playbook export --format md  --include-empty

# Schedule a weekly emailed report (Mon 08:00 Budapest)
oppira reports create --prompt "Weekly competitor movements" \
  --frequency WEEKLY --day 1 --hour 8 --timezone Europe/Budapest

# Tweak a playbook section directly
oppira playbook edit-section channel-strategy --text "Instagram and Facebook are core channels…"

# Oppira Signals: pull the market report, then write the article from it
oppira signals --from 2026-02 --to 2026-07
oppira signals --from 2026-02 --to 2026-07 --md   --out signals-h1-2026.md
oppira signals --from 2026-02 --to 2026-07 --json --out signals-h1-2026.json

Signals recomputes on every call — the digests behind it are rewritten as scraping continues — so save the --json run beside whatever you publish from it. That file, not a re-run, is the record of the figures you quoted.

Reading competitor data

oppira comp posts Acme --platform instagram --from 2026-06-01 --to 2026-06-30
oppira comp posts Acme --full                       # each post printed in full
oppira comp summary Acme                            # followers / posts / engagement per platform
oppira comp timeline Acme --months 6                # month by month
oppira comp digests Acme                            # which monthly digests exist
oppira comp digest Acme 2026-06                     # one month incl. ad + review channels
oppira comp top-posts Acme --from 2026-01 --to 2026-06 --limit 10
oppira comp compare Acme Globex --months 6
oppira comp tone Acme                               # messaging / tone drift

posts returns normalized posts — one shape across Facebook, Instagram, X and LinkedIn, newest first, capped at 100. Everything else is served from the pre-computed monthly digests, so those calls are cheap.

Your plan sets how far back you can read (free 1 month, basic 6, pro 12). When a request reaches past it, the CLI says so instead of silently returning less.

Setting up a new brand

The order matters — each step is a precondition for the next, and the API says so when you skip one.

# 1. Your own company, then the competitors it is measured against.
oppira comp add "My Co" --company --website myco.com --instagram https://instagram.com/myco
oppira discover suggestions --generate        # grounded in your own company + market
oppira comp add "Rival" --website rival.com   # add the ones worth tracking (>= 2)

# 2. The playbook. Needs your own company, 2+ competitors and one battlecard
#    (battlecards generate themselves after the first scrape — give it a while).
oppira playbook bootstrap                     # streams; a few minutes
oppira studio formats                         # your post formats, imported from it

# 3. The Brand Kit. Measure the site, then give each format a design AND ATTACH IT.
oppira studio measure --approve               # colours, type, logo from your own site
oppira studio design ./promo.html --css ./promo.css --format product_promo
oppira studio format-attach product_promo <templateId>

# 4. Connect a channel in the web app, then plan the week.
oppira studio channels                        # must list at least one
oppira studio calendar-generate

A design is not in use until it is attached. oppira studio formats shows a DESIGNS count per format; any format at 0 posts as plain text, and calendar-generate refuses while no format has one.

Publishing a post

oppira studio channels                              # the TARGET values --channel accepts
oppira studio asset-upload ./launch.png --kind image
oppira studio create --body "Launch day 🚀" --media <assetId> --channel facebook:123
oppira studio publish <postId> --now                # or --at 2026-08-01T09:00:00Z

# …or all of it in one run
oppira studio quick-post ./launch.png --body "Launch day 🚀" --channel facebook:123 --now

# THE USER'S FLOW, in a terminal: the agent composes, you watch the steps and
# get the images. This is what to reach for when the question is "does it design well?".
oppira ask "make me a facebook template for the blog" --out ./out

# THE ENGINE'S FLOW: your own HTML+CSS straight through the gate, no model in the
# loop. For tuning the compiler and reading its refusals verbatim (exit code 2).
oppira studio design ./card.html --css ./card.css --width 1080 --height 1350 --out ./card.png

asset-upload is for local files. asset-import only accepts URLs already hosted by Oppira (a deliberate SSRF guard), so it is not a substitute.

Publishing runs a price fact-check first. If a price claim in the body no longer matches your catalog the post is held back; edit it, or re-run with --skip-fact-check.

Creating and editing ads

Ad writes touch real money, so every command that can start or raise spend (create, boost, resume, ad-resume, budget, campaign-update, bulk-status --status active) prints a preview and refuses to act without --yes. The preview is rendered before any API call, so a forgotten --yes never reaches the platform. With --json the blocked command emits {"status":"needs_confirmation","preview":{…}} so scripts can branch on it.

oppira ads accounts                                  # accountId per platform
oppira ads ad-accounts <accountId>                   # act_… / Google customer ids

oppira ads create --account <id> --ad-account act_123 \
  --name "Summer traffic" --goal traffic --budget 25 \
  --headline "Try it free" --link https://example.com --countries US,HU   # preview only
oppira ads create … --yes                            # actually creates it

oppira ads pause <campaignId>                        # stops spend, no --yes needed
oppira ads budget <adId> --amount 30 --yes           # ad-level budget: every platform
oppira ads list-ads --campaign <campaignId>

Capabilities differ per platform — campaign-level (CBO) budgets and campaign deletion are Meta-only, boost has no Google Ads path, and creative/targeting edits are Meta + TikTok. The CLI does not guess: the API answers with a message naming what is possible on that platform, and the CLI prints it as-is.

Researching keywords

Google Search only, Pro, and it needs a connected Google Ads account — accountId is part of every Keyword Planner call, so there is no "research first, connect later".

A research run measures a market and creates the draft plan out of its own rows: the terms it priced, clustered into ad groups by intent, with a match type per keyword. Everything after it reads what that run stored, so only two commands ever call Google again — research (5/day) and search-terms-refresh (10/day).

# 1. Measure. Seeds are 3-5 concrete things a customer would actually type.
oppira keywords research --seeds "domain registration,buy domain,cheap domain" \
  --countries HU --url https://example.com/domains --name "Domains HU"

# 2. Read it back, any time, for free.
oppira keywords plans
oppira keywords plan <planId> --budget 25        # ad groups, match types, reach verdict
oppira keywords metrics --plan <planId> --json   # every measured figure, as rows
oppira keywords metrics --terms "buy domain,domain price" --currency HUF

# 3. Re-shape it without touching Google.
oppira keywords plan-regroup <planId> --landing https://example.com/pricing
oppira keywords plan-add <planId> --group "Generic" --terms "domain sale"
oppira keywords plan-new --file ./chosen-terms.txt --name "Brand terms"

# 4. Launch (preview first — it is the server's, so it checks the ad copy).
oppira keywords launch <planId> --budget 25 --headline "Domains from $6" \
  --body "Register in a minute." --link https://example.com/domains
oppira keywords launch <planId> --budget 25 … --yes      # creates it PAUSED

# 5. The weekly loop: the real queries, and what they propose.
oppira keywords search-terms-refresh
oppira keywords search-terms
oppira keywords decide negated --terms "free domain hosting"

Two things to know before the last step. launch creates the campaign paused unless you pass --status active, and every keyword is stored broad by the create call — the match types in your plan are installed by a follow-up write, which the command reports per ad group. And keywords apply is declarative: the list you send becomes the ad group's full set, so anything you leave out is deleted. It therefore asks twice — --yes to write, and --confirm-removals once you have read the removals in the diff it prints.

research and search-terms-refresh can come back with "accepted, nothing happened" (the allowance is refunded). They say which of three it was — Google's shared quota with the time it resets, seeds too abstract, or no data for that market — and exit non-zero, so a script never reads a quota wall as an empty market.

Scripting & automation

  • Add --json to any command for machine-readable output.
  • Commands exit non-zero on failure, so they behave well in CI.
  • oppira ask reads a question from arguments or piped stdin.
echo "what should I post about this week?" | oppira ask --json

Daily request limit

The CLI is capped at 100 API requests per user per day (resets at UTC midnight). The cap counts requests, not commands — so keep in mind:

  • Passing a competitor name costs one extra request to resolve it. In a loop, pass the id (oppira comp list --json once, then reuse the ids).
  • quick-post is three requests (upload, create, publish).
  • Over the cap every command exits non-zero with error: "quota-exceeded", scope: "daily" and a retryAfter in seconds.
  • Below 10 remaining, the CLI prints a warning to stderr; every response also carries X-Oppira-Daily-Limit / X-Oppira-Daily-Remaining.

Generation-heavy operations (image generation, calendar generate, insight/alert regeneration) have their own, tighter per-day caps that apply to the web app too.

Support

Questions or issues? Reach the Oppira team at oppira.com.