Home / Documentation / UNI Language

UNI Language

The declarative language for MARIA OS Universe workflows

Last synced: Mar 5, 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.

Persona Bindings (MANDATORY)

Every universe MUST assign agent roles via the persona() function. Personas define the identity, expertise, and behavior of each agent.

  // Assign personas (one or more required):
  const lead = persona("ceo_global");
  const researcher = persona("senior_researcher");
  const builder = persona("full_stack_dev");

persona() takes a persona label (string) and returns a persona binding. The label must match an available persona from the system's persona store or samples. If no persona matches exactly, the system will attempt to auto-install from samples.

RULES:

  • Every universe MUST have at least one persona() call. Universes without persona bindings are invalid.
  • Place persona() calls in the first phase (typically "setup" or "init") BEFORE command calls.
  • Each persona represents a distinct agent role in the team.
  • Use descriptive variable names that reflect the agent's role (e.g. lead, researcher, writer).

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

importfromfieldextendsconfigexecutemethodphasewhenifelseforwhileasyncbranchconstletreturnbreakcontinuecheckfixassertintruefalsethisnullroletrustpermissionspawndespawnescalateforbidpermitobligecapability

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