Home / Documentation / UNI Language

UNI Language

The declarative language for MARIA OS Universe workflows

Last synced: Feb 19, 2026

.uni Language Specification

Top-level Structure

A .uni file consists of import declarations followed by one or more field declarations.

import { <name>, ... } from "<namespace>";
field <Name> [extends <Parent>] { ... }

Import Namespaces

  • "maria/commands" — slash commands (research, code, music, slides, image, video, pdf, deps, etc.)
  • "maria/agents" — agent roles (flow, cxo, etc.)
  • "maria/gates" — gate functions for when/check (exists, missing, contains, env)

Field Declaration

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
  }
}

Properties

Key-value pairs at the top of a field body:

  topic = "Build a music composition";
  ndc = 7;

Config Block

  config {
    key = "value";
    number = 42;
    flag = true;
  }

Methods

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.

Execute Block

The main entry point for the field:

  execute() {
    // statements
  }

Phases

Group related steps into named phases:

  phase research {
    // statements
  }

  phase build {
    // statements
  }

  phase validate {
    // statements
  }

Command Calls

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.

CRITICAL: Command Output Paths

Some commands write output to dynamic, timestamped directories — NOT fixed file paths. You MUST use glob patterns in when/check/assert for these commands.

CommandDefault output baseActual output patternExample
/slidesslides/ (or out:){out}/{YYYYMMDD_HHMMSS}_{slug}/export/{title}-{ts}.pptxdeliverables/20250213_143022_my-pitch/export/deck-143022.pptx
/musicmusic/music/{YYYYMMDD_HHMMSS}_{slug}/score.wavmusic/20250213_143022_lullaby/score.wav
/mangamanga/ (or out:){out}/{YYYYMMDD_HHMMSS}_{slug}/images/{title}-{ts}.pngdeliverables/20250213_143022_my-manga/images/manga-143022-01.png
/docsdocs/ (or out:){out}/{YYYYMMDD_HHMMSS}_{slug}/export/{title}-{ts}.docxdeliverables/20250213_143022_report/export/report-143022.docx
/spreadsheetspreadsheet/ (or out:){out}/{YYYYMMDD_HHMMSS}_{slug}/export/{title}.xlsxdeliverables/20250213_143022_budget/export/budget.xlsx
/filmfilm/ (or out:){out}/{YYYYMMDD_HHMMSS}_{slug}/{title}-{ts}.mp4deliverables/20250213_143022_trailer/trailer-143022.mp4
/imageoutputs/images/outputs/images/{slug}-{YYYYMMDD_HHMMSS}.pngoutputs/images/landscape-20250213_143022.png
/chat(no file output)Executor auto-saves text to the target: pathdeliverables/plan.md
/research(no file output)Executor auto-saves text to the target: pathdeliverables/research.md
/code(no file output)Writes to target: path directlysrc/main.ts

Rules for dynamic-output commands (/slides, /music, /manga, /docs, /spreadsheet, /film, /image without --path):

  • when gates MUST use glob: when (missing("deliverables/**/*.pptx")) NOT when (missing("deliverables/presentation.pptx"))
  • check conditions MUST use glob: check [exists("deliverables/**/*.pptx")]
  • assert MUST use glob: assert([exists("music/**/*.wav")])
  • NEVER assume a fixed filename for these commands; they generate timestamped names.
  • /image with path: parameter is the exception — it saves to the exact path specified.
  • For /slides, /manga, /docs, /spreadsheet, /film: use 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.
  • IMPORTANT: Output paths must stay short for Windows compatibility (MAX_PATH=256 usable). Slug is capped at 32 chars, basename at 32 chars. Keep total relative output path under 200 chars.

Rules for text commands (/chat, /research, /code):

  • Use fixed paths with 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.
  • The target: path is relative to the run directory.

Variables & Assignment

  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.

Check / Fix (attached to command calls via variable assignment)

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).

When Blocks (idempotency gates)

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 / Else

  if (env("SKIP_TESTS") == "1") {
    // skip
  } else {
    // run tests
  }

For Loops

  for (item in ["a", "b", "c"]) {
    code("process item", target: item);
  }

While Loops

  let attempts = 0;
  while (attempts < 3) {
    // retry logic
    attempts += 1;
  }

Async / Branch (parallelism)

Run independent branches in parallel:

  async {
    branch research_branch {
      research("topic A");
    }
    branch build_branch {
      code("build component B");
    }
  }

Assert (validation)

  assert([
    exists("deliverables/output.wav"),
    exists("deliverables/manifest.json")
  ]);

Expressions

  • Strings: "hello" (double-quoted)
  • Numbers: 42, 3.14
  • Booleans: true, false
  • null
  • Arrays: [1, 2, 3]
  • Records: { key: "value", num: 42 }
  • Member access: obj.prop
  • Binary ops: +, -, *, /, %, ==, !=, <, >, <=, >=, &&, ||
  • Unary ops: !, -
  • Range: 1..10
  • Function calls: name(args, key: value)

Gate Functions (used in when/check/assert)

  • exists(path) — true if file/dir exists. Supports glob patterns: exists("slides/**/*.pptx")
  • missing(path) — true if file/dir does NOT exist. Supports glob patterns: missing("music/**/*.wav")
  • contains(path, text) — true if file contains text
  • env(key) — returns environment variable value

Glob patterns use * (any filename chars) and ** (any nested dirs). Use them for commands that generate dynamic timestamped output (slides, music, image without --path).

User Inputs (--in)

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)
  • Also appended to the context store (available to all LLM commands via context retrieval)

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.

Semicolons

Statements (var decls, expression statements) end with ";". Block statements (when, if, for, while, async, phase) do NOT require trailing ";".

Comments

// single-line comment
/* multi-line comment */

Keywords

importfromfieldextendsconfigexecutemethodphasewhenifelseforwhileasyncbranchconstletreturnbreakcontinuecheckfixassertintruefalsethisnull

Reference Sample

// --- 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")
      ]);
    }
  }
}

Sample .uni Files7 samples