CLI reference — tcm-ts

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, dev and others; branches for switch and merge; conflicts for resolve; recent commands for reset; packages for upgrade. tcm rm of 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). ##abc is 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 history shows 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 --branch is 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) (or bash, 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 one TCM_CODEBASE or TCM_BRANCH names), as git -C does
tcm --build <build> <command>
run the command with another tcm: a kept build (known-good, previous, or an id from tcm builds) or a tcm.js. -C <dir> after it runs it there. Shows whether a bug is in an older tcm (developing; an agent: tcm_cli with build)

Codebase

tcm init [template]
set up a codebase here: .tcm/codebase.sqlite, #codebase imports in package.json, tsconfig.json, scratch.ts (files). A template (app, react, package, pwa) adds a small project, saved and ready to run, in app (pkg for 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 --into one the namespace holds): typecheck, hash, report, evaluate watch expressions and tests, cached by hash. --fresh re-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 as new, identical, update, alias or resolves (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 add does. 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 to todo (many are listed by namespace; tcm diff lists all). Checks stored table rows against changed row types (data). --clear deletes each file once saved
tcm edit <name>... [--file f] [--force]
write definitions to a scratch file (scratch.ts, or scratch.tsx for JSX), references following updates. Adds to a file holding unsaved work; --force replaces it. Never writes over the codebase's own package.json or tsconfig.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 view shows it, then save as update does. 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-, or 1-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, patch and 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/cache as stack traces name them, mirrored files, and <hash>.ts / modules/<hash>.js from 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; --all for every one), and code using a version nothing names. --json: one object with branch, 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 work main lacks, 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. --update replaces what changed since an earlier import; --exclude skips paths; --tests imports test/ and tests/ too; --mirror writes changes back to the files (importing); --verbose reports every definition
tcm mirror [--check] [--unlink]
the directory changes are written back to. --check re-analyzes its files, which must hash to the codebase's; --unlink stops 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-good to 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_SLOTS still 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/cache of 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; --fresh checks all (hashing)
tcm builds
kept tcm builds (in TCM_BUILDS: tcm's repository's dist/): known-good, previous and older, each with its history entry, date, changes and tcm.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_merge lands 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 --old must 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.
  • --old and --new are taken as written: \n is a backslash and an n, as in code's "a\nb". Write a real line break (inside the quotes, or $'…\n…'), or use --edits JSON, where \n is one. A \n in --new but not --old, or in an --old not 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"}]' (name default or * with as; "type": true for a type only), or imports in a --patches entry.
  • With --patches, each edit matches its definition as tcm view shows 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 view numbers it (lib.f:8:36). Parse errors are reported as check reports them.

Libraries and releases

See 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. tcm is 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 (--severity low|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 its tcm lib add line. The registry is kept; default https://tcm.codes/registry (finding one)
tcm lib trust [--add <handle>] [--rm <handle>]
whose reviews and advisories tcm lib, add and upgrade show 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 manifest definition'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. --draft writes release-<package>.md to summarize in; --notes takes the summary; --remote: where its contributions are (default origin); --force: past too small a version step, or with work in todo. A mirrored codebase's releases are recorded with its files (releases)
tcm release … --announce [--as <handle>] [--source <url>]…
also write a codes.tcm.release record to your AT Protocol repository (an existing version: announce it as is). Sign in in the browser once, or set TCM_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 (--all lists them). Latest 30 unless n (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 1 undoes 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_BRANCH keeps 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; --abort drops it (waits apart). On main, or with --squash, it lands: main merged in first, then todo and tests, then onto main (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, as todo labels them). Code using the others is re-derived against it. To combine them, tcm edit the 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 serve URL 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 as tcm merge's; not made on main)
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; --all includes 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_TOKEN or 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. --tls without 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. one tcm start --base serves); 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.codebase record) so registries show its main branch. url: its tcm serve --public server (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 as tcm release --announce does. --withdraw deletes 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. --http serves a namespace's functions as HTTP endpoints. --trace-calls traces 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's pwa, its manifest, icons and service worker (PWAs). --static writes 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); prune keeps 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 start and tcm dev record 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, serve and studio with 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)

Built from c482a14e