clikae is a verb (切り替え, switching). The headline action carries no verb of its own — the program name is the verb:
clikae <engine> <tank> # switch <engine> to <tank> and run itAn <engine> is a CLI with an adapter (run clikae adapters); a <tank> is a
name you choose (A-Z a-z 0-9 . _ - allowed) for one account/config. The fuel
metaphor runs throughout: a tank holds an engine's quota (its fuel); when a
tank runs dry you carry your work onward with clikae to.
Run clikae help <command> for the full per-command reference. The full design
of the language is in grammar.md.
# Create a tank for Claude Code, and add the matching shell alias
clikae init claude work --alias
# Switch to it and run — the bare verb (no `run` needed)
clikae claude work
clikae claude work -- --help # args after -- go straight to the engine
# Or pick up the alias and use that
source ~/.zshrc # or your rc file
claude-work
# Generate a macOS launcher you can double-click from ~/Applications
clikae app claude work
# See what you've got
clikae tanks # alias: clikae list
# ENGINE TANK
# claude work
clikae tanks -p # also print the tank directory paths
# Tear it all down (tank dir + alias + .app, asks to confirm)
clikae remove claude workclikae is the verb, so switching needs no verb; management commands keep plain, conventional verbs.
| Command | What it does |
|---|---|
<engine> <tank> [-- args] |
Switch <engine> to <tank> and run it. The bare verb. (run is a hidden alias.) |
<engine> <tank> --ephemeral |
Switch and run with ephemeral memory — this session's long-term memory is a throwaway, discarded on exit; the tank's real memory is left untouched. Login + transcripts are normal. claude only (clikae must know the memory layout). See below. |
<engine> |
One tank → use it; several → list them; none → offer to create. |
to <target> [tank] [-- args] |
Carry this shell's current session onto another tank. Same engine → a real resume; a different engine → a written brief (cold start). clikae announces which. Source is auto-detected (env var, else this directory's most recent session). Forwards relay's -y/--fresh/--session. (relay/handoff/continue are hidden aliases.) |
eval "$(clikae env <engine> <tank>)" |
Put the current shell on a tank (export its config env var), so the engine's own command and clikae status/to see it. The explicit alternative to the one-shot bare switch. |
| Command | What it does |
|---|---|
init <engine> <tank> [--alias] |
Create the tank directory; with --alias, also write a shell alias. |
remove <engine> <tank> [--force] [--keep-data] |
Remove dir + alias + .app. --keep-data keeps the directory. |
rename <engine> <old> <new> [--force] |
Rename a tank (moves the dir, rewrites the alias, carries the login). |
migrate [<engine>] [--dry-run] [--force] [--keep-login] |
Adopt a hand-rolled config-dir + alias setup. |
alias <engine> <tank> [--name <n>] |
Write (or replace) a shell alias. Default name <engine>-<tank>. |
app <engine> <tank> [--terminal <app>] [--force] [--out <dir>] |
Generate a macOS .app launcher (default ~/Applications). macOS only. --terminal: terminal (default), iterm2, ghostty. |
app --board [--terminal <app>] [--force] [--out <dir>] |
Generate a clikae.app that opens the board (the menu of recent sessions + tanks) instead of one tank — a single double-click button for the whole on-ramp. |
Ghostty launchers pass their command through a trusted Ghostty config file (
--config-file=), not-e. Ghostty pops an "Allow Ghostty to execute…?" dialog for an externally-injected-ecommand (so a-elauncher looks like an empty shell until you click Allow); a config file is trusted, so the window just opens. The config lives inside the.appand is found viapath to me, so the launcher keeps working if you move it.
| Command | What it does |
|---|---|
to [target] [tank] |
Carry this shell's session onward when a tank runs dry. Bare clikae to falls through to the next tank in your burn order (same engine → a real resume; a different engine → a cold-start brief). Your tanks are the reserve — nothing to configure. |
auto [ask|safe|full] |
(BETA, claude-launched sessions only) How much clikae carries on its own when a session you launched through clikae hits the limit — it has no effect on alias/.app/other-engine launches. ask (default) prompts; safe auto-resumes same-engine + asks to cross; full keeps going (same-engine = resume, cross-engine = a cold brief). The board's A key cycles it. |
watch <engine> [<tank>] [--auto] [--to <target>] |
Watch a session and fall through to the next tank in the burn order when it runs dry (cross-engine via --to). |
burn <engine> <tank> --artifact <path> -- <cmd…> |
Run a headless task on a tank; verify it by the artifact (not the exit code); on a dry tank, re-fire the same task on the next reserve tank. The headless sibling of to/watch. See "Headless tasks" below. |
Supervised launch (BETA · claude · feedback welcome). When you start claude through clikae, clikae stays as the parent. When that session ends after hitting its limit — quit the dead session in an interactive run; a headless
claude -pexits on its own — clikae carries you onward to the next tank in your burn order (perclikae auto) in the same terminal (one redraw), and your conversation continues there. Honest limits: it advances on exit, not by killing a live session mid-stream (that needs engine support — see issue anthropics/claude-code#35744); one hop per run; interactive codex can't be auto-detected (no file signal) so it's claude-only for now. Nothing runs in the background unless you launched it through clikae (no daemon) — deliberate.clikae statusshows what it carried (recent carries). Tell us how it feels.
| Command | What it does |
|---|---|
| (no args) | Open the home dashboard — your "tank board": every tank grouped by engine, the one active in this shell marked, account + alias name, an "Also available" list of engines/targets you can open without a tank (e.g. codex, agy). On a terminal it's an interactive launcher; press ? for the full key legend. Keys: ↑/↓·j/k·Tab/Shift-Tab move, g/G top/bottom, 1-9 jump, ⏎ open (a Continue row offers resume vs switch fresh), r carry session, x incognito, n new, a rename the tank, d delete, / filter, l pick language, q/Esc quit. Piped/scripted it prints the same board as plain text (CLIKAE_NO_INTERACTIVE forces that). |
lang [en-US|ja-JP|zh-TW] |
Show or set the interface language (dashboard + prompts). Persists to $CLIKAE_HOME/lang; the board's l key opens a language picker. Resolution when unset: $CLIKAE_LANG > saved choice > $LC_ALL > $LANG > en-US. |
tanks [-p|--paths] [--json] |
List all tanks, with the logged-in account where the adapter can tell. (Aliases: list, ls.) --json emits machine-readable output ({cli, profile, account, path}) for scripts and the GUI. |
status [<engine>] [--json] |
Show which tank each engine is on in this shell. --json emits one object per engine with a state enum. |
doctor |
Read-only health check: which supported engines are installed and logged in, how many tanks each has, the environment, and what to do next. |
info [--json] |
Show install paths, platform, adapters, and tank count. |
adapters |
List supported engines with descriptions. |
demo |
A 30-second guided tour in a throwaway sandbox — shows isolated tanks, the tank board, and the to idea (your tanks are the reserve), then cleans up. Touches nothing real; the accounts are simulated, so it needs no installed engine. |
agy hardcodes ~/.gemini and ignores env vars, so clikae can't switch it
per-shell like other engines. It folds into the same verbs anyway, via an
opt-in symlink-swap power mode (global: one tank active at a time across all
terminals; reversible):
| Command | What it does |
|---|---|
init agy <tank> |
First time: warns and asks before taking ~/.gemini over (backs it up, migrates your current login into a default tank), then creates <tank>. After: just creates the tank. |
agy <tank> |
Switch the active tank (refuses if agy is running) and start agy. Prints a global-switch notice. |
remove agy <tank> |
Remove the tank. Removing the last tank offers to restore a normal ~/.gemini and turn the power mode off. |
agy --release |
Restore a normal single-account ~/.gemini from the active tank, keep the tank dirs. |
clikae auto-detects your shell from $SHELL and writes the alias to the right
rc file: zsh (~/.zshrc), bash (~/.bash_profile on macOS, else
~/.bashrc), and fish (~/.config/fish/config.fish). For fish it emits fish
syntax — alias <name> 'env VAR=val <binary>' — because fish has no inline
VAR=val cmd; the result behaves identically. clikae remove cleans up the
block in any of them.
Already juggling accounts by hand — say a ~/.claude-acct-a / ~/.claude-acct-b
pair with aliases in your ~/.zshrc? clikae migrate adopts that into clikae:
clikae migrate --dry-run # preview: which dirs move where, which aliases change
clikae migrate # do it (asks to confirm first)It scans your shell rc for aliases that set the engine's config env var and invoke the engine. For each one it:
- moves the referenced config directory under
~/.clikae/profiles/<engine>/<p>/, - rewrites the alias into clikae's managed sentinel block.
The rc file is backed up to <rc>.clikae.bak.<timestamp> first, and an existing
clikae tank is never overwritten. Pass an engine name (clikae migrate gh) to
migrate a different tool's aliases. Default is claude.
⚠️ Don't migrate a config dir that's currently in use.migratemoves the directory, so if a process is running against it right now (e.g. you runclikae migratefrom inside the veryclaudesession whoseCLAUDE_CONFIG_DIRpoints at the dir being moved), you pull the directory out from under that live process — it can fail to write, or recreate an empty dir at the old path and leave you with two half-states. Runmigratefrom a fresh shell with no instance of that engine active.--dry-runis always safe.As of v0.4,
migrateguards against the most common form of this: if$CLAUDE_CONFIG_DIR(or whichever env var the adapter uses) currently points at a directory slated to move, it refuses and tells you to retry from a fresh shell. The guard is not bypassed by--force— it protects your data, it isn't a confirmation prompt.
🔑 macOS + claude: expect a one-time re-login per migrated tank. On macOS, Claude Code keeps its login token in the login Keychain, not inside
CLAUDE_CONFIG_DIR— and the keychain entry is keyed by the config-dir path. Becausemigratemoves the dir to a new path, claude no longer finds the token and asks you to log in once for each migrated tank. Your data is intact; only the saved login doesn't follow the move. To avoid the re-login, pass--keep-login, which copies the saved token from the old path's keychain entry to the new one (macOS only; it never reads or transmits the token anywhere — it stays in your Keychain). macOS may prompt you to allow keychain access.
This is clikae's origin story: you keep a second account precisely because one
account's quota runs out mid-task. clikae to lets you carry the work onward —
like swapping a fuel tank — and keep the same conversation going on a fresh
quota.
# You're working on claude tank `a` and just hit its limit. From the same project
# directory, carry the conversation onto another tank and keep going:
clikae to b # same engine → a real resume, on b's quota
clikae to codex # a different engine → a written brief (cold start)
clikae to codex work # cross to a specific tank of another engineclikae auto-detects which engine + tank this shell is on: first the live env var,
then — since the bare switch / aliases / .app run the engine with a prefix
assignment that never reaches the parent shell — the tank with this directory's
most recent session (the one you were just in here). So switch → work → to
works from one shell. To pin a shell to a tank explicitly instead, use
eval "$(clikae env <engine> <tank>)". The target resolves engine-name-first:
a known engine name crosses to it; anything else is a tank of your current engine.
clikae always announces which mechanism it used so resume-vs-brief is never a
guess.
Same engine (a resume). For Claude Code, clikae finds the current
directory's most recent transcript under the source tank, copies it into the
target tank, and runs claude --resume <id> there — so the conversation
continues, but every new turn burns the target tank's quota. The source tank is
left completely untouched (it copies, never moves), so you can always go back.
A preview + confirm is shown before anything moves; -y skips it, --fresh
switches tanks without carrying, --session <id> carries a specific session.
Carry-over relies on Claude Code's on-disk transcript layout (
<config-dir>/projects/<slug>/<id>.jsonl) and--resume. It's verified against current Claude Code; if a future version changes that layout, it falls back to a fresh start rather than doing anything destructive.
A different engine (a brief). A different model or vendor can't resume a
foreign session — there's no shared transcript format. So clikae writes a
handoff brief (what you're doing, what's done, what's next) and starts the
target engine seeded with it as the opening prompt. clikae tries to write a
summary automatically: if a local model CLI is on your PATH (apfel,
ollama, or llm), it's used to summarise the brief for free — set
CLIKAE_HANDOFF_AUTOLOCAL=0 to disable that auto-detection. Otherwise the brief
is a raw extract (session metadata + your recent prompts), clearly labelled
as raw. To force a specific summariser, point clikae at any model so writing the
brief costs nothing on the tank that just ran dry:
export CLIKAE_HANDOFF_SUMMARIZER='llm -m my-local-model' # any stdin→stdout command
clikae to codex # the model writes the briefThe summarizer (auto-detected or CLIKAE_HANDOFF_SUMMARIZER) receives, on stdin,
an instruction line followed by the tail of the session transcript, and writes the
brief to stdout. If it produces nothing, clikae falls back to the raw extract so a
handoff is never lost. Tune how much
transcript is fed with $CLIKAE_HANDOFF_LINES (default 60). Carrying onward is
read-only on the source — it never touches the source session or any tank.
Under the hood,
clikae todelegates torelay(same engine) orhandoff(different engine). Both remain available as hidden aliases — e.g.clikae handoff claude --out HANDOFF.mdjust writes a brief to a file without starting anything. Runclikae help to/help relay/help handofffor details.
Instead of switching by hand, let clikae watch for the moment a tank runs dry and fall through to the next one. Your tanks are the reserve — there's nothing to set up. Just watch the current session:
clikae watch claude # offer to switch to the next claude tank when dry
clikae watch claude --auto # switch automatically (asks once for consent)
clikae watch claude --to codex/work # cross to a specific tank/engine insteadWhen it detects a dry tank it carries onward to the next tank of the same engine
(skipping any that are themselves over quota); cross-engine needs an explicit
--to. By default it asks first; --auto switches
automatically after a one-time consent (remembered in
$CLIKAE_HOME/auto-relay-consent — delete that file to revoke), and always tells
you what it did.
Honest caveat. An interactive engine hitting its usage limit doesn't exit, returns no code, and fires no hook — so the only thing clikae can watch is what the limit writes to disk. For claude that's the session transcript; for agy it's
~/.gemini/antigravity-cli/cli.log(agy's-prun exits 0 with empty output, so the log line is the only signal). codex's limit is proven not persisted to its transcript, so a dry tank can't be detected for codex from disk. Confirm/tune the match the first time you actually get limited:clikae watch claude --check # would the pattern fire on this session? CLIKAE_LIMIT_PATTERN='…' clikae watch claude # override the match
watch/auto carry an interactive session. For headless grunt work — the
"let the cheaper tank do the dirty work" case — use clikae burn. It runs one
task on a tank and, crucially, knows whether it actually finished: it verifies by
the artifact the task must produce, never the exit code (codex exec exits 0
even when it hit its usage limit and wrote nothing). If the tank ran dry, it
re-fires the same task on the next tank in your reserve.
# Distil a file with codex on tank M; if M is dry, fall through to your next
# codex tank automatically. Success = /tmp/out.md exists.
clikae burn codex M --artifact /tmp/out.md -- \
exec -C /tmp -s workspace-write "read /tmp/in.txt, write /tmp/out.md"
clikae burn codex M --artifact /tmp/out.md --to codex/H -- exec … "<task>" # explicit next hop
clikae burn codex M --artifact /tmp/out.md --timeout 300 -- exec … "<task>" # bound a long runOutcomes: artifact present → done; dry on every reachable tank → fail; ran but
produced no artifact and showed no limit → a real task failure (not rerouted —
it would fail the same everywhere). --no-reroute runs once and stops on a dry tank.
burn is the single-task unit — batch/parallelism stays your orchestrator's
job (fan several burns out, review the artifacts). Make tasks idempotent and
artifact-checked (fixed input/output paths), and pre-stage inputs to /tmp rather
than handing a tank slow iCloud-backed I/O.
burn won't spend the quota you're using. Its auto-reroute skips a tank an
interactive session is live on (it would otherwise burn the conversation you're
mid-flight in) and tanks that share an already-dry account. Pass --allow-active
to override the in-use skip, or --to <tank> to name a hop explicitly.
A tank is a quota source, not the content. burn <engine> <tank> spends
that tank's quota to run a command; what the command reads/writes is just files,
unrelated to tanks. So you can spend a cheap tank's quota to chew on any file —
including another tank's transcript.
Using agy as a cheap read-only worker. agy can't be a burn tank (it's one
global account — there's no reserve to reroute among), but it's a fine headless
worker — you just invoke it directly, outside burn's reserve wrapper:
# agy as a summariser — content in via stdin, agy's own quota spent. Read-only.
cat /tmp/in.md | agy --sandbox -p "summarise this" > /tmp/out.md
# …or through clikae, on a specific agy account (switches the global ~/.gemini
# symlink to tank R, then runs agy headless on R's quota):
clikae agy R -- -p "summarise this" --sandbox < /tmp/in.md > /tmp/out.mdYou give up burn's two guarantees here (no dry→reroute — agy has one account, so
a dry run just fails; no artifact verification), and remember clikae agy switches
a machine-wide symlink, not a per-shell env (and agy has no --model flag — the
model is the app's setting).
clikae status # every engine that has a tank
clikae status claude # just one
# ENGINE TANK ACCOUNT SOURCE
# claude cver hi@cver.net CLAUDE_CONFIG_DIR=…/profiles/claude/cver
# aws (default) - AWS_PROFILE unset — system defaultstatus reads the live value of each adapter's env var in the current shell
and resolves it back to a clikae tank. It's a per-shell view: another terminal
(or one launched from a different clikae app) can be on a different tank.
(default) means the env var is unset (the engine's own default); (external)
means it points somewhere that isn't a clikae tank. The ACCOUNT column shows
the logged-in account when the adapter can tell.
Name tanks however makes sense to you — work, personal, a client name, or
the account email. You don't have to remember what a bare a/b meant: both
tanks and status show the logged-in account when the adapter can read it.
Changed your mind about a name? clikae rename moves the directory, rewrites the
managed alias, and — for claude on macOS — carries the saved Keychain login
across so you don't have to log in again:
clikae rename claude a cver # a → cver; login + alias followIt refuses if the new name is taken or if that engine is currently using the tank
in this shell (run it from a fresh shell). A pre-existing .app launcher is left
alone but flagged — recreate it with clikae app claude cver.
For the surgical, leave-no-trace run: clikae claude work --ephemeral switches to
the tank and runs it, but points the engine's long-term memory at a throwaway
directory that's discarded when the engine quits. The tank's real memory is
stashed aside and restored, untouched.
clikae claude work --ephemeral # incognito: nothing learned this session is kept- Login and transcripts are normal — only the memory store is throwaway (you're still you, the conversation is still logged/resumable).
- Honest scope: clikae guarantees the memory directory is a throwaway. It can't promise the engine "remembers nothing anywhere" — caches, shell history, telemetry, the macOS Keychain are outside clikae's reach. So it's ephemeral memory, not guaranteed total amnesia.
- Supported only for engines whose memory layout clikae knows (currently claude); others say so and exit.
- Unlike a normal switch (which
execs the engine),--ephemeralruns it as a child so cleanup can run on exit. A crashed run self-heals on the next--ephemeral(the real memory is recovered from its stash).
For each tank, clikae:
- Creates
~/.clikae/profiles/<engine>/<tank>/— the directory the engine's env var (e.g.CLAUDE_CONFIG_DIR) points at. (The on-disk path keeps the wordprofilesfor stability; you only ever type/see tank.) - (
alias) Appends a sentinel-wrapped block to your shell rc:The sentinels make safe, exact removal possible.# >>> clikae:claude.work >>> alias claude-work='CLAUDE_CONFIG_DIR="/Users/you/.clikae/profiles/claude/work" claude' # <<< clikae:claude.work <<< - (
app, macOS) Generates an AppleScript-compiled.appthat opens a terminal, runs the env-var-prefixed engine, and sets the window title toclaude (work)so you can tell windows apart. The terminal is Terminal.app by default;--terminal iterm2and--terminal ghosttytarget those instead (set$CLIKAE_TERMINALto change the default). Terminal.app and iTerm2 are driven by AppleScript; Ghostty has no window-opening CLI on macOS, so its launcher goes throughopen -na Ghostty.app --args … -e ….
No daemons, no global state, no network calls. You can read every line.
| Engine | Strategy | Env var |
|---|---|---|
claude (Anthropic Claude Code) |
env-dir |
CLAUDE_CONFIG_DIR |
codex (OpenAI Codex CLI) |
env-dir |
CODEX_HOME |
gh (GitHub CLI) |
env-dir |
GH_CONFIG_DIR |
gcloud (Google Cloud CLI) |
env-dir |
CLOUDSDK_CONFIG |
docker (Docker CLI) |
env-dir |
DOCKER_CONFIG |
helm |
env-dir |
HELM_CONFIG_HOME |
kubectl |
env-file |
KUBECONFIG |
aws (AWS CLI) |
env-var |
AWS_PROFILE |
az (Azure CLI) |
env-dir |
AZURE_CONFIG_DIR |
npm |
env-file |
NPM_CONFIG_USERCONFIG |
terraform |
env-file |
TF_CLI_CONFIG_FILE |
pulumi |
env-dir |
PULUMI_HOME |
vercel (Vercel CLI) |
flag |
— (--global-config <dir>) |
agy (Google Antigravity) |
opt-in symlink | — (hardcoded ~/.gemini; see above) |
The flag strategy is for engines with no config-directory env var: the tank
directory is injected as a command-line flag (e.g. vercel's --global-config)
in the generated alias / .app / run command instead of an exported variable.
Such an engine shows (n/a) in clikae status (there's nothing in the
environment to read back).
Run clikae adapters to see them with descriptions. Adding your own is ~10
lines of bash — see adding-an-adapter.md.
Note on
aws: unlike the others, the AWS adapter doesn't isolate config into a separate directory —AWS_PROFILEselects a named profile from your existing~/.aws/config. Soclikae init aws workexpects a matching[profile work]entry to exist. See the comment at the top oflib/adapters/aws.shfor the alternativeenv-fileapproach.