Skip to content

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.

bash
npm install -g sdtk-kit
sdtk --help
sdtk --version

sdtk 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).

bash
sdtk init --runtime claude
sdtk init --runtime codex
OptionDescription
--runtime <claude|codex>Target runtime (required)
--runtime-scope <project|user>Override default scope (claude defaults project, codex defaults user)
--globalShorthand for user scope
--project-path <path>Initialize a directory other than the CWD
--forceOverwrite existing runtime assets (use after sdtk update)
--skip-runtime-assetsInitialize workspaces only; install no skills
--keep-goingContinue past a failing kit
--verbosePer-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).

bash
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
OptionDescription
--runtime <claude|codex>Target runtime (required)
--scope <project|user>Override default scope (claude defaults project, codex defaults user)
--globalShorthand 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-goingContinue past a failing kit
--verbosePer-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.

bash
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
OptionDescription
--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.

bash
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 claude2

This replaces hand-exporting CLAUDE_CONFIG_DIR / CODEX_HOME, logging in, and hand-writing a shell alias.

OptionDescription
--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-launcherLog in only; do not install or refresh a command
--launcher-onlyInstall/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
--printShow 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 exact export line 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 offending file:line is 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):

bash
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 again

So 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:

bash
sdtk login claude2 --share-sessions   # at login time, or…
sdtk account link                     # …for every slot at once, any time

Both 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.

bash
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
OptionDescription
--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 UTC

This 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.

bash
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.

bash
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 segments

Every 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.sh

The one command that fixes both cases at once — every account, replacing a hand-written entry, with a timestamped backup of each settings.json:

bash
sdtk statusline install --all --force

Slot 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.

bash
sdtk update
sdtk update --check-only   # report outdated kits without changing anything

After 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.

bash
sdtk doctor

Pure 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).

bash
sdtk activate --license SDTK-XXXX-YYYY

Verify with sdtk-spec entitlement status.

Global flags

FlagDescription
--versionPrint the suite version
--helpCommand help

The six toolkit CLIs

Installing sdtk-kit also lands each toolkit's own CLI:

CLIReference
sdtk-specReference
sdtk-designReference
sdtk-codeReference
sdtk-opsReference
sdtk-wikiReference
sdtk-agentReference

SDTK — governed, auditable, resumable AI-assisted engineering.