hue-shift-cli
v1.2.1
Published
Give every VS Code project its own window colour. A Peacock alternative that runs from the terminal, with no extension.
Maintainers
Readme
hue-shift
Peacock colors one workspace. hue-shift colors all of them.
Give every VS Code project its own window colour from the terminal. No extension, no config file to hand-edit, no dependencies.
npx hue-shift-cli --randomWhy not Peacock?
Peacock is great. It is also an extension, so it can only colour the workspace you are currently looking at. If you have 75 projects and want each one to look different, that is 75 manual actions.
hue-shift is a CLI. It walks your projects folder and writes the same workbench.colorCustomizations keys Peacock uses — so the two are compatible — but it does every project in one pass.
| | Peacock | Project Colors | Multi-Workspace | hue-shift |
|---|---|---|---|---|
| Runs from the terminal | ✗ | ✗ | ✗ | ✓ |
| npx, nothing to install | ✗ | ✗ | ✗ | ✓ |
| Colour all projects at once | ✗ | ✗ | ✗ | ✓ |
| Detect drift (--check) | ✗ | ✗ | ✗ | ✓ |
| Contrast validation | ✗ | ✗ | ✗ | ✓ |
| Colour + theme + icons | ✗ | ✗ | theme only | ✓ |
| Zero runtime dependencies | ✗ | ✗ | ✗ | ✓ |
| Works on macOS, Linux, Windows | ✓ | ✓ | ✓ | ✓ |
Measured on an M1 Mac: 75 projects in 0.08 s, including reading and rewriting every settings.json. No build step, no daemon, no extension host.
Install
Nothing to install — run it directly:
npx hue-shift-cli --randomOr install it:
npm i -g hue-shift-cli # global: gives you the `hue-shift` command
npm i -D hue-shift-cli # per project, for npm scripts
bunx hue-shift-cli --random # bun
pnpm dlx hue-shift-cli --random # pnpmThe package is
hue-shift-clibecause npm rejectshue-shiftas too similar to an existinghueshift. Once installed, the command is stillhue-shift.
Requires Node 18 or newer. Works anywhere that reads .vscode/settings.json: VS Code, Cursor, Windsurf, VSCodium.
Quick start
Colour the project you are standing in:
cd ~/code/my-app
npx hue-shift-cli --randomColour every project under a folder, reproducibly:
npx hue-shift-cli --root ~/Documents/projects --random --seed 42See what it would do before it does it:
npx hue-shift-cli --root ~/Documents/projects --random --seed 42 --dryPick a specific colour and only colour the title bar:
npx hue-shift-cli . --color amber --parts titleColour plus a full theme and icon set:
npx hue-shift-cli . --color teal --theme "Default Dark Modern" --icons SetiReload the window (Cmd+Shift+P → Developer: Reload Window) and the colour appears.
Commands
hue-shift [project] [options] one project (default: current directory)
hue-shift --root <dir> [options] every subfolder of <dir> as a projectColour
| Flag | What it does |
|---|---|
| --random | random colour per project (later --root runs only colour new projects) |
| --seed <n> | make --random reproducible |
| --color <hex\|name> | a specific colour |
| --list-colors | print the built-in palette |
Appearance
| Flag | What it does |
|---|---|
| --parts <list> | title,activity,status,border,tab,sash,sidebar · default title,activity,status |
| --adjust <part=mode> | lighten or darken per part, e.g. title=lighten,status=darken |
| --adjust-pct <n> | how far to shift, default 6 |
| --exclude <key> | never write this colour key (repeatable) |
| --theme <name> | also set workbench.colorTheme |
| --icons <name> | also set workbench.iconTheme |
Checks
| Flag | What it does |
|---|---|
| --check | report drift; writes nothing |
| --reset | remove every colour key hue-shift wrote |
| --restore | put settings.json back as it was before the first apply |
| --dry | print the plan; writes nothing |
| --json | machine-readable output |
Exit codes: 0 success, 1 usage error, 2 a project failed (including --restore with no backup).
Named palette
amber #b45309 magenta #a21caf
angular-red #dd0531 mandalorian-blue #1857a4
azure-blue #007fff node-green #215732
burnt-orange #c2410c olive #4d7c0f
crimson #9f1239 peacock-blue #0f4c81
cyan #0e7490 react-blue #61dafb
green #15803d rose #be123c
javascript-yellow #f9e64f slate #334155
svelte-orange #ff3d00
teal #0f766e
violet #6d28d9
vue-green #42b883Every generated colour is checked against its foreground, so text on the title bar never drops below 4.5:1 contrast.
With --random, up to 16 projects get hues at least 22° apart, which keeps them clearly distinguishable. Beyond 16 the colour wheel cannot hold that gap, so hues spread as far apart as possible.
Config file
Pinned colours while you are away from the keyboard. hue-shift reads two files and merges them, project winning over global. CLI flags win over both.
~/.hue-shift.json — defaults for every project:
{
"parts": ["title", "activity", "status"],
"adjust": { "activity": "lighten" },
"adjustPct": 6,
"theme": "Default Dark Modern",
"icons": "Seti"
}<project>/.hue-shift.json — this project only:
{
"color": "teal",
"parts": ["title"],
"exclude": ["statusBar.background"]
}Fields: color, random, seed, parts, adjust, adjustPct, exclude, theme, icons.
What it writes
Only this block, and only the keys for the parts you asked for:
{
"workbench.colorCustomizations": {
"titleBar.activeBackground": "#b45309",
"titleBar.activeForeground": "#ffffff",
"titleBar.inactiveBackground": "#b45309cc",
"titleBar.inactiveForeground": "#ffffff99",
"activityBar.background": "#b45309",
"activityBar.foreground": "#ffffff",
"activityBar.inactiveForeground": "#ffffff88",
"activityBar.activeBackground": "#b45309",
"statusBar.background": "#b45309",
"statusBar.foreground": "#ffffff",
"statusBar.debuggingBackground": "#b45309",
"statusBar.noFolderBackground": "#b45309"
}
}In --root mode it also writes hue-shift.lock.json in the root directory. The lock records each project's colour so later runs do not recolour projects that are already there.
Your settings.json is treated carefully:
- Merged, not replaced. Colour keys hue-shift does not own are preserved.
- Comments survive. JSONC
//and/* */outside the colour block are untouched. - BOM and CRLF survive. Windows line endings stay Windows line endings.
- Peacock stays in sync. If
peacock.colorexists, it is updated to match. --resetis exact. It removes only the keys listed above and leaves everything else.--restoreis exact. The first apply saves the original file to.vscode/.hue-shift-backup.json; restore writes it back byte for byte. If you commit.vscode, add.hue-shift-backup.jsonto.gitignore— it is a copy of your settings.
What it can't do
Honest limits, because a CLI is not an extension:
- No status-bar colour chip. That needs the extension host.
- No Live Share host/guest colours. Needs the VS Code API at runtime.
- No "surprise me on startup". A CLI only runs when you run it.
- No in-editor command palette entries. Use your terminal.
If you want those, run Peacock alongside. They write the same keys, so they do not fight.
Real workflow
Colours that stay put, with drift detection in CI:
# once, assign a palette and write the lock
cd ~/Documents/projects
npx hue-shift-cli --root . --random --seed 42
# later, after adding a project: existing colours stay put
npx hue-shift-cli --root . --random --seed 42
# fail a build when a project has drifted or has no colour
npx hue-shift-cli --root . --checkThe first bulk run writes hue-shift.lock.json in the root. Later runs reuse those colours, so adding or removing folders never recolours an existing project. --seed only affects projects that are not in the lock yet. If the root is a git repository, commit the lock so a fresh clone gets the same palette. Delete it to re-roll every colour.
--check uses the lock as the expected palette and exits non-zero when a project has drifted or has no colour, so it works as a pre-commit or CI guard.
Examples
examples/ contains runnable scripts:
| File | Shows |
|---|---|
| basic.sh | one project, one colour |
| bulk-seeded.sh | all projects, reproducible |
| monorepo.sh | nested packages with a shared palette |
| ci-drift.sh | fail a build when colours drift |
| config-file/ | a pinned .hue-shift.json |
FAQ
Does it work alongside Peacock?
Yes. Both write the same workbench.colorCustomizations keys. hue-shift also updates peacock.color if that setting exists, so Peacock does not fight back when it activates.
Will it overwrite my own colour customizations?
No. It merges. Only the keys listed in What it writes are touched; anything else in workbench.colorCustomizations stays.
How do I undo it?
# revert settings.json to exactly what it was before the first apply
npx hue-shift-cli --root ~/projects --restore
# or just remove hue-shift's colour keys, keeping everything else
npx hue-shift-cli --root ~/projects --reset--restore uses the one-time backup made before the first write. --reset is narrower: it removes only the keys hue-shift wrote, and leaves your other settings, comments, and line endings alone. Neither touches hue-shift.lock.json.
How do I re-roll all colours?
Delete hue-shift.lock.json from the root and run the command again. To re-roll one project, remove its entry from the lock.
Why is the npm package called hue-shift-cli?
npm rejects hue-shift as too similar to an existing hueshift package. The command it installs is still hue-shift.
Two projects got the same colour. Why?
The wheel holds 16 hues at 22° apart. Past 16 projects the gap shrinks, and near-identical colours are possible. Use --color per project if you need exact control.
Do I have to run it every time I open a project?
No. It writes to settings.json, which VS Code reads on startup. Run it once per project, or again when you add projects.
Does it work over SSH or in a remote window? Yes, if the remote machine's filesystem is where the project lives. Run it on the remote, not locally.
Does it touch anything outside .vscode/settings.json?
It creates that file and directory if missing. On the first write it saves .vscode/.hue-shift-backup.json, which --restore uses. In --root mode it writes hue-shift.lock.json in the root. Nothing else.
Troubleshooting
The colour did not appear.
Reload the window: Cmd+Shift+P → Developer: Reload Window. Settings are read at startup.
could not parse colorCustomizations.
The colour block in that project's settings.json is malformed. Fix the JSON, or run --reset to remove the block entirely.
EACCES or permission denied.
The settings file is owned by another user (common in Docker or sudo-created projects). Fix ownership, or run with the right user.
Text on the title bar is hard to read.
hue-shift picks black or white text automatically and keeps contrast above 4.5:1. If a theme overrides it, add that key to exclude in .hue-shift.json.
Colours changed after a git pull.
Someone committed a different settings.json. Re-run the same command with the same --seed to restore, and use --check in CI to catch it.
Development
git clone https://github.com/aotr/hue-shift.git
cd hue-shift
npm testNo dependencies to install. Tests use the built-in node:test runner.
src/colors.js colour maths, palette, seeded random
src/lock.js the stable --root palette
src/settings.js JSONC-safe read / merge / write / reset / check
src/cli.js argument parsing and the runner
bin/hue-shift.js entry point121 tests covering hex parsing, HSL round trips, contrast, JSONC edge cases, BOM/CRLF preservation, merging, reset/restore, the palette lock and every CLI flag.
Contributing
Bug reports, palette additions, and platform fixes are welcome. See CONTRIBUTING.md for the setup, the test commands, and what a good pull request looks like.
git clone https://github.com/aotr/hue-shift.git
cd hue-shift
npm test # 121 tests, no install neededLicense
MIT
