Enable, disable, and reload Chrome extensions from the terminal or Vicinae.
crxctl list
crxctl disable "Dark Reader"
crxctl enable "Dark Reader"
crxctl reload RepoAuracrxctl turns the controls hidden in chrome://extensions into a small typed
interface that works for people, scripts, and coding agents. Its Vicinae command
is a searchable keyboard-first view over the same control code and activity
history.
list,enable,disable,reload, anddoctorcommands.- Stable JSON output for scripts and agents.
- Exact extension-ID targeting and convenient, ambiguity-safe name targeting.
- A searchable Vicinae list with real extension icons and enabled/disabled filters.
- Frequently managed extensions ranked first across both the CLI and Vicinae.
- A protected bridge that cannot disable or reload itself.
- No uninstall operation, webpage access, analytics, account, or cloud service.
Dark Reader 3× v4.9.129 Enabled ● → disable, reload
React Developer Tools v7.0.1 Enabled ● → disable, reload
SingleFile v1.23.1 Disabled ○ → enable
The rows above come from the included Vicinae fixture. Fixture actions remain in memory and never contact Chrome or alter the real activity log.
- Linux with Google Chrome or Chromium.
- Python 3 for the native-messaging host.
- Bun to build and install the single-file CLI and Vicinae bundle.
- Vicinae is optional and required only for the launcher interface. Version 0.24.0 is the currently tested runtime.
- Node.js 22 is used by the development test suite.
git clone https://github.com/testy-cool/crxctl.git
cd crxctl
./install.sh --browser chromeUse --browser chromium for Chromium. The installer:
- registers the narrow native-messaging host for the current user;
- compiles and installs
crxctlunder$XDG_BIN_HOMEor~/.local/bin; - installs the Vicinae package under
$XDG_DATA_HOME/vicinae/extensions/crxctlor~/.local/share/vicinae/extensions/crxctl; - preserves replaced files under
$XDG_STATE_HOME/crxctl/backups; - prints the remaining manual Chrome step.
Inspect the exact paths without changing anything:
./install.sh --browser chrome --dry-runNow open chrome://extensions, enable Developer mode, choose Load unpacked,
and select this repository's chrome-bridge/ directory. Its ID must be:
kmkokakhnlegbconjfepgdbomaakfacc
The ID is derived from the public key committed in manifest.json; it is not a
credential. If Chrome was running while the host was registered, restart Chrome
once. Then verify the complete local path:
crxctl doctorIf ~/.local/bin is not on PATH, invoke ~/.local/bin/crxctl or add that
directory to your shell configuration.
crxctl list [--json]
crxctl enable <id-or-name> [--json]
crxctl disable <id-or-name> [--json]
crxctl reload <id-or-name> [--json]
crxctl doctor [--json]
Names match exactly and case-insensitively. If two installed extensions share a
name, crxctl refuses to guess and asks for the stable Chrome extension ID.
Enabling an enabled extension and disabling a disabled extension are successful
no-ops. Reload is available only for enabled extensions and is truthfully
implemented as disable followed by enable because chrome.management exposes
no direct reload method.
The CLI never prompts. stdout contains only the requested result; warnings and
errors use stderr. Exit 0 means success, 1 an operational failure, and 2
invalid usage. See the frozen CLI contract for JSON schemas,
error identifiers, state semantics, and compatibility guarantees.
The repository includes a portable crxctl agent skill
that teaches coding agents to reload the extension they just built, verify the
JSON result, avoid touching unrelated extensions, and handle bridge failures
without repeatedly interrupting the user.
Install it for Codex from a working clone so updates to the clone remain live:
skill_root="${CODEX_HOME:-$HOME/.codex}/skills"
mkdir -p "$skill_root"
ln -s "$(pwd)/skills/crxctl" "$skill_root/crxctl"Other agents that support SKILL.md packages can load the same directory. The
skill contains no extension inventory, credentials, or machine-specific paths.
Search for Manage Chrome Extensions.
Enter: enable or disable the selected extension.- Vicinae's refresh shortcut: reload an enabled extension.
- Vicinae's copy shortcut: copy the extension ID.
Ctrl+Shift+R: refetch the extension list.Ctrl+Pon Vicinae 0.24.0: open theAll/Enabled/Disabledfilter.
Policy-installed extensions that Chrome will not allow users to disable expose no mutation actions. Enable Use fixture data in the command preferences to judge the interface without installing the Chrome bridge.
Only a Chrome extension with the management permission can control other
extensions. crxctl keeps that privileged boundary small:
terminal CLI ─┐
├─ shared typed control ─ user-only socket ─ native host ─ Chrome bridge
Vicinae UI ───┘
shared/owns the socket contract, target resolution, mutation rules, ranking model, and local activity log.cli/is the scriptable interface and compiles to one local executable.vicinae/is the searchable human interface over the shared control code.host/is a dependency-free Python native host accepting onlylist,setEnabled,reload, andicon.chrome-bridge/is a Manifest V3 extension usingchrome.management,nativeMessaging, andoffscreen.
The internal native-host name and socket filename retain their original Vicinae-era identifiers so upgrades do not break already-installed bridges. They are compatibility details, not separate products.
The Chrome bridge requests:
management: list and enable or disable extensions;nativeMessaging: connect to the local native host;offscreen: render Chrome-internal extension icons.
It requests no host permissions and cannot read webpages. The native host has no shell, arbitrary argv, file, URL, network, or generic-command operation. Requests are schema-checked before Chrome sees them, and the socket is accessible only to the current user.
Successful actions append extension ID, action kind, and timestamp to
$XDG_STATE_HOME/crxctl/activity.jsonl or
~/.local/state/crxctl/activity.jsonl. Existing Vicinae-only action counts are
still included in ranking after an upgrade. Icon PNGs are cached by extension ID
and version under $XDG_CACHE_HOME/crxctl/icons or ~/.cache/crxctl/icons.
These files remain local and nothing is transmitted to this project or its
maintainers.
See SECURITY.md for the full boundary and private reporting path.
First remove crxctl Bridge from chrome://extensions. Then remove the
installed files for Chrome:
rm "${XDG_BIN_HOME:-$HOME/.local/bin}/crxctl"
rm -rf "${XDG_DATA_HOME:-$HOME/.local/share}/vicinae/extensions/crxctl"
rm "${XDG_CONFIG_HOME:-$HOME/.config}/google-chrome/NativeMessagingHosts/io.github.testy_cool.vicinae_chrome_extensions.json"For Chromium, replace google-chrome with chromium. Optional local ranking,
cache, and upgrade backups can then be removed from
${XDG_STATE_HOME:-$HOME/.local/state}/crxctl and
${XDG_CACHE_HOME:-$HOME/.cache}/crxctl.
(cd cli && bun test && bun run build)
(cd vicinae && bun install --frozen-lockfile && bun test && bun run build)
(cd chrome-bridge && node --test tests/*.mjs)
(cd host && python3 -m unittest test_host)The isolated browser proof loads the real bridge and a dummy target into an owned Chromium profile, exercises both control paths, waits beyond the service worker idle interval, and deletes only its own resources:
E2E_IDLE_SECONDS=40 bun run e2e/run.tsContributions are welcome; start with CONTRIBUTING.md.
MIT