CLI reference
tcm help lists the commands. tcm <command> --help (or tcm help <command>) shows a command's arguments and
options, with defaults. tcm --version prints the hash format and TypeScript version.
A mistyped command gets a suggestion. An argument or option a command doesn't take is an error that names it,
shows the command's usage, and suggests the option you meant (unexpected '--olds' (did you mean --old?)).
Conventions
- Arguments starting with
-go after--:tcm run lib.main -- --verbose,tcm search -- --lines. - Missing arguments are asked for at a terminal: names to search for
view,run,edit,docs,deps,mv,rm,devand others; branches forswitchandmerge; conflicts forresolve; recent commands forreset; packages forupgrade.tcm rmof a name others use asks instead of refusing. Outside a terminal (scripts, CI, editors) a missing argument is a usage error. - A hash is
#and its first letters (#abc).##abcis the same hash. -m <message>(--message) says what a history entry is for, on the commands that make one:add,update,patch,mv,alias,rm,resolve,import,upgrade,fmt,merge,revert,reset.tcm historyshows it (messages).--branch <b>reads another branch as it is (code, history, todo), changing nothing. The read-only commands take it:view,search,find,ls,deps,dependents,docs,todo,status,history,diff,at. In a session it's how to look at another session's work without leaving yours. Commands that change things refuse it. (tcm mcp --branchis different: the branch a session works on.)- Package installs show as one line each, replaced by what was installed and how long it took.
- Shell completion:
source <(tcm complete zsh)(orbash,fish,powershell); see install.
Global options
These go before the command.
tcm -C <dir> <command>- run as if started in
dir, on the codebase there (not the oneTCM_CODEBASEorTCM_BRANCHnames), asgit -Cdoes tcm --build <build> <command>- run the command with another tcm: a kept build (
known-good,previous, or an id fromtcm builds) or atcm.js.-C <dir>after it runs it there. Shows whether a bug is in an older tcm (developing; an agent:tcm_cliwithbuild)
Codebase
tcm init [template]- set up a codebase here:
.tcm/codebase.sqlite,#codebaseimports inpackage.json,tsconfig.json,scratch.ts(files). A template (app,react,package,pwa) adds a small project, saved and ready to run, inapp(pkgfor a package), and says what to run next (templates) tcm/tcm watch [--into ns] [--fresh]- watch scratch files (
*.ts, and kept files there to save: a page that says where it goes, or with--intoone the namespace holds): typecheck, hash, report, evaluate watch expressions and tests, cached by hash.--freshre-runs them. Names used but not imported are reported (not imported: …), not written into the open file tcm check [files...] [--into ns] [--fresh]- one-shot
watch. Reports each definition asnew,identical,update,aliasorresolves(resolves a conflict); many identical ones as one line. Adds missing imports to the file and says which; reports unused imports (saving removes them). A parse error is reported alone, with its lines, and nothing is imported tcm add [files...] [--into ns]- save new definitions (default file: the codebase's
scratch.ts). Adds missing imports and removes unused ones, and says so (scratch files) tcm update [files...] [--into ns] [--clear]- save, re-point changed names, re-hash every dependent. Imports as
adddoes. Changes to comments only, or to an import's local name, are saved too (~ text). Definitions the file imports that use what it changes are saved with it ((dependent)), so a class and what's typed with it change in one save (scratch files). Dependents that stop typechecking keep their old hash and go totodo(many are listed by namespace;tcm difflists all). Checks stored table rows against changed row types (data).--cleardeletes each file once saved tcm edit <name>... [--file f] [--force]- write definitions to a scratch file (
scratch.ts, orscratch.tsxfor JSX), references following updates. Adds to a file holding unsaved work;--forcereplaces it. Never writes over the codebase's ownpackage.jsonortsconfig.json. A conflicted name goes to a file of its own (lib.k.ts): both sides merged from the base, with conflict markers where both changed the same lines; conflicted names given together share one file. Saving it without markers resolves the conflict (merging) tcm patch <name> --old <text> --new <text> [--all] [--imports <json>]- find-and-replace on a definition's code as
tcm viewshows it, then save asupdatedoes. See tcm patch tcm patch <name> --edits '[{"old": …, "new": …}]'- several edits to one definition
tcm patch --patches '[{"name": …, "edits": […], "all": false, "imports": […]}, …]'- several definitions and pages, saved as one update and one history entry
tcm view <name|#hash>... [--lines a-b] [--grep text]- show definitions with current names and placement.
--lines:120,120-180,120-, or1-20,90-.--grep: lines with the text and two either side, or a regex written/…/with flags ('/get|set/i'). A missing name is reported with likely matches (or a namespace's contents), the rest shown, exit code 1;edit,rm,patchand others do the same tcm ls [namespace]- list names
tcm find <query>- names with every word of the query in the name, signature or doc (finding code)
tcm find ':<type>'- search by type:
':(xs: T[]) => T'; unknown names are type variables (search) tcm search <text> [--regex] [--case] [--word] [--under ns] [--limit n]- find text in code and pages, each line by definition, numbered as
view --lines. Text as written, or a regex/…/(--regex, the old way: all the text is one).--under: a namespace or definition.--limit: lines shown (default 200). Stops at 500 definitions or 5000 matches, and says so (finding code) tcm at <file>[:<line>]- the definition at a line of a file, or every definition in it with its lines. Takes scratch files, views (
.tcm/src,.tcm/sessions/<branch>/src), generated modules in.tcm/cacheas stack traces name them, mirrored files, and<hash>.ts/modules/<hash>.jsfrom a packed package even if not here tcm deps <name>/tcm dependents <name>- what a definition uses / the named definitions using it now
tcm docs <name>- a definition's documentation, rendered: links and embeds with current names, examples evaluated (docs)
tcm run <name> [args...]- run a definition: a function is called with the args, the result awaited and printed. Commands it starts are found through
<COMMAND>_BIN(TCM_BIN) if the caller set one tcm run --bin <command> -- [args...]- run a command of the codebase's packages (a manifest's
bin) tcm mv <from> <to>- rename a definition or namespace (no code changes)
tcm alias <name> <new>- add another name for a definition
tcm rm <name>... [--force]- remove names. Refuses to remove a definition's last name while named definitions use it, unless
--force(they keep working); names removed in the same command don't count tcm todo [--all] [--json]- conflicts, dependents to fix after an update (first 20;
--allfor every one), and code using a version nothing names.--json: one object withbranch,merging,conflicts(path,sides:hash,side,label),broken(hash,names,message),unnamed(path,uses,madeAs) tcm status- the branch, this tcm's build and hash, the codebase by namespace, unsaved scratch files and what saving would do,
todo, other branches with workmainlacks, this branch's own entries and names changed, orphaned session directories with unsaved work, whether a mirror is in step tcm export <namespace> <dir>- write a namespace's files (pages, CSS, images, a
Dockerfile, a.gitignore), or one file, laid out by name, links as they are now tcm import <dir> [--into ns] [--update] [--exclude a,b] [--tests] [--mirror] [--verbose]- add an existing project, one namespace per file.
--updatereplaces what changed since an earlier import;--excludeskips paths;--testsimportstest/andtests/too;--mirrorwrites changes back to the files (importing);--verbosereports every definition tcm mirror [--check] [--unlink]- the directory changes are written back to.
--checkre-analyzes its files, which must hash to the codebase's;--unlinkstops writing back (mirrors) tcm fmt [namespace]- reformat stored definitions with oxfmt; no hashes change (formatting)
tcm lint [namespace] [--fresh]- lint with oxlint, cached by hash (linting)
tcm test [names...] [--fresh] [--definitions | --suites] [--bin cmd=<path>,...]- run tests (all, or those named and under the namespaces named), cached by hash, then packages' suites, cached by the code they ran. Only passes are reused.
--definitions/--suites: only those.--bin: run suites against another build of a command, e.g.--bin tcm=known-goodto see a new test fail before a fix (testing). Suites share one machine-wide budget:TCM_SLOTS(how many; default from cores and memory;TCM_SUITE_SLOTSstill works),TCM_SLOTS_DIR(testing) tcm packages- the npm packages the codebase uses: range, locked version, what uses them (packages)
tcm upgrade <pkg> [--force]- newest versions within the ranges in use (and of what's built against it); no hashes change, users are re-checked.
--force: even if something built against it can't follow tcm upgrade <pkg>@<range|latest> [namespace]- re-pin code (under
namespace, if given) to another range: re-hashed, dependents follow tcm migrate [--if-needed]- move a codebase from an older hash format or table layout to this tcm's, history included (hashing). The old database is kept beside it (
.tcm/codebase.v<n>.sqlite, or.tcm/codebase.layout-<layout>.sqlite); the new one is used only if it reads back as itself.--if-needed: silent when there's nothing to do tcm compact- compress long texts an older tcm stored uncompressed, reclaim unused space (
VACUUM; other tcms wait), then prune.tcm/cacheof what no branch uses, untouched for an hour (a day for suites' apps; remade when needed) (files) tcm verify [--fresh]- re-render and re-hash every live definition; each must hash to itself. Skips what this tcm already verified under the same name;
--freshchecks all (hashing) tcm builds- kept tcm builds (in
TCM_BUILDS: tcm's repository'sdist/):known-good,previousand older, each with its history entry, date, changes andtcm.js(developing) tcm builds make <entry>- build the codebase's tcm as of a history entry and keep it, to run with
--build tcm lsp [--stdio]- language server for editors (editors)
tcm mcp [--branch b | --direct]- MCP server for AI agents, over stdio (agents). By default a session on its own branch: a new one, or the one this directory's last session left holding work (
--session, the default, is accepted).--branch b: that branch, unless another server is on it.tcm_mergelands it when you say so;tcm_cli ["switch", "<branch>"]moves it.--direct: no session, on the codebase's own branch tcm complete <shell>- completion script for zsh, bash, fish or powershell, including codebase names
tcm patch
- Each
--oldmust appear once (or use--all). Spacing and line wrapping may differ (the code is formatted). Nothing is saved if an old text isn't found (the most similar line is shown) or the result doesn't typecheck. --oldand--neware taken as written:\nis a backslash and an n, as in code's"a\nb". Write a real line break (inside the quotes, or$'…\n…'), or use--editsJSON, where\nis one. A\nin--newbut not--old, or in an--oldnot found, gets a note.- Inside a template literal the text is the source's, escapes included (
\n,\`,\${). An old text found only with its escapes read the other way is matched that way, and noted. A new text that would end or split the literal (a bare backtick, or${without}) is refused, with how to escape it. - Imports follow the code: a name the new code uses is imported (from the codebase, a package the codebase imports
it from, tcm's modules, or the Node builtin that exports it), and one it stops using is removed. For one that
can't be found (an alias), give
--imports '[{"name": "isImportDeclaration", "from": "typescript", "as": "ts_isImportDeclaration"}]'(namedefaultor*withas;"type": truefor a type only), orimportsin a--patchesentry. - With
--patches, each edit matches its definition astcm viewshows it. Two functions declared alike, which one file can't hold, are checked in a file each and saved one after the other. - Output: the lines removed and added. Problems name the definition and its line as
tcm viewnumbers it (lib.f:8:36). Parse errors are reported ascheckreports them.
Libraries and releases
tcm lib- this branch's libraries: mount, what it is, where it came from
tcm lib add <remote|url|dir|tcm> <package>[@<version>]|#<hash> [--as <name>] [--at <name>]- fetch a release's namespace (newest if no version), or a namespace by hash, with what it uses; mount it read-only at
<root>.lib.<name>(or--at). Named after its package (@alice/geo-tools:geo_tools) unless--as; a hash needs--as.tcmis the libraries tcm ships (react,server), offline:tcm lib add tcm react tcm lib add @<handle>/<package>[@<version>] [--as <name>] [--at <name>]- the same for a release announced on the AT Protocol (default: the highest version announced), fetched by hash from the servers the announcement names, recorded with the author's DID (announced releases)
tcm lib upgrade <name|mount> [<package>@<version>|#<hash>] [--source s] [--check]- move to the newest release (or the one given; announced: the highest the author's DID announced) as one update: API changes first, then users follow; what breaks goes to todo. Refused while code uses what the new one lacks.
--source: try it first.--check: only show what would change tcm lib sources <name|mount> [--add <s>] [--rm <s>]- where it's fetched from, in order, each answer checked by hash: add a mirror (remote, URL or directory), or remove one (not the last) (sources)
tcm lib attest <lib|@who/pkg@ver|pkg@ver|#hash> review|advisory|yank [-m text] [--severity s] [--fixed-in v] [--as handle]- a signed statement in your AT Protocol repository: a review, an advisory (
--severitylow|moderate|high|critical;--fixed-in), or a yank (author only). Advisories and yanks need-m(what people say) tcm lib search <query> [--registry <url>]- search a registry's libraries (newest versions) by name, doc words or
:a type, each with itstcm lib addline. The registry is kept; defaulthttps://tcm.codes/registry(finding one) tcm lib trust [--add <handle>] [--rm <handle>]- whose reviews and advisories
tcm lib,addandupgradeshow besides the author's; kept in history tcm lib rm <name|mount>- remove a library's names and record entry; refused while code outside it uses what only it names
tcm pack <package>[@version] [--out dir]- an npm package of a package (a
manifestdefinition's namespace): a release, or the branch as it is (publishing) tcm pack [[subpath=]<namespace|name>...] --name <pkg>[@version] [--description d] [--bin cmd=<name>,...] [--with pkg,...] [--files a,b] [--out dir]- an npm package of the definitions given (default version 0.0.0; default out
.tcm/pack/<name>) (publishing) tcm release [<package>][@version] [--draft] [--notes file] [--remote r] [--force]- release at the version its API changes call for (or a higher one given), with a generated list of changes.
--draftwritesrelease-<package>.mdto summarize in;--notestakes the summary;--remote: where its contributions are (default origin);--force: past too small a version step, or with work intodo. A mirrored codebase's releases are recorded with its files (releases) tcm release … --announce [--as <handle>] [--source <url>]…- also write a
codes.tcm.releaserecord to your AT Protocol repository (an existing version: announce it as is). Sign in in the browser once, or setTCM_ATPROTO_APP_PASSWORD.--source(repeatable, kept; default origin's URL): where to fetch it (announcing) tcm release check [package]- the next release's version and each finding behind it, and what would block it (work left, a tcm library using what isn't its own; exits 1)
tcm release view <package>@<version>- a release's notes
tcm releases [package]- packages and their releases
tcm changelog [package] [--out file]- a package's changelog, from its releases
History and branches
See history, branches and merging.
tcm history [n] [--all]- this branch's entries, newest first: hash, how far back, names changed, re-hashed dependents counted (
--alllists them). Latest 30 unlessn(or-n,--limit). Each is named by its message's first line if it has one, else its command (update lib.f, lib.g (+3)) tcm describe [n|#hash] [-m <message>]- show or set an entry's message (default: the latest); no hash changes.
-m ""removes it. Codebases it was pushed to keep the old one (messages) tcm diff- what the last command changed: edits, dependents re-hashed, renames
tcm diff <n|#hash> | <branch> | <a> <b>- changes since an entry, to a branch, or between two branches or entries
tcm diff <#def> <#def>/tcm diff <conflicted name>- two definitions' code, line by line; a conflicted name's base against each side
tcm reset <n|#hash>- restore the names as of an entry, as a new entry (
tcm reset 1undoes the last command) tcm revert <n|#hash>- undo one entry (a landing, say) and nothing after it, as a new entry, three-way; a later change in the same place is a conflict (
tcm resolve) tcm branch [name] [--delete]- list, create or delete branches. Can't delete
main, the current branch, or one a session's server is on; deleting removes its session directory unless it holds unsaved work tcm switch <name>- switch branch (not in a session:
TCM_BRANCHkeeps a process on its own) tcm merge <branch> [--no-fast-forward] [--squash] [--abort]- merge into this branch: nothing if it already has the other's history, a fast-forward if possible (
--no-fast-forward: a merge entry anyway), else three-way.--squash: one entry whose only parent is this branch's (how sessions land). Definitions both sides edited merge as text where the edits are apart and it typechecks. A merge left with a conflict or a type error waits apart with scratch files to settle, the branch untouched; commands work on it, and it's made once settled;--abortdrops it (waits apart). Onmain, or with--squash, it lands:mainmerged in first, thentodoand tests, then ontomain(landing) tcm resolve <name> <#hash> | --ours | --theirs- pick one definition for a conflicted name, by hash or side (
--ours: this branch's;--theirs: the merged branch's, astodolabels them). Code using the others is re-derived against it. To combine them,tcm editthe name (merging)
Sync and remote execution
See sync.
tcm remote- list remotes
tcm remote add <name> <url|dir> [--token t] [--fingerprint sha256]- add a remote: a
tcm serveURL or another codebase directory.--token: the server's token;--fingerprint: pin its certificate tcm remote rm <name>- remove one
tcm push [remote] [--as branch] [--force]- send this branch's history and move the remote branch to it (a contributor's goes under
@<handle>/). Refuses if the remote has history you don't, unless--force tcm pull [remote] [branch]- fetch into
<remote>/<branch>, then fast-forward, or merge three-way (left to settle astcm merge's; not made onmain) tcm run <name> [args] --on <remote|url|dir>- send what the remote lacks, run there, stream stdout and stderr back
tcm contribute [remote] [--to main] [--as name] [--title t] [--description d]- push this branch (a contributor's as
@<handle>/<branch>) and open a contribution; pushing again updates it (contributions) tcm contribution [--remote origin] [--all]- a remote's open contributions;
--allincludes merged and closed tcm contribution view <id>- its changes, whether it merges, and comments
tcm contribution comment <id> <text> [--about <name>]- comment, optionally on a definition
tcm contribution merge|close|reopen <id>- merge it on the remote (refused on conflicts), close it, or reopen it
tcm serve [--port 7737] [--host 127.0.0.1] [--token t] [--no-run] [--public] [--insecure] [--tls [--tls-cert f --tls-key f]]- serve this codebase over HTTP(S) for push, pull and run.
--token: default$TCM_TOKENor a new one.--no-run: refuse to run code.--public: anyone can pull and read contributions without a token.--insecure: allow clear-text tokens beyond localhost.--tlswithout a cert is self-signed (serving) tcm serve … [--site dir] [--moved /path] [--proxy /path=url[|url],…] [--forge /path=dir,…]- also serve, to anyone: a static site (
--site); redirects for pages that moved under a path (--moved); apps beside it (--proxy, e.g. onetcm start --baseserves); other codebases' web UIs, read-only (--forge /code/notes=../notes) tcm serve's web UI- at
/forge/(and/without--site): pages, search, history, contributions to review (web UI) tcm announce [url] [--name n] [--description d] [--as <handle>] [--withdraw]- list this codebase on the AT Protocol (a
codes.tcm.codebaserecord) so registries show its main branch.url: itstcm serve --publicserver (kept; default origin's).--name(default the directory's) and--description(300 chars max) are kept. Warns if the server needs a token. Signs in astcm release --announcedoes.--withdrawdeletes the record (listing) tcm user [--remote origin]- who you are on a remote: maintainer (with its contributors), contributor, or tokenless reader
tcm user add|rm <handle> [--remote origin]- maintainer only: add a contributor (prints their token once; again gives a new one), who pushes under
@<handle>/and proposes; or remove one, revoking the token (contributors) tcm registry [--port 8080] [--host 127.0.0.1] [--dir tcm-registry] [--base /path]- run a registry: finds AT Protocol announcements, checks each against its author's signed record, mirrors code by hash, shows it. Visitors sign in with AT Protocol to star libraries and see what people they follow, and libraries they use, are up to.
--dir: its data and mirror;--base: served under a path behind a proxy
Apps, data and jobs
See browser apps, production, data, jobs and traces.
tcm dev <app> [--port 5173] [--http <namespace>[=/prefix]] [--trace-calls]- serve an app with hot reload;
<app>is a function run on load or a React component.--httpserves a namespace's functions as HTTP endpoints.--trace-callstraces every server call too (traces) tcm build <app> [--force] [--static <dir> [--base /path]]- immutable production build into
.tcm/apps/<hash>/, reused unless--force; with the app'spwa, its manifest, icons and service worker (PWAs).--staticwrites a static site (each linked page rendered, server components included) for the root or--base; refused if the app calls server functions (static sites) tcm start <app> [--port 8080] [--host 127.0.0.1] [--tls [--tls-cert f --tls-key f]] [--keep N] [--http <namespace>[=/prefix]] [--base /path]- serve in production, following the app's name (deploy and roll back by moving it).
--keep: earlier builds kept serving for open tabs.--base: under a path of a site (deploying) tcm apps [prune [--keep 3]]- list builds (
*= current);prunekeeps the current one and the newest N others tcm data [table [n]]- tables' row counts, or a table's newest rows (default 20)
tcm data migrate <table>- upgrade every stored row to the current row type now, through
migrate tcm jobs [n] | retry <id> | cancel <id>- list background jobs (default 30), queue one again, or cancel a queued one (an id prefix is enough)
tcm traces [name] [--since 24h] [--trace <id>] [--errors] [-n 20]- where the app spent its time (
tcm startandtcm devrecord it, browser side too), by definition, with what got slower than its previous version and the change that did it.name: one definition's versions side by side, callers, slowest calls, errors.--errors: errors by the definition that threw them, with the version they began in.--trace: one trace as a tree.-n: how many definitions (traces) tcm ps- what tcm runs on this codebase, oldest first: each
dev,start,serveandstudiowith its process, app, branch, address and uptime; then agents' MCP servers with their branch and directory tcm studio [--port 4747] [--no-open]- open the codebase in the browser, this machine only: what needs you, code and how it runs, running processes, traces, history, branches, releases. Sign in through the printed link (token in
.tcm/studio/token). Opens the running one if there is one (the studio)