The matcha CLI
matcha scaffolds green-tea projects, runs them in watch mode, and generates pieces already wired into your graph. It is a native binary — installing the CLI needs no JS runtime, and it only reaches for yours when it runs or type-checks your project.
That is deliberate: green-tea runs on Node, Deno, Bun, and the edge, so its tool shouldn’t be bound to one of them.
Install
Section titled “Install”curl -fsSL https://raw.githubusercontent.com/Expressive-Tea/matcha/main/install.sh | shDownloads a checksum-verified prebuilt binary into ~/.local/bin. Environment variables go on the sh side of the pipe:
curl -fsSL …/install.sh | MATCHA_VERSION=v26.7.0 MATCHA_INSTALL_DIR=/usr/local/bin shFrom source, if you have Rust:
cargo install --git https://github.com/Expressive-Tea/matchaScaffold — matcha new
Section titled “Scaffold — matcha new”matcha new my-api # Node (the default)matcha new my-api --runtime deno # or deno | bunmatcha new my-api --template-url gh:owner/repo # any git templateThe starter is alive on the first run: it serves an index.html and streams a rotating zen message over @Sse('/zen'). Open the browser and something is already moving — you are editing a working app, not assembling one.
Only the entry point and the config differ between runtimes. The src/ tree is identical, which is the same portability the framework promises.
Run — matcha run
Section titled “Run — matcha run”matcha runDetects the runtime and starts a watch loop. First match wins:
matcha.toml— an explicitruntime = "node" | "deno" | "bun"deno.json/deno.jsonc→ denobun.lockb/bun.lock→ bunpackage.json→ node
Edge is a deploy target, not a matcha run target.
Generate — matcha create
Section titled “Generate — matcha create”matcha create controller Usersmatcha create step Authenticatematcha create provider Databasematcha create module Billingmatcha create controller Users --check # type-check with your runtime afterwardWrites the file and wires it in — into the right @Module array, or into createApp({ modules }) for a module.
Extend — matcha add
Section titled “Extend — matcha add”matcha add sse # or: stream | bufferInserts a handler of that shape into your controller.
How the edits stay safe
Section titled “How the edits stay safe”create and add edit your TypeScript with tree-sitter, not string splicing or regex. Edits are idempotent — running the same command twice doesn’t duplicate anything — and they revert themselves if the result wouldn’t parse. A generator that corrupts your source is worse than no generator.
One case it deliberately refuses: if modules is a variable (modules: MODULES) rather than an array literal, create module won’t add a second modules key. Duplicate keys parse cleanly, so nothing would fail — it would just silently shadow your value. It tells you instead.