accshift ships as a command-line binary alongside the desktop app, for
scripting, Stream Deck macros and AI automation. It reads and writes the same
config as the GUI: running both at once is safe thanks to an exclusive lock on
mutating operations.
Windows
The desktop installer ships the CLI next to the app and adds the install
directory to your user PATH, so accshift works in any new terminal right
after install. A standalone accshift-cli_<version>_x64.exe binary is also
available on Releases.
Linux
The deb and rpm packages install the CLI to /usr/bin alongside the app. A
standalone accshift-cli_<version>_linux_x86_64 binary is also on Releases.
macOS
The .app bundles the CLI inside Contents/MacOS. Symlink it onto your PATH,
for example into /usr/local/bin. A standalone
accshift-cli_<version>_macos_aarch64 binary is also on Releases.
Building from source (cargo build --release -p accshift-cli) produces the binary at
<target-directory>/release/accshift, or accshift.exe on Windows. The target
directory defaults to target/; Cargo configuration or CARGO_TARGET_DIR can
override it.
accshift platforms # list platforms known to this build
accshift list <platform> # list accounts for a platform
accshift list <platform> --folder <name>
accshift switch <platform> <account-id>
[--online | --invisible]
[--graceful | --force]
[--admin | --no-admin]
[--launch-options "..."]
accshift dry-run <platform> <account-id>
accshift descriptors # what the user descriptor folder holds
accshift diag check # check local health invariants
accshift diag logs --since 30m # inspect recent log records
accshift diag bundle # write a local diagnostic reportEvery one of these needs the "Allow the accshift CLI" toggle in the app
(Settings > General > Integrations). With it off they all exit 7 and do
nothing, diag included.
--folder matches a name within the selected platform, without case
sensitivity, and includes accounts in nested folders. Duplicate names are
ambiguous and cause an error. Folder IDs and paths are not accepted.
The full diag command reference is in logging.md.
--graceful asks the launcher to close itself and waits for it, which is what
you want by default because a launcher killed mid-write can corrupt its own
config. --force terminates it instead, for the cases where it will not go.
dry-run prints the switch instead of performing it: every file, folder and
registry value it would read, copy back or delete, every process it would
close, and the launcher it would start. It walks the same descriptor the real
switch walks, so the two cannot disagree. It opens nothing for writing and
takes no lock, so it is safe to run at any time, including while the GUI is
busy.
Platforms still implemented in code (Steam, Battle.net, Riot, Roblox) have no
plan to show and answer dry_run_unsupported.
descriptors reads the folder where a user drops platforms of their own and
reports both halves: the descriptors that loaded, and every file that was
refused with the field that caused it. A platform missing from platforms is
explained here rather than silently absent. It exits zero either way, rejected
files included: the command was asked what the folder holds and it answered, so
a script reads rejected instead of guessing from a status that would also mean
"could not look". The format itself is in
platform-descriptors.md.
Example:
$ accshift list steam
ACCOUNT NAME STEAM ID
* alice Alice 76561198000000001
bob Bob the Builder 76561198000000002
carol carol_gg 76561198000000003
3 accounts. * = currently signed in
$ accshift dry-run gog 51000000000000000
Dry run: switch gog to 51000000000000000. Nothing below is written.
Roots (a step outside these is refused):
C:\Users\you\AppData\Local\GOG.com
C:\ProgramData\GOG.com
capture C:\Users\you\AppData\Local\GOG.com\Galaxy\Configuration\config.json <- ...\snapshots\51000000000000001\config.json
capture HKCU\Software\GOG.com\Galaxy\refreshToken <- ...\snapshots\51000000000000001\registry_refresh_token.txt
close GalaxyClient.exe
restore C:\Users\you\AppData\Local\GOG.com\Galaxy\Configuration\config.json <- ...\snapshots\51000000000000000\config.json
launch C:\Program Files (x86)\GOG Galaxy\GalaxyClient.exe
The default output is a readable table for humans on a TTY, and switches to JSON automatically when stdout is piped, so scripts and AI tools get a stable contract without passing an extra flag.
--jsonforces the JSON envelope everywhere, TTY included.- Errors always go to stderr, so stdout stays parseable even on failure.
The schema field is versioned. Consumers should check it rather than assume:
a future incompatible shape ships as accshift.v2, and accshift.v1 keeps
meaning what it means here.
{
"schema": "accshift.v1",
"ok": true,
"command": "list",
"data": {
"platform": "steam",
"folder": null,
"accounts": [],
"current": null
}
}{
"schema": "accshift.v1",
"ok": false,
"command": "switch",
"error": {
"code": "lock_contended",
"message": "Another accshift instance is running. Retry once it finishes, or close the GUI."
}
}| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Generic error |
| 2 | Unknown platform on this OS |
| 3 | Unknown account |
| 4 | Another accshift instance holds the lock |
| 5 | I/O error (paths, permissions) |
| 6 | PIN missing, unavailable, or incorrect |
| 7 | CLI disabled in Settings |
Code 4 is retryable: the GUI and the CLI share one config, so a mutating operation takes an exclusive lock and a second one waits rather than corrupting it. Retry once the other instance finishes.
Codes 6 and 7 are deliberate refusals, not failures. Every subcommand respects the "Allow the accshift CLI" setting. Switching also checks the PIN lock configured in the app. An automated pipeline that starts returning 7 has been disabled through that setting. What the PIN lock does and does not protect is covered in the security policy.
Code 7 covers every subcommand, platforms, descriptors and all of diag
included, and the check runs before the command is handed its arguments, so a
refused run reads nothing and writes nothing. There is no exemption list: the
CLI_GATE_EXEMPT const in crates/accshift-cli/src/main.rs is empty on
purpose. Until 1.0.4 the toggle only covered list, switch and dry-run, so
diag bundle still wrote a report carrying the redacted config summary on a
machine whose owner had switched the CLI off. Diagnostics stay reachable
without the CLI: the app has its own diagnostics screen.
A settings file that exists but cannot be read, with no usable .bak beside
it, leaves the toggle unknown. Every subcommand then exits with 5 rather than
assuming the CLI is allowed.