sdtk — Suite Command Reference
The unified command installed by sdtk-kit. It manages the whole suite: one-command setup, updates, Pro activation, and the accounts you run it all with.
npm install -g sdtk-kit
sdtk --help
sdtk --versionsdtk init
Initialize a project for a runtime — chains every toolkit's setup in order: spec → ops → code → design (runtime-aware, installs skills), then wiki → agent (plain init, no skills).
sdtk init --runtime claude
sdtk init --runtime codex| Option | Description |
|---|---|
--runtime <claude|codex> | Target runtime (required) |
--runtime-scope <project|user> | Override default scope (claude defaults project, codex defaults user) |
--global | Shorthand for user scope |
--project-path <path> | Initialize a directory other than the CWD |
--force | Overwrite existing runtime assets (use after sdtk update) |
--skip-runtime-assets | Initialize workspaces only; install no skills |
--keep-going | Continue past a failing kit |
--verbose | Per-kit detail output |
sdtk runtime
Skills-only runtime asset management — installs, removes, or reports the SDTK skills of every runtime-aware kit (spec → ops → code → design) without creating or modifying any project file. sdtk-wiki and sdtk-agent install no skills and are skipped.
Use this instead of sdtk init when a repo already has its own AGENTS.md / CLAUDE.md / SDTK config and you only want the slash-command skills (for example, sdtk init refuses such roots to protect those files — sdtk runtime install is the supported path there).
sdtk runtime install --runtime claude # skills into <repo>/.claude/skills
sdtk runtime status --runtime claude # per-kit install state, both scopes
sdtk runtime uninstall --runtime claude # remove the managed skills| Option | Description |
|---|---|
--runtime <claude|codex> | Target runtime (required) |
--scope <project|user> | Override default scope (claude defaults project, codex defaults user) |
--global | Shorthand for --scope user |
--project-path <path> | Target a directory other than the CWD |
--force | (install) Overwrite existing managed skill files |
--all | (uninstall) Also remove kit-shared runtime assets |
--keep-going | Continue past a failing kit |
--verbose | Per-kit detail output |
sdtk evolve
Local self-improvement loop: the /evolve skill reviews your own past agent sessions for recurring friction and drafts up to 4 bounded lesson edits (each with evidence and a falsifiable "bet"); this CLI stages and applies them behind an explicit human gate. Adopted lessons live in .sdtk/evolve/LEARNED.md — outside every kit-managed file, so they survive sdtk update — and sessions read them at start via the CLAUDE/CODEX template pointer.
sdtk evolve stage --from draft.json # validate + stage (fail-closed redaction; staging only)
sdtk evolve status # lessons, staging, checkpoint reminder, git posture
sdtk evolve adopt # apply the staged proposal (automatic backup)
sdtk evolve revert # restore the pre-adopt backup| Option | Description |
|---|---|
--from <path> | (stage) Draft JSON produced by the /evolve skill (sdtk.evolve-draft.v1) |
--staging <ts> | (adopt) Apply a specific staging (default: latest) |
--project-path <path> | Target a directory other than the CWD |
There is deliberately no auto-adopt: nothing changes LEARNED.md except your own sdtk evolve adopt. Everything is local — no network, no telemetry.
sdtk login / sdtk logout
Log in a named account slot and give it its own command. A slot is one vendor account — claude1, claude2, codex1, claude-work — and the vendor is read from the prefix, so the Codex flow is the Claude flow with the keyword swapped.
sdtk login claude2 # resolve the config dir, install a `claude2` command, then log in
claude2 # start a session on that account
claude2 --resume <id> # reopen a session — every argument is forwarded
sdtk logout claude2This replaces hand-exporting CLAUDE_CONFIG_DIR / CODEX_HOME, logging in, and hand-writing a shell alias.
| Option | Description |
|---|---|
--dir <path> | Use this config dir instead of the resolved default |
--command <name> | Name the launcher something other than the slot |
--bin-dir <path> | Install the launcher here instead of the detected directory |
--device-auth | (codex) Force device-code login |
--no-device-auth | (codex) Force the browser flow even if a headless box is detected |
--no-launcher | Log in only; do not install or refresh a command |
--launcher-only | Install/refresh the command only; skip the vendor login — for an account that is already signed in |
--share-sessions | (claude) Point this slot's session store at the default account's, so either slot can --resume any conversation |
--keep-onboarding | (claude) Do not mark first-run setup complete after login — the setup wizard then runs once and asks you to sign in a second time |
--print | Show the plan and exit without changing anything |
Numbered slots adopt an existing directory rather than creating a duplicate: claude1 → ~/.claude (your default account), claude2 → ~/.claude-2 or a legacy ~/.claude-b. An account you already set up by hand keeps working and simply gains a command.
The launcher is a real executable on PATH, not a shell alias — aliases exist only in interactive shells of one shell family, so they break in scripts, cron, and zsh/fish. Two problems that would otherwise fail silently are reported instead:
- the chosen bin directory is not on
PATH, so the command cannot resolve (the exactexportline to add is printed), and - an existing shell alias of the same name shadows the launcher — bash resolves aliases before
PATH, so the old alias keeps winning. The offendingfile:lineis printed so you can delete one line.
Codex has two login flows. The default opens a browser and waits for it to call back into /auth/callback on this machine; device-code instead prints a link plus a one-time code (valid ~15 minutes) that you enter from any other device. In a container or over SSH the browser round-trip cannot complete, so sdtk login selects device-code automatically when it detects /.dockerenv, an SSH session, or a Linux box with no DISPLAY/WAYLAND_DISPLAY — and prints which flow it chose and why, every time. Claude exposes no equivalent flag because its login needs no mode choice.
Credential-free by design
SDTK creates the directory and the launcher, then hands over to the vendor's own claude auth login / codex login. It never reads, copies, or stores an authentication file — and it cannot tell you whether a directory is currently signed in, because answering that would mean inspecting credentials. Ask the vendor CLI instead, via sdtk account status.
One login, not two
Claude Code keeps credentials and first-run state in two independent places. claude auth login writes the credentials but never sets hasCompletedOnboarding, so the next interactive session runs its setup wizard — and that wizard has its own login step, which does not check the credentials you just obtained. You get asked to sign in twice, against the same client_id and the same scopes.
This was reproduced with SDTK removed entirely (Claude Code v2.1.220):
CLAUDE_CONFIG_DIR=/tmp/probe claude auth login # "Login successful."
CLAUDE_CONFIG_DIR=/tmp/probe claude auth status # loggedIn: true
CLAUDE_CONFIG_DIR=/tmp/probe claude # asks to sign in againSo it is vendor behaviour — but sdtk login is what puts you on that path, by authenticating before your first interactive run. After a successful login it therefore marks first-run setup complete, and claudeN opens a session straight away.
What that skips: the theme picker (change it any time with /theme) and the redundant login step. It does not skip anything security-relevant — the trust prompt and the external-CLAUDE.md-imports prompt are gated separately and still appear. The flag lives in .claude.json (configuration); .credentials.json is never read or written.
Marking only ever happens after the vendor login succeeds. A dir that was never signed in is left alone, so you can never end up with setup marked complete but no credentials.
Continuing one conversation on another account
Session transcripts live inside the config dir, at <dir>/projects/<encoded-cwd>/<session-id>.jsonl. A slot is a config dir, so by default each slot sees only its own history:
claude2 --resume 11fda40d-…
No conversation found with session ID: 11fda40d-…That is the wall you hit when one account runs out of quota mid-conversation. Share the store instead:
sdtk login claude2 --share-sessions # at login time, or…
sdtk account link # …for every slot at once, any timeBoth slots then read one physical store, so a session started on claude1 reopens with claude2 --resume <id>. Existing sessions are merged, never overwritten, and the slot's previous store is preserved as projects.bak.<timestamp> for sdtk account link --undo.
One transcript, one writer
Never --resume the same session id on two accounts at once — two processes appending to one JSONL corrupts it. Different sessions in parallel are fine.
Only projects/ is shared: per-project trust and tool-permission state (.claude.json) and /rewind checkpoints (file-history/) stay per-directory, so the second slot may ask for permissions once and will not see the first slot's checkpoints. On Windows the link is an NTFS junction, which needs no administrator rights.
sdtk account
Visibility across every Claude/Codex account on the machine, plus an opt-in shared session store.
sdtk account status # every account dir, the active default, link state, headroom
sdtk account link # share session history so any account can --resume any session
sdtk account link --undo # restore isolated stores from the automatic backups
sdtk account guide # copy-paste setup for adding another slot| Option | Description |
|---|---|
--json | (status) Machine-readable output |
--no-identity | (status) Skip the per-account vendor identity lookup (faster) |
--dry-run | (link) Print the plan without touching anything |
--undo | (link) Restore the isolated stores |
link only ever touches the projects/ (Claude) or sessions/ (Codex) tree, and the original store is preserved as <top>.bak.<timestamp> — nothing is deleted. Identity comes from the vendor CLI's own auth status, never from a credential file.
One writer per session
After linking, never --resume the same session id on two accounts at once — that is two writers on one transcript. Different sessions in parallel are fine.
Limits, and what cannot be shown
sdtk account status reports the last limit each account actually hit — session (5h), weekly, monthly spend — read from that account's own transcripts:
.claude ACTIVE, isolated store
identity: you@example.com · pro
limit hit: weekly · 2026-07-14 07:21 UTC
limit hit: session (5h) · 2026-07-20 05:38 UTCThis is history, not headroom. It says which wall you reached and when; it does not say how much quota remains.
A live "% of the 5-hour or weekly limit used" cannot be produced for Claude. Codex writes a proactive rate_limits snapshot into every session file, which is why its row shows used 27% (resets …). Claude writes no such snapshot anywhere on disk — it records a limit only after a 429. The only other source is the server (/api/oauth/usage), which needs your OAuth token; reading it would break the credential-free invariant, so SDTK does not.
claude auth status is the vendor's command and its output cannot be changed by SDTK.
sdtk usage
Token-usage meter across every discovered Claude and Codex account — read locally from the CLIs' own session data.
sdtk usage # per-account usage summary
sdtk usage --json # machine-readable
sdtk usage --dir <path> # also probe <path> as an account dir (repeatable)Totals are reported per account × model over three rolling windows — last 5 hours, today (local midnight), and the last 7 days — so a number can be lined up against the limit window it actually counts toward. Codex accounts additionally report their most recent rate-limit snapshot (used %, window, reset time), which Codex itself writes to disk.
--dir adds a directory to the scan rather than narrowing to it, and may be repeated; use it for an account that lives outside $HOME.
No network calls: it aggregates what the vendor CLIs already wrote to disk. Accounts sharing a session store (see sdtk account link) are reported as shared rather than split, because per-account attribution is not recoverable once the store is merged.
Credential-free by design
usage only ever opens <account-dir>/projects/**/*.jsonl (Claude) or <account-dir>/sessions/**/*.jsonl (Codex). It never opens a credential file — not .credentials.json, not auth.json, not anything else at the account root. It exits 0 always: this is a meter, not a health gate.
sdtk statusline
Optional 3-line Claude Code status line — model / directory / git branch, a context-window bar, and 5h/7d rate-limit bars with session cost. Strictly opt-in: neither sdtk init nor sdtk runtime install enables it; they only print a one-line pointer.
sdtk statusline preview # offline sample render — no live session needed
sdtk statusline install # current account ($CLAUDE_CONFIG_DIR or ~/.claude)
sdtk statusline install --all # every discovered Claude account dir
sdtk statusline status # per-account install state
sdtk statusline uninstall
sdtk statusline install --runtime codex # Codex's own native segmentsEvery value is read verbatim from the JSON Claude Code pipes in — a field your version does not emit simply disappears; nothing is fabricated. install writes a single statusLine entry pointing at sdtk statusline render, so no per-account script file can drift, and an existing statusLine aimed at a different command is left alone unless you pass --force.
Multi-account: two things install deliberately will not do
Both of these are correct — they refuse to destroy something you did not ask them to touch — and both are reported rather than done silently.
1. It targets one account, not all of them. With no --all or --dir, install writes to the account you are invoking it under ($CLAUDE_CONFIG_DIR, else ~/.claude). If you have a second account, it is untouched, and install now says so:
installed: claude1 (.claude) (~/.claude/settings.json) — backup: settings.json.bak.1785566951506
note: 1 other Claude account dir(s) not targeted — claude2 (.claude-b). Use --all to include them.2. It will not overwrite someone else's status line. A statusLine pointing at a different command — a hand-written script from before you had SDTK, for instance — is left alone. status shows it as other command: rather than installed.
status names every account by slot and directory together, so a row maps onto the name you actually type:
sdtk statusline — 2 Claude account dir(s)
claude1 .claude not installed
claude2 .claude-b current, other command: ~/.claude-b/statusline.shThe one command that fixes both cases at once — every account, replacing a hand-written entry, with a timestamped backup of each settings.json:
sdtk statusline install --all --forceSlot names vs directory names
A row reads claude2 and .claude-b because those name two different things: claude2 is the slot (what you type), .claude-b is the directory on disk it resolved to. sdtk login claude2 prefers ~/.claude-2, but adopts a legacy ~/.claude-b if that is what already exists — letter suffixes predate numbered slots, and adopting one is what stops a second account being orphaned by a rename. So the directory records the history, the slot is the stable name, and status prints both so you never have to translate between them.
Codex has no custom-command status line. Its status line is a native segment picker (a [tui] table in config.toml), so --runtime codex turns on Codex's own equivalent segments instead. That path is append-only: if config.toml already has a [tui] table, install refuses to edit it and prints the lines to add by hand.
sdtk update
Update all installed SDTK kits.
sdtk update
sdtk update --check-only # report outdated kits without changing anythingAfter updating, refresh each project's runtime assets with sdtk init --runtime <r> --force.
sdtk doctor
Detect version fragmentation across installs: which package each suite CLI on your PATH actually executes, and whether a different (especially newer) copy of that kit is installed but shadowed — e.g. the umbrella's bundled copy hiding a newer standalone install, or the reverse.
sdtk doctorPure local filesystem resolution — no network, no npm calls. Exits 0 when healthy, 1 with actionable warnings when fragmentation or missing CLIs are found (usable as a setup/CI guard). Run it whenever a CLI seems to ignore a fix you just updated to.
When a sdtk-spec.config.json is present in the working directory, doctor also prints an informational config-health NOTE if that config still has unfilled placeholders — pointing you at sdtk-spec config apply-profile <name> and sdtk-spec config check. The note is advisory only: it does not change the exit code (config completeness is enforced by sdtk-spec config check, not here).
sdtk activate
Activate a Pro license on this machine (one-time purchase; unlocks Pro capabilities across all kits).
sdtk activate --license SDTK-XXXX-YYYYVerify with sdtk-spec entitlement status.
Global flags
| Flag | Description |
|---|---|
--version | Print the suite version |
--help | Command help |
The six toolkit CLIs
Installing sdtk-kit also lands each toolkit's own CLI:
| CLI | Reference |
|---|---|
sdtk-spec | Reference |
sdtk-design | Reference |
sdtk-code | Reference |
sdtk-ops | Reference |
sdtk-wiki | Reference |
sdtk-agent | Reference |