Tabman
A local-first tab switchboard and collection library for Brave and other Chromium browsers, with a companion Python CLI vault.
Tabman has two surfaces:
- Extension — a WXT + React side panel and dashboard for finding, grouping, discarding, closing, parking, and restoring tabs. Data stays in IndexedDB.
- CLI — a Python 3.14 command-line vault for importing, searching, organizing, and exporting tab collections as SQLite, JSON, or Markdown.
No account, cloud service, or AI provider is required for the core workflow.
Status
The 0.3.x rebuild is usable today. The extension switchboard, local library, schema, SQLite vault, and deterministic organizer are implemented. Optional BYOK AI organization works with any OpenAI- or Anthropic-compatible endpoint.
Install the extension
From a GitHub Release
After a release has been published, this PowerShell command downloads the latest extension archive, extracts it, and opens the Brave extensions page (when Brave is installed in a standard location):
irm https://raw.githubusercontent.com/Anshulgada/brave-tab-manager/main/scripts/install-extension.ps1 | iex
macOS/Linux:
curl -LsSf https://raw.githubusercontent.com/Anshulgada/brave-tab-manager/main/scripts/install-extension.sh | sh
Then:
- Enable Developer mode in
brave://extensions. - Choose Load unpacked.
- Select the folder printed by the installer (under
%LOCALAPPDATA%\Tabman\extension-lateston Windows, or~/.local/share/tabman/extension-latestelsewhere). - Open Brave's side panel and select Tabman.
You can also download the tabmanextension-*-chrome.zip asset from the Releases page, extract it, and load that folder manually.
From source
cd extension
bun install
bun run build
Load extension/output/chrome-mv3 with Load unpacked. The extension requires Chromium 114 or newer.
The extension is not on the Chrome Web Store yet.
Install the CLI
The CLI requires Python 3.14. uv can install and manage Python for you, so a system Python installation is optional.
Run without installing
uvx tabman --help
uvx runs the published package in an isolated, temporary environment.
The
tabmanname on PyPI now points at this rebuild.uvx tabmananduv tool install tabmaninstall the current release, and the one-line installers fall back to the Git branch when a release is newer than the published package.
Install persistently with uv
uv tool install tabman
tabman --help
uv tool install puts the tabman command on your PATH. Upgrade it later with uv tool upgrade tabman, and remove it with uv tool uninstall tabman.
If you are installing from Git:
uv tool install "git+https://github.com/Anshulgada/brave-tab-manager.git@main"
One-line installer
Windows PowerShell:
irm https://raw.githubusercontent.com/Anshulgada/brave-tab-manager/main/scripts/install.ps1 | iex
macOS/Linux:
curl -LsSf https://raw.githubusercontent.com/Anshulgada/brave-tab-manager/main/scripts/install.sh | sh
The installer is a complete setup, not just a download:
- It installs
uvif the machine does not have it. - It installs the CLI with
uv tool install(PyPI first, Git branch as a fallback) and verifies thattabman --versionworks. - It asks "Download the Tabman browser extension too?" and, when you say yes, downloads the latest release archive and unpacks it for Load unpacked.
- It prints the next steps: the commands to try, where the extension was unpacked, and how to finish the browser setup.
Environment variables:
| Variable | Effect |
|---|---|
TABMAN_VERSION |
Pin a version, for example 0.3.15. |
TABMAN_ASSUME_YES=1 |
Accept the extension prompt without asking. |
TABMAN_SKIP_EXTENSION=1 |
Skip the extension download entirely. |
TABMAN_EXTENSION_ROOT |
Where the extension is unpacked. |
pipx
pipx install tabman
From a checkout
uv sync
uv run tabman --help
uv sync installs Python 3.14.6 from .python-version if it is not already available.
Quick start
tabman --help
tabman config show
tabman import path\to\tabman-export.json
tabman search wxt
tabman export backup.json
tabman export backup.md --format markdown
tabman --help uses rich-click, so the command tree is rendered with colored, rounded Rich panels, grouped commands, and readable option columns.
The extension
The extension has three surfaces, and they all work without an account.
The side panel is the switchboard. It lists every open window with its tabs, searches across all of them at once, and shows the duplicates you have open in more than one place. Select tabs to close them, discard them, move them to another window, group them, or park them into the library. The Collect button saves every open tab into a new collection without closing anything, and the Saved list restores a collection back into the browser.
The library is a full page application. The home of an empty library offers Collect every open tab, which asks for a collection name and saves the open tabs into it without closing them. It has a collection rail with live counts, a command palette on Ctrl + K, a card or list view of the saved tabs, tag filters, sorting, a floating bar for bulk actions, and a dark theme you can flip from the top bar. Drag a saved tab onto a collection in the rail to move it. The Collect every open tab action at the top of the library saves what you have open right now, without closing the tabs.
The organizer asks a provider to group your open tabs. Add a provider once with its display name, base URL, model, and format, give it a key, and the organizer sends one batched request, shows you exactly what it would create, and writes nothing until you apply it. The local rules, which group by hostname, always work offline.
Live capture (optional)
The extension is the preferred way to capture open tabs. The CLI can also read Chromium's local DevTools endpoint, but Brave must first be restarted with remote debugging enabled:
brave.exe --remote-debugging-port=9222
tabman capture --name "Open tabs"
Remote debugging exposes local browser control. Do not leave the port enabled when you are not using it.
Organize
tabman organize
tabman organize --apply --yes
The first command stores a preview proposal. The second applies it and replaces the vault with the proposed collections. The default provider is deterministic, which groups tabs by hostname without contacting any service.
AI providers
Tabman speaks the compatibility formats that nearly every hosted and local model gateway implements, so you are never locked into one vendor:
| Format | Endpoint | Notes |
|---|---|---|
responses |
POST {base}/responses |
OpenAI's current API and the one their docs recommend. Used by the built-in openai provider. |
openai |
POST {base}/chat/completions |
The classic OpenAI-compatible format. OpenRouter, Groq, Together, Ollama, vLLM, LM Studio, LiteLLM, Mistral and friends. |
anthropic |
POST {base}/v1/messages |
Anthropic and Anthropic-compatible gateways. |
Add a provider once with a display name, a base URL, and the model name exactly as the provider expects it:
tabman config add-provider "Local Ollama" --base-url http://127.0.0.1:11434/v1 --model qwen3 --format openai
tabman config add-provider "My Gateway" --base-url https://gateway.example/v1 --model gpt-6-luna --format responses --env-var MY_GATEWAY_KEY
tabman config add-provider "My Claude" --base-url https://gateway.example --model claude-haiku-5-5 --format anthropic
Then add the key and use the provider by its display name:
tabman config set-key "Local Ollama"
tabman organize --provider "Local Ollama"
tabman organize --provider "Local Ollama" --apply --yes
List and remove providers:
tabman config list-providers
tabman config remove-provider "Local Ollama"
Keys are read from the environment first (OPENAI_API_KEY, ANTHROPIC_API_KEY, or TABMAN_<NAME>_API_KEY for custom providers) and otherwise from secrets.env in the user config directory.
For a one-off run you can skip the configuration entirely:
tabman organize --provider "My Gateway" --base-url https://gateway.example/v1 --model gpt-6-luna --api-format responses
Two providers are built in and only need a key:
| Provider | Default model | Default base URL | Key |
|---|---|---|---|
openai |
gpt-6-luna |
https://api.openai.com/v1 |
OPENAI_API_KEY |
anthropic |
claude-haiku-5-5 |
https://api.anthropic.com |
ANTHROPIC_API_KEY |
gpt-6-luna and claude-haiku-5-5 are the fast, cheap members of their families, which is what bulk tab grouping needs; point a custom profile at gpt-6-astra, gpt-6.1-sol, claude-sonnet-5-5, or claude-opus-5-5 when you want more reasoning for the same job.
Gemini and Mistral still work with
--provider gemini/--provider mistralwhile you hold a key, but both vendors changed their free tiers, so the compatibility providers above are the recommended path.
Data locations
- Vault:
%LOCALAPPDATA%\tabman\vault.sqlite3 - Config:
%LOCALAPPDATA%\tabman\config.json - Secrets:
%LOCALAPPDATA%\tabman\secrets.env - Extension data: Brave's IndexedDB for the Tabman extension
The extension and CLI keep separate stores. They exchange data through the shared JSON schema in packages/schema/: export from the dashboard, then run tabman import <file>, or export from the CLI and import through the dashboard.
Importing old Tabman data
The pre-0.3 JSON and all_tabs.md formats are supported with an explicit flag:
tabman import old-tabs.json --legacy
tabman import all_tabs.md --legacy
Commands
| Command | Purpose |
|---|---|
tabman import <file> |
Import a shared JSON export, or legacy data with --legacy. |
tabman export <file> |
Write JSON (default) or Markdown (--format markdown). |
tabman search [query] |
Search titles, URLs, and hosts. Table by default; use --format tsv or --format json for scripts. |
tabman capture --name <name> |
Capture tabs from a local Chromium CDP endpoint. |
tabman organize |
Preview a deterministic or BYOK AI proposal. |
tabman organize --apply --yes |
Apply the stored preview proposal. |
tabman config show |
Show non-secret settings and available key names. |
tabman config set |
Set default vault, provider, and URL-sharing options. |
tabman config set-key <provider> |
Prompt for and store a provider key. |
tabman config list-providers |
List built-in and configured providers. |
tabman config add-provider <name> |
Add an OpenAI- or Anthropic-compatible provider. |
tabman config remove-provider <name> |
Remove a configured provider. |
tabman version |
Print the installed version. |
Screens
| Library | Switchboard |
|---|---|
The dark theme, the command palette, and the bulk action bar are part of the same system.
Development
Prerequisites:
- Python 3.14.6 (managed by uv)
- Node 24+
- Bun 1.4.3+ (for the extension)
- Brave or another Chromium browser 114+
Run the checks locally
The continuous integration workflow only runs when a commit lands on main, so every branch is verified on your machine first. One command runs exactly what the workflow runs:
uv run python scripts\ci_local.py
Narrow it down when you only touched one stack, or let it work out what changed:
uv run python scripts\ci_local.py python
uv run python scripts\ci_local.py extension
uv run python scripts\ci_local.py changed
The pre-push hook runs the same gates for the files you are about to push, so a branch cannot reach main unchecked. Every script in scripts/ uses rich-click, so they all share the same look and the same options:
uv run python scripts\ci_local.py --help
uv run python scripts\pre_push.py --help
uv run python scripts\preview_ui.py --help
uv run pytest
uv run ruff check .
uv run ruff format --check .
uv run ty check
uv run bandit -q -r src
cd extension
bun run check
bun run typecheck
bun run test
bun run build
Run the workflow itself locally
act (MIT) runs the real workflow file in a container, so a broken YAML or a bad action version fails before you push:
winget install nektos.act # Windows
brew install act # macOS
sudo apt install act # Debian and Ubuntu
uv run python scripts\ci_local.py --act
The same works as a subcommand: uv run python scripts\ci_local.py act.
act needs Docker or Podman. On Windows, Podman is the free option. Two more open source tools are worth knowing:
- actionlint checks the workflow files for mistakes without running them.
- zizmor audits the workflows for security problems such as unpinned actions and template injection.
Both are part of the local gate, and the workflow job on main runs them again:
uv run zizmor --persona=regular --min-severity=low .github/workflows
actionlint
zizmor is already a development dependency, so uv run zizmor works out of the box. actionlint is a single binary:
winget install rhysd.actionlint # Windows
brew install actionlint # macOS
go install github.com/rhysd/actionlint/cmd/actionlint@latest
The act run needs a working container engine. If Docker reports that it cannot start on Windows, the WSL kernel modules are the usual cause, and wsl --update in an elevated prompt followed by a restart fixes it. The native gate above does not need a container at all.
The workflows pin every action to a commit hash, keep the checkout credentials out of the workspace, and disable the caches in the release job, so a poisoned cache cannot reach a published artifact.
This repository is public, so GitHub-hosted runners are free and unlimited for it. The workflow still runs only on
mainto keep the queue short and the feedback local.
Everyday commands
uv sync
uv run pytest
uv run ruff check .
uv run ruff format --check .
uv run ty check
uv run bandit -q -r src
uv run pip-audit
uv run detect-secrets-hook --baseline .secrets.baseline
Extension checks:
cd extension
bun install
bun run check
bun run typecheck
bun run test
bun run build
Version sync check:
uv run python scripts/check_versions.py
Documentation
| File | What it covers |
|---|---|
| PRIVACY.md | What is stored, what leaves the machine, and how to remove it |
| SECURITY.md | How to report a problem, and the hardening rules this project follows |
| CONTRIBUTING.md | The setup, the gate, and the rules for a pull request |
| CODE_OF_CONDUCT.md | How we treat each other |
| docs/architecture.md | How the two surfaces fit together |
| docs/dos-and-donts.md | The mistakes this project has already made |
| docs/TABMAN-REBUILD-PLAN.md | The original rebuild plan |
| docs/TABMAN-REBUILD-TASKS.md | The phase checklist |
Release process
-
Update the version in both
pyproject.tomlandextension/package.json. They must match. -
Run
uv lock, then the test and lint commands above. -
Commit and tag the release:
git tag v0.3.16 git push origin v0.3.16
-
.github/workflows/release.ymlbuilds the wheel/sdist and extension zip, publishes to PyPI, and creates a GitHub Release with the install scripts.
The old pypi-publish.yml workflow was removed: it published on every push to main and would race with the tag-driven release. release.yml is now the only path that touches PyPI.
One-time PyPI setup
The workflow uses PyPI trusted publishing, so no password or API token is stored in GitHub. The tabman project already has a trusted publisher; on PyPI, open the project's Publishing settings and check that it reads:
- Owner:
Anshulgada - Repository:
brave-tab-manager - Workflow:
release.yml - Environment:
pypi
The matching pypi environment lives in the GitHub repository settings. If the PyPI name is not owned by this project, use a unique distribution name and update the workflow URL and the install commands here.
License
MIT
Metadata
Release files for tabman 0.4.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| tabman-0.4.2.tar.gz | 38.6 kB | With provenance | Details for tabman-0.4.2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| tabman-0.4.2-py3-none-any.whl | Python 3 | none | any | Details for tabman-0.4.2-py3-none-any.whl |
Total release size: 76.5 kB
Release files / tabman-0.4.2.tar.gz
| Download URL | tabman-0.4.2.tar.gz |
|---|---|
| Size | 38.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bee3332ca3b615fe9da18c651599e0c304db0c30ad8cb309381570ceb1f2f8f7
|
|
BLAKE2b-256 checksum How to use checksums |
80ecb15d81964563e387356a697835cf455fee47fab66d70919f3d79bcb44bc2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logRelease files / tabman-0.4.2-py3-none-any.whl
| Download URL | tabman-0.4.2-py3-none-any.whl |
|---|---|
| Size | 38.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d252aaf217132cc43676ab32684e7efbea90d4d23194a4b6bef4b62c557e3271
|
|
BLAKE2b-256 checksum How to use checksums |
0feef484a698cfa92ffebb2d2ed933e440cc2bb2a3e035e8097bb63efaa3328b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logSecurity for tabman 0.4.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for tabman-0.4.2.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.