01
Small enough to stay in context
The whole onboarding surface is one 2,193-token AGENTS.md. Every fenced snippet in it executes in CI, so stale examples cannot survive.
v0.2 · MIT · zero-install CLI
Tel gives coding agents one compact surface for CLIs, HTTP APIs and browser UIs — Python-style layout, real JavaScript interop, and a 2,193-token guide that fits in context.
$npx @codebam/tel init
import std.http as http
type User = {id: Num, name: Str}
fn route(req):
match req.path:
"/api/users": [User(id=1, name="ada")]
"/api/sum":
n = int(req.query.n ?? "0")
{sum: (1..=n).sum()}
_: http.res(404, {error: "not found"})
async fn main():
server = await http.serve(int(env.get("PORT", "0")), route)
print("listening on :{server.port}")
server
Why Tel
Tel is designed for the way agents actually write code: a tiny guide, dense but explicit syntax, and a standard library that removes boilerplate.
01
The whole onboarding surface is one 2,193-token AGENTS.md. Every fenced snippet in it executes in CI, so stale examples cannot survive.
02
Python-style layout, ambient bindings, underscore placeholders, pipelines and pattern matching remove ceremony without hiding the shape of the program.
03
Import npm packages and Node builtins, use native prototypes, pass Tel lambdas as JS arrows and await promises — under run, --target js and --target ts.
04
Run it with the tree-walk interpreter, compile typed ESM for Node, or build a reactive browser bundle with the call-style view DSL.
05
match with destructuring and guards, variant types, Result with ? and ??, plus a stdlib that ships http, json, fs, env and time.
06
npx @codebam/tel check reports file:line:col for undefined names, arity and scope mistakes. The same CLI reformats code and counts exact cl100k tokens.
Syntax
Five representative programs. The exact snippets below are counted with the same cl100k_base tokenizer used in the benchmark.
The interpreter calls fn main(args). Strings interpolate; nil falls back with ??.
fn main(args):
name = args[0] ?? "world"
print("hello, {name}")
npx @codebam/tel run app.tel -- ada
std.http serves async handlers. Returning an object sends JSON; http.res overrides.
import std.http as http
async fn handle(req):
match req.path:
"/health": {ok: true}
"/sum": {sum: (0..=int(req.query.n ?? "0")).sum()}
_: http.res(404, "not found")
async fn main():
server = await http.serve(int(env.get("PORT", args[0] ?? "0")), req => handle(req))
print("bound {server.port}")
server
PORT=8080 npx @codebam/tel run app.tel
Top-level bindings inside web fn become signals. Reads re-render; writes in handlers update the DOM.
web fn App():
n = 0
div(class="app",
h1("Counter ", n),
button(onclick=() => n += 1, "+"))
npx @codebam/tel build app.tel --target web -o app.mjs
Variant types, match arms and guards. No exhaustiveness bugs hiding in a switch fallthrough.
type Shape = Circle(r: Num) | Rect(w: Num, h: Num)
fn area(s: Shape) -> Num:
match s:
Circle(r): 3.14159 * r * r
Rect(w, h): w * h
print(area(Circle(2)), area(Rect(3, 4)))
npx @codebam/tel run app.tel
Comprehensions, UFCS methods and placeholder lambdas keep data code dense and readable.
import std.{json}
xs = [3, 1, 4, 1, 5]
evens = xs.filter(_ % 2 == 0).map(_ * 10)
print(json.stringify({count: len(xs), max: xs.max(), evens: evens}))
npx @codebam/tel run app.tel
Token efficiency
Exact cl100k_base BPE counts over equivalent programs that were all executed and compared. Lower is cheaper, and cheaper means more room for the actual task.
| Program | Tel | Python | TypeScript | Tel share of TS |
|---|---|---|---|---|
| FizzBuzz | 58 | 59 | 74 | |
| List pipeline | 78 | 98 | 100 | |
| HTTP JSON handler | 104 | 264 | 210 | |
| Base three | 240 | 421 | 384 |
| v0.2 program | Tel | TypeScript | Tel share of TS |
|---|---|---|---|
| Typed record pipeline | 89 | 124 | |
| Async load | 47 | 52 | |
| Route dispatcher | 164 | 199 | |
| v0.2 three | 300 | 375 |
Six equivalent programs, counted with the exact cl100k_base tokenizer in tools/tokcount.mjs. Methodology and per-file counts live in docs/TOKENS.md. The 2,193-token agent guide is budgeted at 2,200 and contract-tested.
Quickstart
Tel is published as @codebam/tel, so npx runs it with zero project dependencies. tel init also installs AGENTS.md, so your agent picks up the language contract automatically.
Nothing to install globally: npx fetches the published @codebam/tel package. tel init writes main.tel and the 2,193-token AGENTS.md contract.
mkdir app && cd app
npx @codebam/tel init
The interpreter calls fn main(args). No build step and no dependencies to wire up.
npx @codebam/tel run main.tel -- world
Static diagnostics in file:line:col, then typed ESM for Node or a browser bundle with --target web.
npx @codebam/tel check main.tel
npx @codebam/tel build main.tel --target ts --outdir out
For agents
Paste this into your coding agent before it writes any Tel. The guide is small enough to stay in context and every fenced snippet in it executes in CI.
Read AGENTS.md first and follow it. Write new application code in Tel (.tel),
not TypeScript. Use `npx @codebam/tel run FILE` to execute and
`npx @codebam/tel check FILE` before you finish. If you need details
beyond AGENTS.md, read docs/SPEC.md.
Dogfooding
Every dynamic byte here — HTML, JSON and the browser island — is compiled from .tel source and runs at the edge.
SSR
The Worker matches routes in Tel, builds vnodes with the call-style DSL, runs them through html(), and returns a Response.
API
Health, benchmarks and the token estimator are Tel functions. The estimator is the exact same module the browser island imports.
Client
The interactive tour is a web fn component compiled with --target web. Signal writes re-render the DOM; there is no framework underneath.
FAQ
Everything below is verifiable from the repository and the measured results on this page.
Install Node >= 20 and run npx @codebam/tel. The published package is @codebam/tel, so there is no global install: npx @codebam/tel init writes main.tel plus the AGENTS.md guide, and npx @codebam/tel run main.tel executes it.
Tel is v0.2. The runtime, compiler, checker and formatter are covered by a 94-test verification suite, and known limitations are documented. It is young, but already pleasant for small tools, APIs and prototypes.
TypeScript is one of Tel's compile targets. Tel adds a Python-style surface, a higher-level stdlib, built-in checking and token-lean idioms for AI-authored code, then emits typed ESM you can read, review and deploy.
Yes. Default, named and namespace imports work, Tel lambdas are JavaScript arrows, and await works on promises. Packages must be installed exactly as in Node; the Tel toolchain itself needs none.
This site does. Tel compiles to a standalone ESM module; a small generated wrapper turns its exported fetch handler into a Worker default export. The HTML you are reading, the JSON APIs and the browser island are all compiled from Tel.
Each compiled file inlines the shared runtime, so a hello-world bundle is roughly 28 KB before gzip. There are no runtime npm dependencies, and the Worker here needs no Node compatibility flags.
Yes: npx @codebam/tel check gives file:line:col diagnostics, fmt canonicalizes layout, tokens reports exact cl100k_base counts, and init installs the AGENTS.md contract into a project.
MIT.