A reactive modeling language Ψ
Term is a programming framework built on a simple, indentation-based syntax (the tree format). You write the logic once, and one source compiles to idiomatic Rust, TypeScript, Kotlin, and Swift, so the same program runs in the browser, on Node, on native servers, and on iOS and Android. It is dependently typed with a rich type system, ships with a full toolchain (compiler, package manager, and language server), and is designed so the compiler can reach near-optimal native code on each target without the author giving up a clean, readable surface.
Every platform has its own language, its own build tools, its own ecosystem. Writing an app that runs on servers, browsers, iOS, and Android means learning four toolchains, maintaining four codebases, and watching them drift apart. The logic is the same. The plumbing is not.
Dart, through Flutter, has a comparable multi-platform reach but different goals: Term emits each platform's native idioms rather than shipping one runtime, and puts a formal type system and proof checker at the center. Kind is the closer relative on that side, a dependently typed language with a small core, while Term aims more squarely at building real cross-platform applications.
You write the usual types, classes, functions, and data models. Beyond that, templates can parse the tree AST directly, so the boilerplate of building a structure of any shape is generated rather than written by hand.
The kernel underneath is a small dependent type theory: quantitative
type theory (each value tracks how many times it is used), observational
equality, a cumulative universe hierarchy, and self types. On top of it,
a .tree file can state a theorem and prove it by structural induction,
rewriting, and a fixed set of proof steps, and the kernel checks the
proof. The same checker verifies ordinary code, discharging assertions
through decision procedures for linear arithmetic, ring identities,
congruence closure, and nonlinear non-negativity.
Most types are inferred, and a language server gives live diagnostics, hovers, completion, and go-to-definition, with a parser that recovers from errors instead of stopping at the first one. Compilation is incremental, rebuilding only the modules that changed, and the package manager follows the pnpm model, with a content-addressed store and linked dependencies.
Doing this optimally is the hard part. Because meaning is fixed by the type system rather than by how the code is phrased, the backend is free to specialize, monomorphize, and lower aggressively per target, so speed comes from the compiler rather than from the author writing awkward code.
On the application side, the standard library is written in .tree and
covers the usual primitives, collections, IO, text, time, and
cryptography, each mapped to its native counterpart. The web stack is
reactive in the fine-grained style, updating the DOM directly through
signals rather than diffing a virtual DOM, with server-side rendering,
client takeover, and hot module reload.
The goal with Term is to iterate and refine code toward maximal performance while keeping it clearly readable on the other end. These have always been treated as a trade-off: you write it the way you want to read it, or you contort it into the shape the machine wants. Term is built on the premise that they are two representations of one meaning, and that the translation between them is the compiler's job, not the author's.
With AI in the loop, that translation becomes an almost automated process. You write the program as you want to read it. An agent then iterates on the lowering: profiling the emitted Rust, TypeScript, Kotlin, or Swift, trying alternative specializations, measuring, and keeping what wins. Because the type system fixes the meaning precisely, every rewrite can be checked against the source for equivalence, so the loop can run unattended without drifting from what you wrote. The proof kernel makes this stronger than testing: a transformation is not just plausible, it is verified.
Compilers already do this within a fixed budget of built-in passes. The difference is that the search no longer has to stop where the pass list ends. Optimization becomes an open-ended refinement process that accumulates: each program converges toward the best known lowering for its targets, and improvements found for one program feed back into the shared patterns used by all of them. The readable source stays the single artifact you maintain. Everything below it is regenerated, re-verified, and only ever gets faster.
The end state is that the performance ceiling of a Term program is the performance ceiling of the hardware, not of the author's willingness to write unreadable code. You describe the program once, clearly, and the toolchain carries it the rest of the way to essentially optimal machine behavior on every platform it touches.
A complete, example-first guide lives in book/. It
is a tree of short cheatsheet pages: each one opens with a reference of
every keyword, command, or operator in its topic, then shows the .tree
you write to use it, with a note on how it maps to ideas you already
know.
Start with the syntax model, then read what
you need: the language (functions, forms,
matching, loops, traits, templates, modules, and more),
proofs, the
standard library, the
web framework,
command-line apps, and the
term toolchain.
This repository is a monorepo. Each part of the ecosystem is a deck (a
package) under deck/. A deck's name comes from the deck <name> line
in its own deck.tree, not from package.json, and the decks import
each other by that name (@term/base/code/list), linked locally through
term link.
The compiler, CLI, language server and package manager are written in
TypeScript (under each deck's code/). The standard library, the app
framework and everything else are written in .tree and compiled by the
compiler itself.
.
├── deck/ every package
│ ├── make/ @term/make the compiler (TypeScript)
│ │ └── code/
│ │ ├── parser/ .tree text to a generic tree, and diagnostic codes
│ │ ├── compile/ mill to typed AST, then one emitter per backend
│ │ │ (typescript.ts, rust.ts, kotlin.ts, swift.ts, wgsl.ts)
│ │ ├── check/ type inference, the kernel, the prover, contracts
│ │ ├── lint/ lint rules
│ │ └── format/ the formatter
│ ├── mill/ @term/mill the DSL grammars, one mine.tree and
│ │ └── code/<name>/ mint.tree per dialect (code, view, host, zone, ...)
│ ├── call/ @term/call the CLI, built to host/line.js
│ │ └── code/ line.ts is the entry, make.ts the build driver,
│ │ test.ts the test runner, hold.ts the proof gate
│ ├── flow/ @term/flow the language server
│ ├── deck/ @term/deck the package manager
│ ├── base/ @term/base the standard library, all .tree
│ │ ├── code/ one file per type (list.tree, text.tree, ...)
│ │ │ ├── mask/ traits
│ │ │ ├── proof/ equality and proof lemmas
│ │ │ └── native/ per-platform impls (node, browser, rust, swift, kotlin, shared)
│ │ ├── test/ stdlib tests
│ │ └── hold.json the proof baseline `term hold` reads
│ ├── site/ @term/site app framework: DOM, http, graphics
│ ├── face/ @term/face headless UI components
│ ├── bind/ @term/bind typed platform bindings, the largest deck
│ ├── host/ @term/host the data dialect, read at run time
│ ├── feed/ @term/feed text and binary format grammars
│ ├── cask/ @term/cask native app shell: window, WebView, bridge
│ ├── scan/ @term/scan dependency and advisory scanning
│ └── test/ @term/test fuzzing, benchmarks, model checking
├── book/ the language guide, one cheatsheet page per topic
├── test/ the TypeScript suites, by area (parser/, check/,
│ compile/, call/, native/, ...)
├── host/line.js the built CLI. Rebuild it after editing any .ts
├── link/ locally linked decks
└── deck.tree this workspace's own declaration
Where to start digging:
| you want | open |
|---|---|
| what a keyword means | book/, then the dialect in deck/mill/code/ |
| why a file fails to compile | deck/make/code/check/ |
| what a backend emits | deck/make/code/compile/<target>.ts |
| what a CLI command does | deck/call/code/line.ts, then the file it dispatches to |
| a stdlib type or task | deck/base/code/<name>.tree |
| the tests for any of it | test/<area>/ |
Design notes, plans and the roadmap live outside this tree, in the
parent repository's note/term/.
.tree source
↓
make (compiler): parse → resolve → check → emit
├─→ TypeScript (browsers, Node.js)
├─→ Rust (servers, CLI, embedded)
├─→ Kotlin (Android, JVM)
├─→ Swift (iOS, macOS)
├─→ LLVM (native binaries)
└─→ WGSL (GPU shaders)
The compiler parses .tree files into a surface AST, mills them into a
typed AST, resolves names, and type-checks through a gradual,
bidirectional inference pass that elaborates into a dependent kernel for
soundness. Each backend then emits idiomatic output for its platform.
Generics, traits, async, and effects lower to the natural construct on
each target: native traits on Rust, protocols on Swift, interfaces on
Kotlin, and monomorphization on LLVM and WGSL.
The CLI (call) drives the whole pipeline and ships a dev server with hot module reload. The language server (flow) reuses the same analysis for diagnostics, hover, and go-to-definition.
pnpm add @cluesurf/term -g
Working on Term itself takes Node, pnpm, Rust, Swift, a JDK, Kotlin, Maven and, for the mobile suites, the Android
SDK and the iOS simulator. One command checks them all on macOS, Linux or Windows, and installs what is missing with
--commit:
sh task/dev/bootstrap/start.sh check
sh task/dev/bootstrap/start.sh install --commit
powershell -ExecutionPolicy Bypass -File task\dev\bootstrap\start.ps1 check
# Compile a project
term make
# Run in dev mode (watch + hot reload)
term flow
# Add a package
term deck save <package>
# Run tests
term testtask double
take value, like number
like number
send back
call add
read value
read value
Compiles to:
Rust
fn double(value: i64) -> i64 {
return value + value;
}TypeScript
export function double(value: number): number {
return value + value
}MIT
Made by ClueSurf, meditating on the universe.