
Research
/Security News
OpenAPI React Query Codegen Compromised in Mini Shai-Hulud npm Supply Chain Attack
Ten malicious OpenAPI React Query Codegen versions were published to npm in the Mini Shai-Hulud attack, all with valid provenance.
Every open tab is a thread you forgot to pull. Tabctl finds them all.
A command-line instrument for browser tab orchestration — list, search, group, archive, close, undo — wired into Edge or Chrome through a native messaging bridge. Built for humans who hoard tabs and the AI agents who clean up after them.
npm install -g tabctl
tabctl setup --browser chrome
# Load the extension: chrome://extensions → Developer mode → Load unpacked → paste: ~/.local/state/tabctl/extension/
tabctl ping
If it pings back, the wire is live. You're connected.
Give your coding agent eyes into the browser. One command and it learns the protocol.
tabctl skill
# or: npx skills add https://github.com/ekroon/tabctl --skill tabctl -a opencode -a github-copilot -a claude-code
Nothing leaves your machine. No cloud. No telemetry. Just a socket between your terminal and your browser, quiet as rain on neon.
Every mutation is undoable — tabctl undo rewinds closes, archives, and group changes like they never happened. A configurable policy layer shields pinned tabs and protected domains from accidental destruction. You pull the trigger; tabctl keeps the safety on until you mean it.
When tabctl is installed as a skill, your agent sees what you see. Just talk to it.
"Which of my tabs can I close?" The agent scans for duplicates, stale pages, and tabs you haven't touched in days — then offers to clean house.
"Are any of my open tabs relevant to my note on Project Helios?" When connected to Obsidian, your agent cross-references every open tab against your notes and surfaces the ones that matter.
"I just finished researching service mesh architectures. Organize what I found." Groups your tabs by theme, extracts key URLs, and drops a summary into your notes — before you forget what you were looking at.
"Where's that AWS pricing page I had open somewhere?" The agent searches your open tabs and groups by title and URL — and brings it back into focus.
"Pull every error message from my open Sentry tabs into a markdown table." The agent reads each tab, extracts what you need, and formats it — no copy-paste, no context switching.
"Group everything by project. You know which ones." Your agent infers context from URLs, titles, and your workspace — then sorts ninety tabs into five groups with names that actually make sense.
tabctl works through a lightweight local stack: the CLI talks to a native messaging host, which proxies requests to a browser extension. The host only runs while the browser is open and the extension is connected.
This repo contains:
tabctl) for on-demand workflowsnpm install
npm run build
npm link # puts tabctl on your PATH
If you haven't run npm link, you can always use node ./cli/tabctl.js instead of tabctl.
Run setup — it syncs the extension, tells you where to load it, and auto-derives the extension ID:
tabctl setup --browser chrome
This will:
~/.local/state/tabctl/extension/)chrome://extensionsEdge? Use
--browser edgeand load fromedge://extensionsinstead.
If you need to override the auto-derived ID (e.g. for a custom extension path):
tabctl setup --browser chrome --extension-id <your-extension-id>
tabctl ping # check the connection
tabctl list # see your open tabs
Multiple browsers? See Multi-Browser Setup for running tabctl with both Chrome and Edge.
| Command | Description |
|---|---|
tabctl list | List open tabs and groups |
tabctl open --url <url> --group <name> | Open tabs into a group (reuses existing, skips duplicates) |
tabctl analyze | Find stale or duplicate tabs |
tabctl inspect --tab <id> | Extract page metadata or CSS selectors |
tabctl group-gather | Merge duplicate groups with the same name |
tabctl close --tab <id> | Close tabs with full undo support |
tabctl report | Generate reports in JSON, Markdown, or CSV |
tabctl undo | Revert the last action |
See CLI.md for the full command reference, options, and examples.
When --out is omitted, screenshots are written to ./.tabctl/screenshots/<timestamp> and the JSON response includes writtenTo.
Use screenshots only when you need visual context, then extract selectors with inspect.
tabctl screenshot --tab <id> --mode full
tabctl inspect --tab <id> --signal selector --selector '{"name":"target","selector":".your-selector"}'
--selector-attr href-url or set attr to href-url/src-url:tabctl inspect --tab <id> --signal selector --selector '{"name":"link","selector":"a[href]","attr":"href-url"}'
tabctl inspect --tab <id> --signal selector --selector "link=a[href]" --selector-attr href-url
Install the tabctl skill for agents (OpenCode, Claude Code, Codex, etc.) via the bundled command (uses the Skills CLI under the hood):
tabctl skill
This writes a project-local skill to .opencode/skills/tabctl/SKILL.md. You can also install globally:
tabctl skill --global
To install into a specific agent toolchain with skills:
npx skills add https://github.com/ekroon/tabctl --skill tabctl -a opencode
By default the CLI loads a policy file from:
<configDir>/policy.json (default: ~/.config/tabctl/policy.json)
Set TABCTL_CONFIG_DIR to override the config directory.
This is a protection-only policy that marks tabs as ineligible for agent actions. Example:
{
"protect": {
"pinned": true,
"groupTitles": ["🔒"]
}
}
Create a default policy file:
tabctl policy --init
tabctl setup does not install a default policy.
See config/policy.example.json for a starter template.
Config directory: TABCTL_CONFIG_DIR → $XDG_CONFIG_HOME/tabctl → ~/.config/tabctl
An optional config.json in the config directory can set dataDir to override where state files (socket, undo log) are stored. When TABCTL_CONFIG_DIR is set but no dataDir is configured, data defaults to <configDir>/data/; otherwise it uses $XDG_STATE_HOME/tabctl (or ~/.local/state/tabctl).
See CLI.md for full details.
<dataDir>/tabctl.sock (default: ~/.local/state/tabctl/tabctl.sock)<dataDir>/undo.jsonl (default: ~/.local/state/tabctl/undo.jsonl)<configDir>/profiles.jsonAdvanced topic — you only need this if you run tabctl with more than one browser (e.g. Edge and Chrome).
tabctl supports multiple browser profiles. Each profile connects to a different browser (Chrome, Edge).
# Setup for Edge
tabctl setup --browser edge
# Setup for Chrome (with custom name)
tabctl setup --browser chrome --name chrome-work
# List profiles
tabctl profile-list
# Switch default
tabctl profile-switch edge
# One-off command with different profile
tabctl list --profile chrome-work
If you launch Chrome with --user-data-dir, Chrome looks for native messaging manifests inside that directory. Use --user-data-dir in setup to write the manifest to the right place:
tabctl setup --browser chrome --user-data-dir /path/to/chrome-profile
This writes the manifest to <user-data-dir>/NativeMessagingHosts/ instead of the system-wide location.
Each profile gets its own:
Policy is shared across all profiles.
Source lives in src/ and compiles to build/, then syncs to the runtime locations:
src/extension/background.ts -> extension/background.jssrc/host/host.ts -> host/host.jssrc/cli/tabctl.ts -> cli/tabctl.jssrc/tests/unit/*.ts -> tests/unit/*.jsBuild and test:
npm install
npm run build
npm test
The base version lives in package.json and is embedded into the CLI, host, and extension at build time.
Commands:
npm run bump:patch
npm run bump:minor
npm run bump:major
Local builds default to a dev version when a .git directory is present, appending the short SHA.
npm run build
This produces versions like 0.1.0-dev.abc12345 (and appends .dirty when the repo has uncommitted changes).
For release builds without SHA, set:
TABCTL_VERSION_MODE=release npm run build
Notes:
close --apply uses the most recent analysis by analysisId.close without --apply requires --confirm to prevent accidental closure.list and group-list paginate by default (limit 100); use --limit, --offset, or --no-page.--group-id -1 or --ungrouped to target ungrouped tabs.--selector implies --signal selector.page-meta, selector).attr supports href-url/src-url to return absolute http(s) URLs.screenshot --out writes per-tab folders into the target directory.tabctl undo accepts a positional txid, --txid, or --latest.tabctl history --json returns a JSON array in data.--format is only supported by report (use --json elsewhere).FAQs
CLI tool to manage and analyze browser tabs
We found that tabctl demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Research
/Security News
Ten malicious OpenAPI React Query Codegen versions were published to npm in the Mini Shai-Hulud attack, all with valid provenance.

Security News
Socket joins more than 100 technology, cybersecurity, and financial organizations calling for a global surge in cyber defense.

Product
Enterprise security teams can now detect malware, credential theft, suspicious network activity, and risky updates across Microsoft Edge extensions.