tt-studio-cli
Skill52 starsChanged 29 days agoApache-2.0View on GitHub
The TT-Studio CLI skill — how the `python run.py` command-line interface (the `tt_setup/` package) is built and how to change it the TT-Studio way: Typer CLI conventions (flag grouping, early-dispatch utility flags, `--help`/`--help-env` docs), package layout and the re-export pattern, the calm minimal-by-default terminal output, hardware/QB2 detection (IS_QB2 opt-in, verify-don't-trust), and the testing/verification patterns (pure helpers, PTY render checks). Use for any CLI work: BEFORE adding or changing a `python run.py` flag or its help text, editing anything under `tt_setup/` or its terminal output, touching startup/teardown flow, or changing hardware detection.
In this repository15 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- .cursor/rules/ai-models.mdcCursor rule
- .cursor/rules/backend.mdcCursor rule
- .cursor/rules/docker-deployment.mdcCursor rule
- .cursor/rules/frontend.mdcCursor rule
- .cursor/rules/general.mdcCursor rule
- .cursor/rules/project-overview.mdcCursor rule
- cli-designSkill
- cut-releaseSkill
- draft-release-notesSkill
- feature-branch-prSkill
- license-attribution-complianceSkill
- tt-studio-add-modelSkill
- tt-studio-debug-bundleSkill
- tt-studio-overviewSkill
The file
We split this skill into the rules it gives an agent
6 rules. About 0 are common, 0 are distinctive, and 6 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 57
When you move a function into a submodule, add every import it needs locally (names are module-scoped now, not inherited from the old …
- Very specific: line 77
Add a tests/test_cli.py case (see Testing) and document it in CLAUDE.md (Common commands) + dev-docs/run-py-guide.md (options table).
- Very specific: line 95
Reusable renderers: When a piece of output should be re-viewable (e.g.
- Very specific: line 108
Lenient defaults; strict behavior is opt-in: Never block a dev laptop or cloud/no-hardware box out of the box.
- Very specific: line 169
Git / PRs: follow the feature-branch-pr skill (branch off dev, minimal diff, verify, no AI attribution in commits/PRs).
- Very specific: line 171
Keep CLAUDE.md (Common commands) and dev-docs/run-py-guide.md in sync when you add a flag.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
TT-Studio CLI (python run.py / tt_setup/)
python run.py is the one entrypoint for everything (setup, start, stop, purge,
logs, status, bug reports). run.py itself is a thin shim: it bootstraps deps
into a managed venv and delegates to the tt_setup/ package. startup.sh is
deprecated.
Read this before touching the launcher. For the terminal-output design system specifically,
dev-docs/launcher-terminal-design.mdis the canonical reference — this skill covers the broader "how the launcher is built and how we like CLIs."This skill is TT-Studio-specific — it names real paths, modules and hardware in this repo. The same design language with nothing repo-specific in it, plus runnable reference files, is the
cli-designskill; use that one for any other CLI.
Architecture
run.py → tt_setup.bootstrap.ensure_environment() (venv) → tt_setup.cli.main().
The package is split into focused subpackages, each with an __init__.py that
re-exports its prior public surface:
| Area | Package / module |
| CLI surface + orchestration | cli/ (_args.py = Typer flags, _run.py = phased flow + early dispatch) |
| Terminal design system | console/ (_theme, _stepper, _panels, _prompts, _steps) |
| Env & secrets config | env_config/ (_values, _dotenv, _preferences, _hf_access, _version, _configure) |
| Host services lifecycle | services/ (_ports, _fastapi, _docker_control, _frontend, _health) |
| Inference-server artifact | inference_server/ (_catalog, _config, _env, _git, _metadata, _privileges, _orchestrator) |
| Stop / purge teardown | cleanup/ (_runtime, _orchestrate, _resource_ops) |
| Docker build/failure diag | docker_diag/ (_build_progress, _diagnostics) |
| Standalone modules | bootstrap.py, shell.py, startup_checks.py, docker.py, constants.py, logging.py, monitor.py/monitor_app.py (--status TUI), bug_report.py (--report-bug), shortcut.py (--install-shortcut), switch.py (--switch), image_source.py (prebuilt-vs-local image choice), config_store.py, spdx.py, settings.py, venv_utils.py |
The re-export rule. A package's __init__.py imports and re-exports the names
its submodules define, so from tt_setup.console import step and
import tt_setup.console as C keep working. When you add/move a submodule, wire
its public names through the __init__.
Linter blind spot — read this before moving code between submodules. Most
submodules do from tt_setup.constants import *. That makes ruff downgrade
undefined-name detection (F821) to a silent F403, so a name you use but
forgot to import in a submodule will NOT be flagged and will crash at runtime.
When you move a function into a submodule, add every import it needs locally
(names are module-scoped now, not inherited from the old monolith). tests/
test_no_undefined_names.py is the backstop; run it.
Adding / changing a CLI flag
- Declare it in
tt_setup/cli/_args.pyas atyper.Option, with arich_help_panel=so it lands in the right--helpgroup: Setup & Configuration · Model Deployment · Lifecycle · Reset (--purge-all) · Advanced · Developer Tools · Release (maintainers) · Troubleshooting & Info. Deprecated flags usehidden=Trueand warn + normalize onto the current flag. - Thread it into the
args = SimpleNamespace(...)passed to_run(args). - Dispatch it. Utility/lifecycle flags (
--stop,--status,--logs,--info,--report-bug,--install-shortcut,--check-headers) run in the early-dispatch block at the top of_run()— do the work, thenreturn/sys.exit()before the 5-phase startup. Only flags that modify a normal startup fall through to the phases. - Name it for the user: lowercase, action-oriented, and match the wording of
its output ("Stopping/Stopped" for
--stop, not "Cleaning up"). - Add a
tests/test_cli.pycase (see Testing) and document it inCLAUDE.md(Common commands) +dev-docs/run-py-guide.md(options table).
Terminal output — the short version
Full rules: dev-docs/launcher-terminal-design.md. The essentials:
- Everything goes through
tt_setup/console/— never rawprint()+ ANSI. - Calm, minimal by default;
-vreveals everything. Gate routine "done" lines onif show_detail():. The pinned phase stepper + one collapsing line perstep()is the normal signal; endpoint URLs live in the ready panel. - Always show failures, prompts, and actionable warnings — never fold those.
- Never wrap an interactive prompt in a capturing
step()or an active phase spinner — suspend around prompts/sudo/anything with its ownLive. - Degrade cleanly on non-TTY (piped) — no escape-code garbage.
bootstrap.pyruns before Rich exists → stdlib only, never importtt_setup.console. Mimic the calm style by hand (it has a stdlib spinner).
Reusable renderers. When a piece of output should be re-viewable (e.g. the
ready panel via --info), extract a pure renderer that probes live state
(show_ready_panel in cli/_run.py) and call it from both places — don't
duplicate the assembly.
CLI design principles — "what we like"
These emerged across the launcher work; apply them to new launcher behavior.
- Fail fast, with a fix and an escape hatch. Stop early (at the Checks
phase) when something is genuinely wrong, but the message must say how to fix it
and how to opt out — e.g. "fix your TT tooling and re-run, or set
IS_QB2=false". Never a bare traceback; use anerrornotice_panel. - Lenient defaults; strict behavior is opt-in. Never block a dev laptop or
cloud/no-hardware box out of the box. Strict checks are opt-in (
IS_QB2) or gated on real signals (tt-smi installed //dev/tenstorrentpresent). - Assume-and-verify, not assume-and-trust. A config claim (e.g. "this is a QB2") is checked against reality (tt-smi); a mismatch is surfaced, not hidden.
- Plain language in user-facing text — say "couldn't read the QB2's chips," not "tt-smi returned nonzero." Explain internal tool/var names in passing.
- Capture subprocess noise; surface it only on failure. A child process must
not scroll under a live spinner. Capture stdout/stderr (e.g. compose runs with
--ansi never+ captured output;run_docker_command(interactive=False)for captured non-interactive sudo) and reveal the buffer only when the step fails — the wayconsole.step()does. - Reuse existing helpers (
build_docker_compose_command, console primitives) instead of hand-rolling equivalents. - Make state re-viewable and hints discoverable — the ready panel footer
advertises
--stop/--logs/--info.
Hardware & QB2
- Detect:
detect_tt_hardware()(docker.py, checks/dev/tenstorrent);check_tt_smi()(shell.py, runstt-smi -s, returns(status, detail, board_type)). Classification lives inshell.py:_classify_boards()mirrorsboard_control/services.py:get_board_type(substring match + device count →P300x2/T3K/…);describe_board()maps to a friendly name;resolve_hardware_label()builds the ready-panel label + optional QB2 warning (pure/testable). - QB2 ==
P300x2(Blackhole QuietBox).IS_QB2is an opt-in flag, default false — a dev laptop/cloud is never held to the strict check. Thett_qb2_launchrelease branch shipsIS_QB2=true. It is independent ofTT_INFERENCE_ARTIFACT_BRANCH, which only selects the inference-server build (that decoupling is deliberate — don't re-derive QB2 from the artifact branch). - Behavior when
IS_QB2=true: confirm via tt-smi → labelQuietBox (QB2); a different board → non-fatal warning; can't read the chips while real TT tooling is present → hard-stop at Checks; no TT tooling at all → skip (escape hatch). WithIS_QB2false (default) the whole strict path is skipped.
Testing & verification
- Tests live in
tests/(stdlibunittest), onetest_<area>.pyper module, each with the pre-refactor shimtry: from tt_setup import X ... except ImportError: import run as X. Run:.tt_studio_run_venv/bin/python -m pytest tests/ -q(keep green). - Extract pure helpers for anything with branching logic (e.g.
resolve_hardware_label,_classify_boards) so it's unit-testable without hardware, a TTY, or Docker. Test the matrix of inputs directly. - Verify terminal output under a real PTY (
pty.spawn([...])) to confirm spinner animation +\r\033[2Kself-heal + collapse to✓, and pipe non-TTY output through| catto confirm no stray escape codes. The live spinner/rule sweep and real prompts only render on a true terminal — check those on hardware. tests/test_no_undefined_names.pyguards theimport *blind spot; keep it passing when you move code between submodules.- Try
COLUMNS=80andCOLUMNS=120for panel/rule widths, and-vto confirm folded detail returns.
Conventions
- SPDX headers are mandatory on every new
.py(not on.md):# SPDX-License-Identifier: Apache-2.0/# SPDX-FileCopyrightText: © 2026 Tenstorrent AI ULC. Runpython run.py --check-headers. - Docker errors link, they don't fix. Point to official docs; never advertise
--fix-dockeror Linux-onlysudo service docker start. Compose v2 only. - Git / PRs: follow the
feature-branch-prskill (branch offdev, minimal diff, verify, no AI attribution in commits/PRs). - Keep
CLAUDE.md(Common commands) anddev-docs/run-py-guide.mdin sync when you add a flag; the file map indev-docs/launcher-terminal-design.mdpredates the package split and lists the old monolith paths — treat this skill's table as the current layout.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

