The declarative language for MARIA OS Universe workflows
Last synced: Feb 19, 2026
A .uni file consists of import declarations followed by one or more field declarations.
import { <name>, ... } from "<namespace>"; field <Name> [extends <Parent>] { ... }
Fields are the primary organizational unit, analogous to a class/module:
field MyUniverse { topic = "description of what this universe does"; ndc = 42; config { outputDir = "deliverables"; parallel = true; } execute() { // phases and commands go here } }
Key-value pairs at the top of a field body:
topic = "Build a music composition"; ndc = 7;
config { key = "value"; number = 42; flag = true; }
Named reusable subroutines within a field. Methods allow you to extract repeated logic (e.g. solving each subject, generating each chapter) into a single definition and call it multiple times with different arguments.
Definition:
method solveSubject(subjectName: String) { const a = research("Study " + subjectName, target: "deliverables/" + subjectName + "/notes.md", apply: true) check [exists("deliverables/" + subjectName + "/notes.md")] fix (maxAttempts: 3, cmd: "research"); const b = chat("Solve problems for " + subjectName, target: "deliverables/" + subjectName + "/answers.md", apply: true) check [exists("deliverables/" + subjectName + "/answers.md")] fix (maxAttempts: 3, cmd: "chat"); } method validate(path: String) { assert([exists(path)]); }
Calling methods from execute/phase blocks:
phase solve { this.solveSubject("Math"); this.solveSubject("Science"); this.solveSubject("History"); }
IMPORTANT: Methods are called with this.methodName(args). They are
executed inline — each call runs the method's steps sequentially.
Methods do NOT need to be imported. They are defined in the same field.
The main entry point for the field:
execute() { // statements }
Group related steps into named phases:
phase research { // statements } phase build { // statements } phase validate { // statements }
Invoke slash commands as function calls. Commands MUST be imported:
research("query about topic"); code("implement feature X", target: "src/main.ts"); chat("generate context document for project planning", target: "contexts/context.md", apply: true); image("a serene mountain landscape at sunset", size: "1024x1024", format: "png", path: "deliverables/image.png"); music("compose a lullaby in C major", type: "melody", bpm: "72", key: "C"); slides("create a 10-slide pitch deck", purpose: "pitch", slides: "10", out: "deliverables", pptx: true, pdf: true); deps("npm-global", pkg: "md-to-pdf", apply: true);
When a step needs to generate/save text content (documents, plans, summaries, context files),
prefer chat with a target parameter over code. The executor auto-saves chat output
to the target file. Use code only when actual source-code modifications are needed.
Some commands write output to dynamic, timestamped directories — NOT fixed file paths. You MUST use glob patterns in when/check/assert for these commands.
| Command | Default output base | Actual output pattern | Example |
|---|---|---|---|
| /slides | slides/ (or out:) | {out}/{YYYYMMDD_HHMMSS}_{slug}/export/{title}-{ts}.pptx | deliverables/20250213_143022_my-pitch/export/deck-143022.pptx |
| /music | music/ | music/{YYYYMMDD_HHMMSS}_{slug}/score.wav | music/20250213_143022_lullaby/score.wav |
| /manga | manga/ (or out:) | {out}/{YYYYMMDD_HHMMSS}_{slug}/images/{title}-{ts}.png | deliverables/20250213_143022_my-manga/images/manga-143022-01.png |
| /docs | docs/ (or out:) | {out}/{YYYYMMDD_HHMMSS}_{slug}/export/{title}-{ts}.docx | deliverables/20250213_143022_report/export/report-143022.docx |
| /spreadsheet | spreadsheet/ (or out:) | {out}/{YYYYMMDD_HHMMSS}_{slug}/export/{title}.xlsx | deliverables/20250213_143022_budget/export/budget.xlsx |
| /film | film/ (or out:) | {out}/{YYYYMMDD_HHMMSS}_{slug}/{title}-{ts}.mp4 | deliverables/20250213_143022_trailer/trailer-143022.mp4 |
| /image | outputs/images/ | outputs/images/{slug}-{YYYYMMDD_HHMMSS}.png | outputs/images/landscape-20250213_143022.png |
| /chat | (no file output) | Executor auto-saves text to the target: path | deliverables/plan.md |
| /research | (no file output) | Executor auto-saves text to the target: path | deliverables/research.md |
| /code | (no file output) | Writes to target: path directly | src/main.ts |
Rules for dynamic-output commands (/slides, /music, /manga, /docs, /spreadsheet, /film, /image without --path):
when (missing("deliverables/**/*.pptx")) NOT when (missing("deliverables/presentation.pptx"))check [exists("deliverables/**/*.pptx")]assert([exists("music/**/*.wav")])path: parameter is the exception — it saves to the exact path specified.out: "deliverables" to keep output under deliverables/. This avoids deep nesting from default base directories. For /slides, also add pdf: true to generate PPTX+PDF in one step.Rules for text commands (/chat, /research, /code):
target: parameter: these are auto-saved to that exact path.target: with binary extensions (.pptx, .pdf, .wav, .mp3, .mid, .png) is silently skipped by auto-save.
Binary artifacts are written by the commands themselves, not via target. So do NOT rely on target
to create binary files — the check will never pass, causing an infinite retry loop.target: path is relative to the run directory.const result = research("query"); let count = 0; // Variable reassignment (let variables only) count = count + 1; count += 1; count -= 1; count *= 2; count /= 2; count %= 3;
Assignment operators: =, +=, -=, *=, /=, %=
Only let variables can be reassigned; const variables are immutable.
Every command should have check (verification) and fix (repair) attached:
const plan = research("find best libraries for audio synthesis") check [exists("deliverables/plan.md")] fix (maxAttempts: 3, cmd: "code");
Check conditions use gate functions: exists(), missing(), contains(), env(). Fix specifies how to repair: maxAttempts (number) and cmd (command to run).
Skip work that is already done:
// For text commands (chat/code/research) — use fixed paths: when (missing("deliverables/plan.md")) { const plan = chat("Write plan", target: "deliverables/plan.md", apply: true) check [exists("deliverables/plan.md")] fix (maxAttempts: 2, cmd: "chat"); } // For binary-output commands (slides/music/image) — use glob patterns: when (missing("music/**/*.wav")) { const song = music("compose a track", type: "bgm") check [exists("music/**/*.wav")] fix (maxAttempts: 3, cmd: "music"); }
if (env("SKIP_TESTS") == "1") { // skip } else { // run tests }
for (item in ["a", "b", "c"]) { code("process item", target: item); }
let attempts = 0; while (attempts < 3) { // retry logic attempts += 1; }
Run independent branches in parallel:
async { branch research_branch { research("topic A"); } branch build_branch { code("build component B"); } }
assert([ exists("deliverables/output.wav"), exists("deliverables/manifest.json") ]);
Glob patterns use * (any filename chars) and ** (any nested dirs). Use them for commands that generate dynamic timestamped output (slides, music, image without --path).
Users can attach files or text when inflating a universe via --in.
The executor stores user-provided inputs in the run directory:
contexts/user-input.md — full text of user inputs (with file headers for multi-file)You can use exists("contexts/user-input.md") to check whether the user provided inputs,
and branch accordingly:
if (exists("contexts/user-input.md")) { // User provided input files — use them directly const analysis = chat("Read and analyze the user-provided data in contexts/user-input.md. ...", target: "deliverables/analysis.md", apply: true) check [exists("deliverables/analysis.md")] fix (maxAttempts: 2, cmd: "chat"); } else { // No input provided — fetch/acquire the data ourselves const fetched = research("find and fetch the required data from official sources", target: "deliverables/fetched-data.md", apply: true) check [exists("deliverables/fetched-data.md")] fix (maxAttempts: 3, cmd: "research"); }
When the topic description mentions "attached files", "provided input", or similar,
you SHOULD generate an if/else branch using exists("contexts/user-input.md")
so the universe works both with and without user-provided inputs.
Note: LLM commands (/chat, /research, /code) automatically have access to the context store, which includes user inputs. You can reference them in prompts as "the user-provided input" or "the data in contexts/user-input.md" — the LLM agent will find and use the content.
Statements (var decls, expression statements) end with ";". Block statements (when, if, for, while, async, phase) do NOT require trailing ";".
// single-line comment /* multi-line comment */
// --- Example: Music composition universe --- // NOTE: /music outputs to music/{timestamp}_{slug}/ with dynamic filenames. // Use glob patterns (e.g. "music/**/*.wav") for when/check/assert. import { research, music, chat, deps } from "maria/commands"; import { flow } from "maria/agents"; import { exists, missing } from "maria/gates"; field MusicComposition { topic = "Compose an original lullaby in C major with piano and strings"; ndc = 12; config { genre = "classical"; format = "wav"; } execute() { phase research { when (missing("deliverables/plan.md")) { const plan = chat("Research best practices for lullaby composition in C major, piano + strings arrangement. Write a detailed plan with structure, instrumentation, and tempo guidelines.", target: "deliverables/plan.md", apply: true ) check [exists("deliverables/plan.md")] fix (maxAttempts: 3, cmd: "chat"); } } phase compose { // /music writes to music/{timestamp}_{slug}/score.wav (dynamic path) // Use glob to check for any .wav under music/ when (missing("music/**/*.wav")) { const composition = music("compose lullaby in C major, 3 minutes, piano melody with string accompaniment", type: "bgm", genre: "classical" ) check [exists("music/**/*.wav")] fix (maxAttempts: 3, cmd: "music"); } } phase quality_gate { // Quality gate: iterate until review passes while (missing("deliverables/quality-passed.txt")) { const review = chat("Review the generated music under music/ for audio quality. Check tempo, key, instrumentation match requirements. If quality is acceptable write PASS, otherwise describe issues.", target: "deliverables/quality-review.md", apply: true ) check [exists("deliverables/quality-review.md")] fix (maxAttempts: 2, cmd: "chat"); } // Conditional: generate metadata only when requested if (env("INCLUDE_METADATA") == "1") { const meta = chat("Generate metadata JSON for the composition including title, key, tempo, duration, instruments", target: "deliverables/metadata.json", apply: true ) check [exists("deliverables/metadata.json")] fix (maxAttempts: 2, cmd: "chat"); } } phase validate { assert([ exists("deliverables/plan.md"), exists("music/**/*.wav") ]); } } }