This is the complete mlld reference guide, combining all documentation modules. For modular access, see individual files in docs/llm/. Generated: 2026-01-05T04:55:10.935Z mlld is a modular prompt scripting language for dynamically assembling context and orchestrating LLMs—think Make + npm for the LLM era, or a Unix pipe for chaining discrete AI/tool steps. mlld has two syntax modes based on file extension: **.mld files (strict mode)** - Default for scripts - Bare directives: `var @x = 1` (no slash prefix) - Text lines are errors (catches accidental output) - Blank lines ignored (formatting whitespace) **.md or .mld.md files (markdown mode)** - For documentation/literate scripts - Slash prefix required: `/var @x = 1` - Text lines become content output - Designed for mixing prose with executable code This guide uses **strict mode** (bare directives) in all examples. To use markdown mode, add `/` prefix to each directive. ```mlld >> Strict mode (.mld) var @name = "Alice" show `Hello @name!` >> Markdown mode (.mld.md) - same code with slashes /var @name = "Alice" /show `Hello @name!` ``` What mlld IS: * A workflow orchestrator (like Make + npm for the AI era) * A conductor's baton (you conduct, tools play) * Executable documentation (reads like a guide, runs like a script) * A logical router (route data and actions based on conditions) What mlld ISN'T: * A template engine (not Jinja/Handlebars) * A shell script replacement (it orchestrates shells; doesn't replace them) Mental model shift: * "How do I implement this?" → "What tool/module handles this?" * "I need an if-statement" → "I need a decision point" * "Let me concatenate strings" → "Let me create a template" Think Docker Compose or GitHub Actions: declare what happens, don't program how. **Directives** - Commands that do things: `var`, `show`, `run`, `for`, `when`, `import`, `export` **Variables** - Always prefixed with `@`: `@name`, `@data`, `@result` **Templates** - Backticks or `::...::` for interpolation: `` `Hello @name` `` **File loading** - Angle brackets load content: ``, `` **Pipelines** - Chain transformations: `@data | @json | @validate` **Executables** - Reusable functions: `exe @greet(name) = `Hello @name!`` **Blocks** - Multi-statement bodies with `let` for local variables: `exe @f(x) = [ let @y = @x * 2; => @y ]` **Modules** - Import/export for code reuse: `import { @helper } from @corp/utils` In strict mode (.mld), directives are bare keywords. In markdown mode (.mld.md), prefix with `/`. ```mlld >> Strict mode var @greeting = `Hello @name!` show @greeting >> What NOT to do Hello @name! Let me show something. >> This is an error in strict mode ``` Create with `var @name = value`. Reference with `@name` in templates/commands. ```mlld var @name = "Alice" show `Hello @name!` run cmd {echo "User: @name"} run js (@name) { console.log("Hi", name) } ``` Every command needs braces. Use language specifiers: `cmd {}` for simple commands, `sh {}` for shell scripts, `js {}` for code. ```mlld >> Language specifiers run cmd {echo "hello"} >> simple commands (pipes only) run sh {npm test && build} >> shell scripts (&&, ||, multi-line) run js {console.log("hi")} >> JavaScript code >> Executables exe @hi() = cmd {echo hi} exe @script() = sh { echo "multi"; ls -la } exe @calc(x) = js { return x * 2 } ``` Only these produce output: `show`, `run`, `output`. Everything else mutates state. ```mlld var @secret = "hidden" >> no output show `Visible` run cmd {echo "Also visible"} ``` Default to `::...::` or backticks for templates. Use `@var` for interpolation. | Syntax | Interpolation | Use For | |--------|---------------|---------| | `` `...` `` | `@var` `` | Default inline | | `::...::` | `@var` `` | When backticks in text | | `"..."` | `@var` `` | Single-line only | | `'...'` | None (literal) | Literal text | | `{...}` | `@var` `` | Commands/code | ```mlld >> Backticks (default) var @msg = `Hello @name!` >> Double-colon (when text has backticks) var @doc = ::Use `npm test` before @env:: >> Double quotes (single-line) var @path = "@base/files/@filename" >> Single quotes (literal - no interpolation) var @literal = '@name stays literal' ``` Loops work inside templates: ```mlld var @list = ` for @item in @items - @item.name: @item.value end ` ``` Objects/arrays use dot notation. Arrays support slicing. Methods available on values. ```mlld var @user = {"name":"Alice","scores":[10,20,30]} show @user.name >> Alice show @user.scores.1 >> 20 >> Array slicing var @arr = [1,2,3,4,5] show @arr[0:3] >> [1,2,3] show @arr[-2:] >> [4,5] - last 2 >> Builtin methods var @list = ["apple", "banana"] show @list.includes("banana") >> true show @list.join(", ") >> "apple, banana" var @text = "Hello World" show @text.toLowerCase() >> "hello world" show @text.split(" ") >> ["Hello", "World"] show @text.trim().startsWith("H") >> true (chained) ``` Use `exe` for reusable templates, commands, or complex logic with blocks. ```mlld >> Simple template exe @greet(name) = `Hello @name!` show @greet("Bob") >> Command exe @list(dir) = cmd {ls -la @dir | head -5} >> Block syntax for multi-statement bodies exe @process(data) = [ let @validated = @validate(@data) let @transformed = @transform(@validated) => @transformed ] ``` Angle brackets load file contents. Works with globs and AST selectors. ```mlld var @content = >> file contents var @config = >> parsed as object var @author = .author >> field access >> Globs var @docs = show @docs.length >> number of files for @doc in @docs => show @doc.mx.filename >> AST selectors (code extraction) var @funcs = >> all functions var @handler = >> specific function ``` Detection rule: only `<…>` with `.`, `/`, `*`, or `@` are file refs. XML-like `` is plain text. Module imports for code reuse. Namespace imports avoid collisions. ```mlld >> Registry modules import { @parallel, @retry } from @mlld/core import @corp/utils as @corp >> Local files import { @helper } from "./utils.mld" import { @config } from <@base/config.mld> >> Import types import module { @api } from @corp/tools >> cached import static { @prompt } from "./prompt.md" >> embedded at parse import live as @status >> always fresh >> Directory imports import "@agents" as @agentRegistry show @agentRegistry.alice.tldr ``` `when` for conditionals. Use `first` for switch-style (stops at first match). ```mlld >> Simple condition when @isProd => show "Production mode" >> Switch-style (first match wins) when first [ @role == "admin" => show "Admin" @role == "user" => show "User" * => show "Guest" ] >> Bare when (evaluates all matches) when [ @score > 90 => show "Excellent!" @hasBonus => show "Bonus earned!" none => show "No matches" ] ``` `foreach` transforms collections. `for` executes or collects. ```mlld >> foreach (transform array) var @names = ["alice", "bob"] exe @greet(name) = `Hi @name!` var @greetings = foreach @greet(@names) >> for (execute per item) for @n in @names => show `Name: @n` >> for (collect results) var @doubled = for @x in [1,2,3] => @x * 2 >> for with block syntax for @item in @items [ let @processed = @transform(@item) show `Done: @processed` ] >> Parallel execution for parallel(3) @task in @tasks => @runTask(@task) >> For with inline filter var @valid = for @x in @items when @x != null => @x ``` Logical, comparison, and ternary operators. ```mlld >> Comparison: <, >, <=, >=, ==, != >> Logical: &&, ||, ! >> Ternary: condition ? trueVal : falseVal var @isValid = @score > 80 && @submitted var @status = @isPro ? "premium" : "basic" var @canEdit = @isOwner || (@role == "editor" && !@isLocked) >> In conditions when @tokens > 1000 && @mode == "production" => show "High usage" ``` State updates via `state://` protocol. SDK captures writes without filesystem I/O. ```mlld var @count = @state.count + 1 output @count to "state://count" ``` ```typescript // SDK provides state, captures writes execute(file, payload, { state: { count: 0 } }); // Returns: { stateWrites: [{ path: 'count', value: 1 }] } ``` Create primitives, arrays, objects, or assign from command/code results. ```mlld var @n = 42 var @price = 19.99 var @ok = true var @arr = [1, 2, 3] var @obj = {"key": "value"} var @merged = { ...@obj, "extra": 1 } >> object spread exe @add(a, b) = js { return a + b } var @sum = @add(@n, 8) >> 50 (number preserved) var @date = cmd {date} >> command result var @readme = >> file contents ``` **Conditional inclusion** (`@var?`): omit content when variable is falsy. ```mlld var @tools = "json" var @empty = "" >> In commands: @var?`...` run cmd { echo @tools?`--tools "@tools"` @empty?`--empty` } >> Output: --tools "json" (--empty omitted) >> In arrays var @list = [@a, @b?, @c] >> @b omitted if falsy >> In objects var @obj = {"name": @n, "title"?: @t} >> title omitted if @t falsy ``` Truthiness: falsy = `null`, `undefined`, `""`, `"false"`, `"0"`, `0`, `NaN`, `[]`, `{}` Prefer backticks; use `::` for backticks-in-text. ```mlld var @message = `Hello @name, welcome!` var @doc = ::Use `mlld` to orchestrate:: >> Multi-line var @report = ` Status: @status Config: <@base/config.json> Data: @data|@json ` ``` **When-expressions in templates:** ```mlld var @status = when [ @score > 90 => "A" * => "F" ] var @arr = [ 1, when [ @flag => 2 ], 3 ] ``` **Loops in templates:** ```mlld var @toc = ` for @item in @items - @item.name end ` ``` Note: `for` and `end` must be at line start inside template body. Angle brackets load file contents. Supports field access, globs, and AST selection. ```mlld >> Basic loading var @content = var @config = >> auto-parsed as object var @author = .author >> field access >> Globs (returns array) var @docs = show @docs.length for @doc in @docs => show @doc.mx.filename >> With "as" template var @toc = as "- [<>.mx.fm.title](<>.mx.relative)" ``` **AST Selection** (extract code from files): ```mlld >> Exact names var @handler = >> Wildcards var @handlers = >> prefix match var @validators = >> suffix match >> Type filters var @funcs = >> all functions var @classes = >> all classes >> Name listing (returns string arrays) var @names = >> all definition names var @funcNames = >> function names only ``` Supported: `.js`, `.ts`, `.jsx`, `.tsx`, `.py`, `.go`, `.rs`, `.java`, `.rb` Type keywords: `fn`, `var`, `class`, `interface`, `type`, `enum`, `struct` **Metadata fields** (via `.mx`): ```mlld var @file = show @file.mx.filename >> "README.md" show @file.mx.relative >> relative path show @file.mx.tokens >> token count estimate show @file.mx.fm.title >> frontmatter field ``` **JSON string accessors** (`.data` and `.text`): ```mlld >> When you have a JSON string and need to parse it var @jsonStr = '[{"name":"Alice"},{"name":"Bob"}]' var @parsed = @jsonStr.data >> parses JSON string to array/object show @parsed.0.name >> "Alice" >> When you have an object and need the JSON string var @obj = {"name": "Alice"} var @str = @obj.text >> stringified JSON show @str >> '{"name":"Alice"}' >> Common in pipelines with LLM responses var @response = @llm("return JSON") | @json.llm var @items = @response.data >> if response is JSON string ``` Arrays and strings have builtin methods. **Array methods:** - `@arr.includes(value)` - true if contains value - `@arr.indexOf(value)` - index or -1 - `@arr.length` - array length - `@arr.join(separator)` - join to string **String methods:** - `@str.includes(sub)` - true if contains substring - `@str.indexOf(sub)` - index or -1 - `@str.length` - string length - `@str.toLowerCase()` / `toUpperCase()` - `@str.trim()` - remove whitespace - `@str.startsWith(prefix)` / `endsWith(suffix)` - `@str.split(separator)` - split to array ```mlld var @fruits = ["apple", "banana", "cherry"] var @message = "Hello World" show @fruits.includes("banana") >> true show @fruits.join(" and ") >> "apple and banana and cherry" show @message.toLowerCase() >> "hello world" show @message.split(" ") >> ["Hello", "World"] >> Method chaining show @message.trim().toLowerCase().startsWith("hello") >> true ``` Chain stages with `|`. Built-ins: `@json`, `@xml`, `@csv`, `@md`. ```mlld var @users = cmd {cat users.json} | @json | @csv >> Custom functions in pipelines exe @double(n) = js { return n * 2 } var @x = cmd {echo "5"} | @double >> JSON parsing modes var @relaxed = @input | @json.loose >> single quotes, trailing commas var @strict = @input | @json.strict >> strict JSON only var @extracted = @llmResponse | @json.llm >> extract from LLM response ``` **Pipeline context:** - `@mx.try` - current attempt number - `@mx.stage` - current stage name - `@p[-1]` - previous stage output **Retry in pipelines:** ```mlld exe @validator(input) = when [ @input.valid => @input @mx.try < 3 => retry "need more detail" * => "fallback" ] var @result = @raw | @validator ``` **Parallel groups:** ```mlld >> Two transforms run concurrently var @results = || @fetchA() || @fetchB() || @fetchC() >> With concurrency cap var @capped = || @a() || @b() || @c() (2, 100ms) >> cap=2, 100ms pacing ``` Use `>>` at start of line or `<<` at end. ```mlld >> This is a comment var @x = 5 << end-of-line comment show @x >> also works here ``` - `@now` - current timestamp - `@input` - stdin/env (must be allowed in config) - `@base` - project root path - `@debug` - environment info - `@fm` - current file's frontmatter (in modules) Execute shell commands or code. Language specifier determines execution context. **Decision tree:** - Single line + pipes only → `cmd { ... }` (safe, recommended) - Needs `&&`, `||`, control flow → `sh { ... }` (full shell) - JavaScript/Python code → `js { ... }` / `python { ... }` ```mlld >> cmd (pipes only, safe) run cmd {echo Hello | tr '[:lower:]' '[:upper:]'} var @date = cmd {date} >> sh (full shell scripts) run sh { npm test && npm run build || echo "Build failed" } >> js/python (code execution) run js {console.log("hello")} var @result = js {return 42} ``` **Working directory override:** ```mlld run cmd:/ {pwd} >> runs in / run sh:/tmp {pwd} >> runs in /tmp run js:/tmp {console.log(process.cwd())} ``` **Stdin support:** ```mlld var @data = '[{"name":"Alice"}]' run cmd { cat | jq '.[]' } with { stdin: @data } >> Pipe sugar (equivalent) run @data | { cat | jq '.[]' } ``` **Parameter syntax by language:** - `cmd`: interpolate with `@param` - `sh`: use shell variables as `$param` - `js/python`: parameters passed as variables Define reusable commands, code, templates, or multi-statement blocks. **Simple forms:** ```mlld >> Command exe @list(dir) = cmd {ls -la @dir | head -5} >> JavaScript exe @add(a, b) = js { return a + b } >> Template exe @greet(name) = `Hello @name!` >> External template file exe @welcome(name, role) = template "./prompts/welcome.att" >> Prose (requires config) exe @analyze(data) = prose:@config { session "Analyze @data" } ``` **Prose execution** (LLM skill invocation): Prose requires a config reference specifying the model and skill: ```mlld var @config = { model: "claude-3", skillName: "prose" } >> Inline (interpolates like templates) exe @summarize(text) = prose:@config { summarize @text } >> File reference (.prose files do NOT interpolate) exe @review(code) = prose:@config "./review.prose" >> Template files (.prose.att or .prose.mtt interpolate) exe @greet(name) = prose:@config "./greet.prose.att" ``` Interpolation rules: - `prose:@config { inline }` - interpolates `@var` like templates - `"file.prose"` - no interpolation, raw content - `"file.prose.att"` - ATT interpolation (`@var`) - `"file.prose.mtt"` - MTT interpolation (`{{var}}`) Default skill is `"prose"` (OpenProse). Custom interpreters via `skillName`. **Block syntax** (multi-statement bodies): ```mlld exe @process(data) = [ let @validated = @validate(@data) let @transformed = @transform(@validated) => @transformed ] >> With accumulation exe @countItems(items) = [ let @count = 0 for @item in @items [ let @count += 1 ] => @count ] ``` Block rules: - Use `[...]` for multi-statement bodies - `let @var = value` for block-scoped variables - `let @var += value` for accumulation (arrays/strings/objects) - `=> value` required as last statement for return **When-first in exe** (value-returning): ```mlld exe @classify(score) = when first [ @score >= 90 => "A" @score >= 80 => "B" @score >= 70 => "C" * => "F" ] >> With blocks for side effects exe @handler(input) = when first [ @input.valid => [ show "Processing..." let @result = @transform(@input) => @result ] * => { error: "Invalid input" } ] ``` **Shadow environments** (expose JS helpers): ```mlld exe @double(n) = js { return n * 2 } exe @cap(s) = js { return s[0].toUpperCase() + s.slice(1) } exe js = { double, cap } >> expose to all js blocks var @out = js { cap("hello") + ": " + double(5) } >> "Hello: 10" ``` Write data to files or streams. ```mlld output @content to "out.txt" output @data to "config.json" output @message to stdout output @error to stderr output @config to "settings.yaml" as yaml ``` Syntactic sugar for `output to stdout`. Works in action contexts. ```mlld log @message >> same as output @message to stdout log `Processing: @item` >> In action contexts for @item in @items => log @item when @debug => log "Debug info" ``` Append newline-delimited records. ```mlld append @record to "events.jsonl" >> JSON object per line append "raw line" to "events.log" >> In pipelines var @_ = @data | append "audit.jsonl" >> In loops for @name in @runs => append @name to "pipeline.log" ``` `.jsonl` enforces JSON serialization. Other extensions write text. `.json` blocked. Stream output during execution. ```mlld stream @claude("prompt") >> keyword form stream @generateReport() >> directive form >> Parallel streams stream @a() || stream @b() >> concurrent, buffered results ``` Suppress: `--no-stream` flag or `MLLD_NO_STREAM=true` `when` handles conditionals. Three forms: simple, bare (all matches), first (switch-style). **Simple form:** ```mlld when @isProd => show "Production mode" when @score > 90 => show "Excellent!" ``` **Bare form** (evaluates all matching conditions): ```mlld when [ @score > 90 => show "Excellent!" @hasBonus => show "Bonus earned!" none => show "No matches" >> runs only if nothing matched ] ``` **First form** (stops at first match, like switch): ```mlld when first [ @role == "admin" => show "Admin panel" @role == "user" => show "User dashboard" * => show "Guest view" >> wildcard catches all ] ``` **Value-returning when** (in exe): ```mlld exe @classify(score) = when first [ @score >= 90 => "A" @score >= 80 => "B" * => "F" ] var @grade = @classify(85) >> "B" ``` **Block actions** (side effects + return): ```mlld var @result = when first [ @needsProcessing => [ show "Processing..." let @processed = @transform(@data) => @processed ] * => @data ] ``` **Local variables in when:** ```mlld when @mode: [ let @prefix = "Status:" "active" => show "@prefix Active" "pending" => show "@prefix Pending" * => show "@prefix Unknown" ] ``` **Augmented assignment:** ```mlld exe @collect() = when [ let @items = [] @items += "a" @items += "b" * => @items >> ["a", "b"] ] ``` `+=` works with arrays (concat), strings (append), objects (merge). **Operators in conditions:** - Comparison: `<`, `>`, `<=`, `>=`, `==`, `!=` - Logical: `&&`, `||`, `!` - Parentheses: `(@a || @b) && @c` ```mlld when first [ @role == "admin" || @role == "mod" => show "Privileged" @active && @verified => show "Active user" !@banned => show "Allowed" * => show "Blocked" ] ``` `for` iterates over collections. Arrow form for single actions, block form for multiple. **Arrow form:** ```mlld for @item in @items => show `Processing @item` for @n in [1,2,3] => log @n ``` **Collection form** (returns results): ```mlld var @doubled = for @x in [1,2,3] => @x * 2 >> [2, 4, 6] var @names = for @user in @users => @user.name ``` **Block form:** ```mlld for @item in @items [ let @processed = @transform(@item) show `Done: @processed` ] >> Collection with block var @results = for @item in @items [ let @step1 = @validate(@item) let @step2 = @transform(@step1) => @step2 ] ``` **For with inline filter:** ```mlld var @valid = for @x in @items when @x != null => @x var @admins = for @u in @users when @u.role == "admin" => @u.name ``` **Skip keyword** (drop items from results): ```mlld var @filtered = for @x in @items => when [ @x.valid => @x none => skip >> omit this item from results ] >> Equivalent to inline filter, but allows complex logic var @processed = for @item in @data => when first [ @item.type == "a" => @transformA(@item) @item.type == "b" => @transformB(@item) * => skip >> unknown types dropped ] ``` **Object iteration:** ```mlld var @cfg = {"host": "localhost", "port": 3000} for @v in @cfg => show `@v.mx.key: @v` >> Output: host: localhost, port: 3000 ``` **Nested for:** ```mlld for @x in ["A","B"] => for @y in [1,2] => show `@x-@y` >> Output: A-1, A-2, B-1, B-2 ``` **Batch pipelines** (process collected results): ```mlld var @total = for @n in [1,2,3,4] => @n => | @sum var @sorted = for @item in @items => @process(@item) => | @sortBy("priority") ``` Run iterations concurrently with `for parallel`. ```mlld >> Default concurrency (MLLD_PARALLEL_LIMIT, default 4) for parallel @x in @items => show @x >> Custom concurrency cap for parallel(3) @task in @tasks => @runTask(@task) >> With pacing (delay between starts) for parallel(2, 1s) @x in @items => @process(@x) ``` **Parallel blocks:** ```mlld for parallel(3) @task in @tasks [ let @result = @runTask(@task) show `Done: @task.id` ] ``` **Error handling:** - Errors accumulate in `@mx.errors` - Failed iterations add error markers to results - Outer-scope writes blocked (use block-scoped `let` only) ```mlld exe @process(tasks) = [ let @results = for parallel @t in @tasks => @run(@t) => when [ @mx.errors.length == 0 => @results * => @repair(@results, @mx.errors) ] ] ``` `foreach` applies a function to each element, returning transformed array. ```mlld var @names = ["alice", "bob", "charlie"] exe @greet(name) = `Hi @name!` var @greetings = foreach @greet(@names) >> ["Hi alice!", "Hi bob!", "Hi charlie!"] ``` **In exe:** ```mlld exe @wrapAll(items) = foreach @wrap(@items) show @wrapAll(["a", "b"]) >> ["[a]", "[b]"] ``` **With options:** ```mlld show foreach @greet(@names) with { separator: " | " } >> "Hi alice! | Hi bob! | Hi charlie!" ``` Bounded iteration with `while`. ```mlld exe @countdown(n) = when [ @n <= 0 => done "finished" * => continue (@n - 1) ] var @result = 5 | while(10) @countdown ``` **Control keywords:** - `done @value` - Terminate, return value - `done` - Terminate, return current state - `continue @value` - Next iteration with new state - `continue` - Next iteration with current state **While context** (`@mx.while`): - `iteration` - Current iteration (1-based) - `limit` - Configured cap - `active` - true when inside while **With pacing:** ```mlld var @result = @initial | while(100, 1s) @processor >> 1s between iterations ``` mlld has no `return` or `exit`. Model outcomes with `when` and flags. ```mlld >> Instead of early return, use conditional flow var @check = @validate(@input) when [ @check.valid => @process(@input) !@check.valid => show `Error: @check.message` ] ``` Module-first design: keep `.mld` files readable; move complexity into focused modules. Avoid "kitchen sink" modules or side effects on import. Modules require frontmatter and explicit exports. ```mlld --- name: text-utils author: alice version: 1.0.0 about: String helpers license: CC0 --- needs { js: [] } exe @upper(s) = js { return s.toUpperCase() } exe @trim(s) = js { return s.trim() } export { @upper, @trim } ``` **Frontmatter fields:** - `name` - Module name (required for registry) - `author` - Your username (required for registry) - `version` - Semver version - `about` - Brief description - `license` - License (CC0 recommended) **Accessing frontmatter in module:** ```mlld var @meta = { id: @fm.id, name: @fm.name, version: @fm.version } ``` Import from registry, local files, or URLs. **Registry modules:** ```mlld import { @parallel, @retry } from @mlld/core import @corp/utils as @corp >> With version import { @helper } from @alice/utils@1.0.0 import { @helper } from @alice/utils@^1.0.0 >> semver range ``` **Local files:** ```mlld import { @helper } from "./utils.mld" import { @config } from <@base/config.mld> import { @prompt } from "../prompts/main.mld" ``` **Namespace imports:** ```mlld import @alice/utils as @alice import @bob/utils as @bob show @alice.format(@data) show @bob.format(@data) >> no collision ``` **Directory imports:** ```mlld import "@agents" as @agentRegistry show @agentRegistry.alice.tldr show @agentRegistry.support.helper.name >> With options import "./agents" as @agents with { skipDirs: [] } ``` Directories auto-load `*/index.mld`. Default `skipDirs: ["_*", ".*"]`. Control caching and resolution behavior. | Type | Behavior | Use Case | |------|----------|----------| | `module` | Content-addressed cache | Registry modules (default) | | `static` | Embedded at parse time | Prompts, templates | | `live` | Always fresh | Status APIs | | `cached(TTL)` | Time-based cache | Feeds, configs | | `local` | Dev modules (llm/modules/) | Development | | `templates` | Directory of .att files | Template collections | ```mlld import module { @api } from @corp/tools import static { @prompt } from "./prompt.md" import live as @status import cached(1h) as @feed import local { @dev } from @alice/experimental ``` **Template collections:** ```mlld import templates from "@base/agents" as @agents(message, context) show @agents["alice"](@msg, @ctx) >> agents/alice.att show @agents.support["helper"](@msg, @ctx) >> agents/support/helper.att ``` Template collections require parameters. All templates in directory share the signature. Explicit exports required (auto-export is legacy). ```mlld >> Export specific items export { @upper, @trim, @format } >> Common pattern: export config object var @meta = { id: @fm.id, name: @fm.name } exe @process(data) = [...] export { @meta, @process } ``` **Module patterns:** ```mlld >> Library module exe @haiku(prompt) = @prompt | cmd { claude -p --model haiku } exe @sonnet(prompt) = @prompt | cmd { claude -p --model sonnet } export { @haiku, @sonnet } >> Config/agent module var @meta = { id: @fm.id, name: @fm.name } var @prompts = { primary: @primaryPrompt, optional: @optionalPrompt } export { @meta, @prompts } >> Gate module exe @gate(response, instruction, message) = [...] export { @gate } ``` Dev modules live in `llm/modules/` (flat structure). ``` llm/modules/ ├── my-utils.mld.md # author: alice, name: experimental └── helpers.mld # author: bob, name: tools ``` ```mlld import local { @helper } from @alice/experimental ``` Matched by frontmatter `author` and `name` fields. Public registry at github.com/mlld-lang/registry. **Publishing:** ```bash mlld publish my-tool.mld.md # first time creates PR mlld publish my-tool.mld.md # updates publish directly mlld publish --tag beta my-tool.mld.md # with tag ``` **Installing:** ```bash mlld install @alice/utils mlld install @alice/utils@1.0.0 mlld update @alice/utils mlld ls # list installed ``` **Lock files:** - `mlld-lock.json` auto-generated - Commit to version control - Only registry modules validated Prefixes map `@` references to content sources. **Built-in:** - `@author/module` → Registry - `@base/file` → Project root - `./file.mld` → Local (with fuzzy extension matching) **Custom prefixes** (mlld-config.json): ```json { "resolvers": { "prefixes": [ { "prefix": "@lib/", "resolver": "LOCAL", "config": { "basePath": "./src/lib" } }, { "prefix": "@company/", "resolver": "GITHUB", "config": { "repository": "company/private-modules", "branch": "main" } } ] } } ``` **Quick setup:** ```bash mlld alias --name notes --path ~/notes mlld alias --name shared --path ../shared --global mlld setup --github # private repo wizard ``` Coordinate multiple tools with data flow. ```mlld var @areas = [ {"name": "auth", "files": ["auth/*.ts"], "tests": ["test/auth/*"]}, {"name": "api", "files": ["api/*.ts"], "tests": ["test/api/*"]} ] exe @runQA(area) = cmd {echo "Testing @area.name" | cat} var @results = foreach @runQA(@areas) ``` Chain transformations with validation. ```mlld import { @fetchData, @validate, @transform } from @data/pipeline var @raw = @fetchData("https://api.example.com/users") var @valid = @validate(@raw, { schema: "user" }) var @report = @transform(@valid, { format: "report" }) show `Processed @report.count users` ``` **With built-in transforms:** ```mlld var @data = cmd {curl -s https://api.example.com/data} var @processed = @data | @json | @validate | @transform | @csv output @processed to "report.csv" ``` Route execution based on conditions. ```mlld import { @getPR, @commentOnPR } from @company/github var @pr = @getPR(@MLLD_PR_NUMBER) var @status = when first [ @pr.mergeable => "ready" * => "blocked" ] when [ @status == "ready" => @commentOnPR(@MLLD_PR_NUMBER, "Ready to merge") @status == "blocked" => show "Needs attention" ] ``` Validate at each step before proceeding. ```mlld var @processed = @data | @validate | @normalize | @analyze when [ @processed.ok => @emitReport(@processed) !@processed.ok => show "Validation failed" ] ``` Score and route to different handlers. ```mlld exe @router(message, handlers) = [ let @scores = for @h in @handlers => { handler: @h.name, score: @h.scorer(@message) } let @best = @scores | @sortBy("score") | @first => when first [ @best.score > 0.7 => @handlers[@best.handler].handle(@message) * => null ] ] ``` Validate or filter before proceeding. ```mlld exe @gate(response, config) = [ let @check = @validate(@response) => when first [ !@config.required => { pass: true } @check.valid => { pass: true } * => { pass: false, reason: @check.error } ] ] ``` Run independent tasks concurrently. ```mlld >> Parallel for for parallel(3) @task in @tasks [ let @result = @runTask(@task) show `Done: @task.id` ] >> Parallel pipeline groups var @results = || @fetchA() || @fetchB() || @fetchC() >> With error handling exe @runAll(tasks) = [ let @results = for parallel @t in @tasks => @run(@t) => when [ @mx.errors.length == 0 => @results * => @repair(@results, @mx.errors) ] ] ``` Call LLMs with structured prompts. ```mlld import { @haiku, @sonnet } from "@lib/claude.mld" exe @classify(text) = [ let @prompt = `Classify this text as positive/negative/neutral: @text` let @response = @haiku(@prompt) => @response.trim().toLowerCase() ] exe @analyze(data) = [ let @prompt = `Analyze this data and return JSON: @data|@json` let @response = @sonnet(@prompt) => @response | @json.llm ] ``` Define agent configuration modules. ```mlld --- id: my-agent name: My Agent --- var @meta = { id: @fm.id, name: @fm.name, workDir: "/path/to/work" } exe @systemPrompt(context) = template "./prompts/system.att" exe @primaryPrompt(msg, ctx) = template "./prompts/primary.att" var @prompts = { primary: @primaryPrompt } export { @meta, @prompts, @systemPrompt } ``` mlld uses dual configuration: - `mlld-config.json` - Your project settings (edit manually) - `mlld-lock.json` - Auto-generated locks (don't edit) Allow env vars in config, then import via `@input`. **mlld-lock.json:** ```json { "security": { "allowedEnv": ["MLLD_NODE_ENV", "MLLD_API_KEY", "MLLD_GITHUB_TOKEN"] } } ``` **Usage:** ```mlld import { @MLLD_NODE_ENV, @MLLD_API_KEY } from @input show `Running in @MLLD_NODE_ENV` ``` All env vars must be prefixed with `MLLD_`. Document metadata at file start. ```yaml --- name: my-module author: alice version: 1.0.0 about: Brief description license: CC0 --- ``` Access via `@fm`: ```mlld var @id = @fm.id var @version = @fm.version ``` Paths can be literal, interpolated, or resolver-based. ```mlld var @dir = "./docs" var @userFile = "data/@username/profile.json" var @template = 'templates/@var.html' >> literal '@' >> URLs as sources show var @remote = ``` Four modes for SDK consumers: **document** (default): Returns string ```typescript const output = await processMlld(script); ``` **structured**: Returns full result object ```typescript const result = await interpret(script, { mode: 'structured' }); console.log(result.effects); console.log(result.stateWrites); ``` **stream**: Real-time events ```typescript const handle = interpret(script, { mode: 'stream' }); handle.on('stream:chunk', e => process.stdout.write(e.text)); await handle.done(); ``` **debug**: Full trace ```typescript const result = await interpret(script, { mode: 'debug' }); console.log(result.trace); ``` Inject runtime context without filesystem I/O. ```typescript processMlld(template, { dynamicModules: { '@state': { count: 0, messages: [...] }, '@payload': { text: 'user input', userId: '123' } } }); ``` ```mlld var @count = @state.count + 1 var @input = @payload.text ``` Dynamic imports are labeled `src:dynamic` and marked untrusted. File-based execution with state management. ```typescript const result = await execute('./agent.mld', payload, { state: { conversationId: '123', messages: [...] }, timeout: 30000 }); for (const write of result.stateWrites) { await updateState(write.path, write.value); } ``` Features: - In-memory AST caching (mtime-based invalidation) - State hydration via `@state` module - Payload injection via `@payload` - State writes via `state://` protocol Static analysis without execution. ```typescript const analysis = await analyzeModule('./tools.mld'); if (!analysis.valid) { console.error('Errors:', analysis.errors); } const tools = analysis.executables .filter(e => analysis.exports.includes(e.name)); ``` Use cases: MCP proxy, module validation, IDE/LSP, security auditing. The mlld SDK enables programmatic execution of mlld scripts from application code. **Entry points:** - `processMlld(script)` - Simple: script in, string out - `interpret(script, opts)` - Full control with execution modes - `execute(filepath, payload, opts)` - File-based with state management - `analyzeModule(filepath)` - Static analysis without execution ```typescript import { processMlld } from 'mlld'; const output = await processMlld(` var @name = "World" show \`Hello @name!\` `); // output: "Hello, World!" ``` With file context for imports: ```typescript const output = await processMlld(script, { filePath: './scripts/my-script.mld' }); ``` Four modes control what `interpret()` returns: **document** (default): Plain text output ```typescript const output = await processMlld(script); ``` **structured**: Output + effects + exports + metrics ```typescript const result = await interpret(script, { mode: 'structured' }); console.log(result.output); console.log(result.effects); console.log(result.stateWrites); ``` **stream**: Real-time event consumption ```typescript const handle = interpret(script, { mode: 'stream' }); handle.on('stream:chunk', e => process.stdout.write(e.text)); handle.on('effect', e => console.log('Effect:', e.type)); await handle.done(); ``` **debug**: Full execution trace ```typescript const result = await interpret(script, { mode: 'debug' }); console.log(result.ast); console.log(result.trace); ``` Inject runtime data without filesystem I/O. Enables multi-tenant apps with per-user context. ```typescript const result = await processMlld(template, { dynamicModules: { '@state': { count: 0, messages: [] }, '@payload': { text: userInput, userId: session.id } } }); ``` In your script: ```mlld import { @count, @messages } from @state import { @text, @userId } from @payload var @newCount = @count + 1 show `User @userId said: @text` ``` Dynamic modules are auto-labeled `src:dynamic` for security tracking. Track state changes via `state://` protocol instead of filesystem writes. ```mlld var @count = @state.count + 1 output @count to "state://count" var @prefs = { theme: "dark" } output @prefs to "state://preferences" ``` Application handles persistence: ```typescript const result = await execute('./agent.mld', payload, { state: { count: 0 } }); for (const write of result.stateWrites) { await database.setState(write.path, write.value); } ``` `execute()` provides file-based execution with caching and state. ```typescript const result = await execute('./agent.mld', { text: 'user input', userId: '123' }, // payload { state: { count: 0, messages: [] }, timeout: 30000 } ); console.log(result.output); console.log(result.stateWrites); console.log(result.metrics); ``` Features: - AST caching with mtime invalidation - `@payload` and `@state` auto-injected - Timeout and cancellation support `analyzeModule()` extracts metadata without execution. ```typescript const analysis = await analyzeModule('./tools.mld'); if (!analysis.valid) { console.error('Parse errors:', analysis.errors); return; } // Discover exported functions const tools = analysis.executables .filter(e => analysis.exports.includes(e.name)); // Check capabilities console.log('Needs:', analysis.needs); console.log('Guards:', analysis.guards); ``` Use cases: MCP proxy tool discovery, module validation, IDE/LSP, security auditing. Thin wrappers exist for Go, Python, and Rust. These call the mlld CLI and provide idiomatic APIs. **Go:** ```go client := mlld.New() output, _ := client.Process(`var @x = 1 show @x`, nil) ``` **Python:** ```python from mlld import Client client = Client() output = client.process('var @x = 1\nshow @x') ``` **Rust:** ```rust let client = Client::new(); let output = client.process("var @x = 1\nshow @x", None)?; ``` All wrappers provide: `process()`, `execute()`, `analyze()` All require: Node.js + mlld CLI at runtime Variables always need `@` prefix. ```mlld >> Wrong var greeting = "Hello" >> Correct var @greeting = "Hello" ``` Strict mode (.mld) uses bare directives. Markdown mode (.mld.md) uses slashes. ```mlld >> Wrong (in .mld file) /var @x = 1 /show @x >> Correct (in .mld file) var @x = 1 show @x ``` Directives don't use `@` prefix. ```mlld >> Wrong @run cmd {echo "hello"} var @result = @run cmd {echo "hello"} >> Correct run cmd {echo "hello"} var @result = cmd {echo "hello"} ``` Use `@var` not `${var}`. mlld is not JavaScript. ```mlld >> Wrong var @msg = "Hello ${name}" show `Result: ${count}` >> Correct var @msg = "Hello @name" show `Result: @count` ``` In strict mode, plain text is an error. Use `show` or templates. ```mlld >> Wrong (strict mode) Hello @name! >> Correct show `Hello @name!` ``` Angle brackets load content; quotes are literal strings. ```mlld var @content = >> loads file contents var @path = "README.md" >> literal string ``` `` is plain text. `` (has `.`) is a file ref. ```mlld >> These are plain text (no . / * @) >> These are file references <@base/config.json> ``` Don't call mlld functions inside shell commands. ```mlld >> Wrong run cmd { RESULT=$(@helper("x")) echo $RESULT } >> Correct var @r = @helper("x") run @r | { cat } ``` Template collections need parameters and directories. ```mlld >> Wrong import { @tpl } from "./file.att" >> single file import templates from "./agents" as @agents >> missing params >> Correct exe @tpl(x) = template "./file.att" >> single file import templates from "./agents" as @agents(message, context) ``` Parallel blocks can't write to outer scope. Use `let`. ```mlld >> Wrong var @total = 0 for parallel @x in @items [ var @total += 1 >> outer scope write blocked ] >> Correct exe @countItems(items) = [ let @results = for parallel @x in @items => 1 => @results.length ] ``` mlld has no `return`. Use `=> value` in blocks. ```mlld >> Wrong exe @calc(x) = [ let @result = @x * 2 return @result ] >> Correct exe @calc(x) = [ let @result = @x * 2 => @result ] ``` Commands always need braces. ```mlld >> Wrong run cmd echo "hello" >> Correct run cmd {echo "hello"} ``` Move heavy logic to helpers or modules. Keep orchestration simple. ```mlld >> Wrong (too much logic inline) var @result = for @item in @items => when first [ @item.type == "a" && @item.status == "active" => [ let @x = @item.value * 2 let @y = @transform(@x) let @z = @validate(@y) => when [ @z.ok => @z.value * => null ] ] * => null ] >> Correct (extract to helper) exe @processItem(item) = [ let @x = @item.value * 2 let @y = @transform(@x) let @z = @validate(@y) => when [ @z.ok => @z.value * => null ] ] var @result = for @item in @items when @item.type == "a" => @processItem(@item) ``` Guards protect data and operations. Label sensitive data, define policies. **Labeling data:** ```mlld var secret @apiKey = "sk-12345" var pii @email = "user@example.com" ``` **Defining guards:** ```mlld guard @noShellSecrets before secret = when [ @mx.op.type == "run" => deny "Secrets blocked from shell" * => allow ] run cmd { echo @apiKey } >> Blocked by guard ``` **Guard syntax:** ``` guard [@name] TIMING LABEL = when [...] ``` - `TIMING`: `before`, `after`, or `always` - Shorthand: `for` equals `before` Validate or transform input before operations. ```mlld guard @sanitize before untrusted = when [ * => allow @input.trim().slice(0, 100) ] ``` Validate output after operations. ```mlld guard @validateJson after op:exe = when [ @isValidJson(@output) => allow * => deny "Invalid JSON" ] ``` Transform data during guard evaluation. ```mlld guard @redact before secret = when [ @mx.op.type == "show" => allow @redact(@input) * => allow ] ``` Handle denied operations gracefully. ```mlld exe @handler(value) = when [ denied => `Blocked: @mx.guard.reason` * => @value ] ``` System-assigned labels for tracking: | Label | Applied To | |-------|------------| | `src:exec` | Results from `/run` and `/exe` | | `src:file` | File loads | | `src:dynamic` | Dynamic module imports | | `dir:/path` | File directories (all parents) | **Example directory guards:** ```mlld guard before op:run = when [ @input.any.mx.taint.includes('dir:/tmp/uploads') => deny "Cannot execute uploaded files" * => allow ] ``` Labels flow through operations: - Method calls: `@secret.trim()` preserves labels - Templates: interpolated values carry labels - Field access: `@user.email` inherits from `@user` - Iterators: each item inherits collection labels - Pipelines: labels flow through stages Multiple guards can apply. Resolution order: 1. All applicable guards run (file top-to-bottom) 2. `deny` takes precedence over all 3. `retry` next 4. `allow @value` (transformed) 5. `allow` (unchanged) Guards are non-reentrant (won't trigger on their own operations). Declare required capabilities in modules. ```mlld --- name: my-tool --- needs { js: [] sh } ``` Capabilities: `js`, `sh`, `cmd`, `node`, `python`, `network`, `filesystem` Where interpolation applies: | Context | Syntax | Example | Notes | |---------|--------|---------|-------| | Backticks | `@var` | `` `Hello @name` `` | Primary template | | `::...::` | `@var` | `::Use `cmd` for @name::` | Backticks in text | | Commands `{...}` | `@var` | `{echo "@msg"}` | Interpolates | | Double quotes | `@var` | `"Hi @name"` | Interpolates | | Single quotes | literal | `'Hi @name'` | No interpolation | | Directive level | `@var` | `show @greeting` | Direct reference | | Directive | Purpose | Example | |-----------|---------|---------| | `var` | Create variable | `var @x = 1` | | `show` | Output content | `show @message` | | `run` | Execute command | `run cmd {echo hi}` | | `exe` | Define executable | `exe @f(x) = ...` | | `when` | Conditional | `when @x => show "yes"` | | `for` | Iteration | `for @x in @arr => ...` | | `import` | Import module | `import { @f } from @m/m` | | `export` | Export items | `export { @f, @g }` | | `output` | Write to file | `output @x to "f.txt"` | | `log` | Output to stdout | `log @message` | | `append` | Append to file | `append @x to "f.jsonl"` | | `guard` | Security policy | `guard before secret = ...` | | `stream` | Stream output | `stream @f()` | | `needs` | Declare capabilities | `needs { js: [] }` | | Specifier | Use Case | Features | |-----------|----------|----------| | `cmd` | Simple commands | Pipes only, safe | | `sh` | Shell scripts | `&&`, `\|\|`, multi-line | | `js` | JavaScript | Full JS, return values | | `node` | Node.js | npm packages available | | `python` | Python | Python code | | `prose:@cfg` | LLM skill | Requires config, interpreter-agnostic | | Source | Interpolation | Example | |--------|---------------|---------| | `prose:@cfg { inline }` | Yes (`@var`) | `prose:@cfg { analyze @data }` | | `"file.prose"` | No | Raw prose content | | `"file.prose.att"` | Yes (`@var`) | ATT-style interpolation | | `"file.prose.mtt"` | Yes (`{{var}}`) | MTT-style interpolation | | Transform | Purpose | |-----------|---------| | `@json` | Parse JSON (loose mode) | | `@json.strict` | Parse strict JSON | | `@json.loose` | Parse JSON5 | | `@json.llm` | Extract JSON from LLM response | | `@xml` | Format as XML | | `@csv` | Format as CSV | | `@md` | Format as Markdown | **Comparison:** `<`, `>`, `<=`, `>=`, `==`, `!=` **Logical:** `&&`, `||`, `!` **Ternary:** `@cond ? @trueVal : @falseVal` **Precedence:** `!` → comparison → `&&` → `||` → `?:` | Syntax | Result | |--------|--------| | `@arr[0:3]` | First 3 elements | | `@arr[-2:]` | Last 2 elements | | `@arr[:-1]` | All except last | | `@arr[2:]` | From index 2 to end | | Field | Description | |-------|-------------| | `.mx.content` | File content (default) | | `.mx.filename` | Filename only | | `.mx.relative` | Relative path | | `.mx.absolute` | Absolute path | | `.mx.tokens` | Token count estimate | | `.mx.fm` | Frontmatter object | | `.mx.key` | Object key (in iteration) | | `.mx.keys` | All keys (for objects) | | Extension | Mode | Behavior | |-----------|------|----------| | `.mld` | Strict | Bare directives, text errors | | `.mld.md` | Markdown | Slash prefix, text as content | | `.att` | Template | `@var` interpolation | | `.mtt` | Template | `{{var}}` interpolation (escape hatch) | | `.prose` | Prose | No interpolation, raw content | | `.prose.att` | Prose | `@var` interpolation | | `.prose.mtt` | Prose | `{{var}}` interpolation | When standard templates cause parsing issues (e.g., XML tags with `@` in attributes trigger file load detection), use `:::...:::` or `.mtt` files: ```mlld >> Problem: triggers file load detection var @doc = :::: >> fails - @ in angle brackets >> Solution: triple-colon uses {{var}} interpolation, ignores @ and <> var @doc = :::::: >> works >> Or use .mtt external template exe @header(version) = template "./header.mtt" ``` Trade-offs: `:::...:::` only supports `{{var}}` interpolation - no `@var`, ``, `@exe()`, pipes, or loops. Use only when needed. | Variable | Purpose | |----------|---------| | `MLLD_PARALLEL_LIMIT` | Max concurrent operations (default 4) | | `MLLD_NO_STREAM` | Disable streaming output | | `MLLD_DEBUG` | Enable debug output | | `MLLD_BASH_HEREDOC` | Use heredocs for large vars | - Documentation: https://mlld.ai/docs - Examples: https://github.com/mlld-lang/mlld/tree/main/tests/cases/valid/feat - Source: https://github.com/mlld-lang/mlld This cookbook shows real-world mlld patterns through annotated examples. Each recipe demonstrates multiple features working together. All examples use strict mode (bare directives). 1. LLM Library - Clean utility module for calling Claude models 2. Gate Pattern - Validation/filtering with structured returns 3. Agent Definition - Configuration module with frontmatter and templates 4. Router - Complex decision logic with scoring 5. Orchestrator - Parallel execution with routing and gating 6. Codebase Audit - Parallel file review using Claude A clean utility module for calling Claude models. Demonstrates: - Simple executable definitions - when-first for conditional logic - Pipeline + cmd with working directory - Clean exports ```mlld >> Claude model primitives >> Pure invocation - no agent/variant logic >> Model-specific helpers (no tools for pure text tasks) exe @haiku(prompt) = @prompt | cmd { claude -p --model haiku --tools "" } exe @sonnet(prompt) = @prompt | cmd { claude -p --model sonnet --tools "" } exe @opus(prompt) = @prompt | cmd { claude -p --model opus --tools "" } >> Generic invocation with working directory and tools >> - tools="" => disable all tools (--tools "") >> - tools="Read,Grep,..." => specific tools (--allowedTools "...") >> - tools=null/omitted => use Claude defaults (no flag) exe @claude(prompt, model, dir, tools) = when first [ @tools == "" => @prompt | cmd:@dir { claude -p --model @model --tools "" } @tools => @prompt | cmd:@dir { claude -p --model @model --allowedTools "@tools" } * => @prompt | cmd:@dir { claude -p --model @model } ] >> With system prompt (appends to preserve tool guidance) exe @claudeWithSystem(prompt, system, model, dir, tools) = when first [ @tools == "" => @prompt | cmd:@dir { claude -p --model @model --append-system-prompt "@system" --tools "" } @tools => @prompt | cmd:@dir { claude -p --model @model --append-system-prompt "@system" --allowedTools "@tools" } * => @prompt | cmd:@dir { claude -p --model @model --append-system-prompt "@system" } ] export { @haiku, @sonnet, @opus, @claude, @claudeWithSystem } ``` **Key patterns:** - `@prompt | cmd { ... }` - Pipeline input to command via stdin - `cmd:@dir { ... }` - Execute in specific working directory - `when first [ ... ]` - Switch-style matching for argument handling - Clean function signature with sensible defaults A gate validates or filters before proceeding. Demonstrates: - Block syntax with let and return - Nested function calls - Method chaining - Structured returns ```mlld >> Substance Gate >> Filters responses that just acknowledge without adding value >> Only applies to optional responses - required ones always pass import { @haiku } from "@lib/claude.mld" >> Build the evaluation prompt exe @prompt(res) = ` Does this response add value to the conversation, or does it just acknowledge/decline? Response to evaluate: --- @res --- Answer with just "yes" (adds value) or "no" (just acknowledging/declining). ` >> Check if response has substantive content exe @hasSubstance(response) = [ let @result = @haiku(@prompt(@response)) => @result.trim().toLowerCase().startsWith("yes") ] >> Gate entry point >> Returns: { pass: bool, reason?: string } exe @gate(response, instruction, message) = [ >> Required responses always pass let @isOptional = @instruction.promptKey == "optional" => when first [ !@isOptional => { pass: true } @hasSubstance(@response) => { pass: true } * => { pass: false, reason: "Response lacks substance" } ] ] export { @gate } ``` **Key patterns:** - `exe @f(x) = [ ... ]` - Block syntax for multi-statement bodies - `let @var = ...` - Block-scoped variable - `=> value` - Return value from block - `@result.trim().toLowerCase().startsWith(...)` - Method chaining - `@haiku(@prompt(@response))` - Nested function calls - `{ pass: true, reason: "..." }` - Structured return objects An agent configuration module. Demonstrates: - Frontmatter with @fm access - Template executables loading external files - Object literals with function references - Directory-relative imports - Clean export structure ```mlld --- id: support-agent name: Support Agent --- >> Import shared variant configurations import { @standard as @variants } from "@shared/agent-variants.mld" >> Agent metadata using frontmatter var @meta = { id: @fm.id, name: @fm.name, workDir: "/path/to/project", defaultVariant: "readonly", variants: @variants } >> System prompt template (takes context parameter) exe @systemPrompt(teammates) = template "./prompts/system.att" >> Routing description for quick classification var @tldr = <./prompts/routing-tldr.att> >> Prompt templates (exported as callable functions) exe @primaryPrompt(msg, ctx) = template "./prompts/primary.att" exe @optionalPrompt(msg, ctx) = template "./prompts/optional.att" >> Prompts as object for dynamic access var @prompts = { primary: @primaryPrompt, optional: @optionalPrompt } >> Context assembly import { @assemble } from "./context/assemble.mld" var @ctx = { assemble: @assemble } export { @meta, @tldr, @prompts, @ctx, @systemPrompt } ``` **Key patterns:** - `@fm.id`, `@fm.name` - Access frontmatter fields - `exe @f(x) = template "./file.att"` - External template as executable - `var @tldr = <./prompts/file.att>` - Load file content directly - `{ primary: @primaryPrompt }` - Functions as object values - Relative imports with `"./path"` syntax A router that scores and routes messages to agents. Demonstrates: - Complex scoring logic with block syntax - for with inline filtering - Object spread in results - when-first for classification - Nested executables for clean organization ```mlld >> Response-Required Router >> Scores agents, applies thresholds, returns execution-ready instructions import { @haiku } from "@lib/claude.mld" >> Policy thresholds var @THRESHOLD_REQUIRED = 0.7 var @THRESHOLD_OPTIONAL = 0.3 >> Build routing context from agent descriptions exe @buildRoutingContext(agentRegistry) = [ let @lines = for @agent in @agentRegistry => `- **@agent.mx.key**: @agent.tldr` => @lines.join("\n") ] >> Calculate reply pressure based on message mentions >> - Own message: 0 (never respond to yourself) >> - Starts with @all: 0.8 (everyone should respond) >> - First in mention list: 1.0 >> - Others in mention list: 0.6 >> - Mentioned elsewhere: 0.2 >> - Not mentioned: 0 exe @getReplyPressure(agent, msg) = [ let @isOwnMessage = @msg.from_agent == @agent let @startsWithAll = @msg.body.trim().startsWith("@all") let @hasAll = @msg.mentions.indexOf("all") >= 0 let @idx = @msg.mentions.indexOf(@agent) let @startsAt = @msg.body.trim().startsWith("@") => when first [ @isOwnMessage => 0 @startsWithAll => 0.8 @hasAll => 0.2 @idx == -1 => 0 @startsAt && @idx == 0 => 1.0 @startsAt && @idx > 0 => 0.6 * => 0.2 ] ] >> Evaluate a single agent (scoring only) exe @evaluateAgent(agentId, msg, turnPressure) = [ let @replyP = @getReplyPressure(@agentId, @msg) let @turnP = @turnPressure[@agentId] >> Turn pressure only applies if agent has some reply pressure let @effectiveTurnP = when first [ @replyP > 0 => @turnP * => 0 ] let @total = @replyP + @effectiveTurnP => { agent: @agentId, replyPressure: @replyP, turnPressure: @effectiveTurnP, responseRequired: @total } ] >> Classify evaluation into execution instructions exe @classifyForExecution(eval) = [ let @score = @eval.responseRequired => when first [ @score >= @THRESHOLD_REQUIRED => { ...@eval, promptKey: "primary", variant: null } @score >= @THRESHOLD_OPTIONAL => { ...@eval, promptKey: "optional", variant: "readonly" } * => null ] ] >> Main router entry point exe @router(message, agentRegistry, turnPressure) = [ >> Build routing context let @routingContext = @buildRoutingContext(@agentRegistry) let @agentKeys = @agentRegistry.mx.keys >> Initial deterministic evaluation let @evaluations = for @agentId in @agentKeys [@evaluateAgent(@agentId, @message, @turnPressure)] >> Apply thresholds and classify let @classified = for @eval in @evaluations => @classifyForExecution(@eval) >> Filter results let @toInvoke = for @c in @classified when @c != null => @c let @skipped = for @eval in @evaluations when @eval.responseRequired < @THRESHOLD_OPTIONAL => @eval => { toInvoke: @toInvoke, skipped: @skipped } ] export { @router } ``` **Key patterns:** - `for @agentId in @agentKeys [...]` - For with block body - `for @c in @classified when @c != null => @c` - For with inline filter - `{ ...@eval, promptKey: "primary" }` - Object spread in result - `@agentRegistry.mx.keys` - Get all keys from object - `@msg.body.trim().startsWith("@")` - Method chaining on fields - Nested when-first in block for decision trees An orchestrator that routes and executes in parallel. Demonstrates: - Multiple imports from different sources - Parallel for blocks - Chained execution (route → invoke → gate → post) - when-first inside parallel iteration ```mlld >> Orchestrator: executes what router tells it import { @message, @agentIds, @turnPressure } from @payload import { @invoke } from "@lib/invoke.mld" import { @stripToolMarkup } from "@lib/strip-tool-markup.mld" import { @router as @responseRequired } from "@routers/response-required.router.mld" import { @gate as @substanceGate } from "@gates/substance.gate.mld" >> Load agents from directory import "@agents" as @agentRegistry >> Run router to get instructions var @routing = @responseRequired(@message, @agentRegistry, @turnPressure) >> Invoke a single agent based on routing instruction exe @invokeAgent(instruction, msg) = [ let @agent = @agentRegistry[@instruction.agent] let @msgCtx = @agent.ctx.assemble(@msg) let @prompt = @agent.prompts[@instruction.promptKey](@msg, @msgCtx) let @rawResponse = @invoke(@prompt, @agent, @agentRegistry, @instruction.variant) let @response = @stripToolMarkup(@rawResponse) => { agent: @instruction.agent, response: @response } ] >> Route → Invoke → Gate → Post >> Execute up to 3 agents concurrently for parallel(3) @instruction in @routing.toInvoke [ let @result = @invokeAgent(@instruction, @message) let @check = @substanceGate(@result.response, @instruction, @message) => when first [ @check.pass => run cmd { mm post --as @result.agent "@result.response" } * => null ] ] export { @routing } ``` **Key patterns:** - `import "@agents" as @agentRegistry` - Directory import - `import { @router as @responseRequired }` - Aliased import - `for parallel(3) @instruction in [...] [...]` - Parallel for with block - `@agentRegistry[@instruction.agent]` - Dynamic field access - `@agent.prompts[@instruction.promptKey](@msg, @ctx)` - Dynamic function call - Chained processing: route → invoke → gate → post A codebase audit tool using Claude for parallel file review. Demonstrates: - Glob patterns for file loading - File metadata access - Claude as a function via pipe + cmd - Parallel for loops with structured returns ```mlld >> Claude model helper - haiku for fast, cheap reviews exe @haiku(prompt) = @prompt | cmd { claude -p --model haiku --tools "" } >> Build review prompt exe @buildPrompt(filename, content) = ` Review this code for issues: File: @filename --- @content --- List 2-3 issues or "LGTM". Be concise (max 3 lines). ` >> Load TypeScript files var @allFiles = >> Review function exe @reviewFile(file) = [ let @prompt = @buildPrompt(@file.mx.relative, @file) let @review = @haiku(@prompt) let @trimmed = @review.trim() => { file: @file.mx.relative, review: @trimmed } ] >> Parallel review - up to 5 concurrent var @reviews = for parallel(5) @f in @allFiles => @reviewFile(@f) >> Output results for @r in @reviews [ show `## @r.file` show @r.review show "" ] ``` **Key patterns:** - `` - Glob pattern loads all matching files - `@file.mx.relative` - Access file metadata (relative path) - `@prompt | cmd { claude -p }` - Pipe to Claude CLI via stdin - `for parallel(5)` - Process up to 5 files concurrently These six recipes cover the most common mlld patterns: | Recipe | Features Demonstrated | |--------|----------------------| | LLM Library | Pipelines, when-first, cmd:dir, exports | | Gate | Blocks, let/return, method chaining, structured returns | | Agent Definition | Frontmatter, templates, function objects | | Router | Complex scoring, for-filter, object spread, nested when | | Orchestrator | Parallel for, directory imports, dynamic access | | Codebase Audit | Globs, file metadata, Claude-as-function, parallel review | For reference documentation, see the other llms-*.txt modules.