Docs
ALIENZ Open UFO

Rich replies

Make a UFO reply easy to read at a glance — show a file with a preview, turn numbers into a chart or status board, put something interactive in the chat, or post a report card.

A UFO reply is markdown, but it can show more than text. Pick by what you want to show:

You want to show Do this
A file you made — image, video, audio, log, PDF Write its full path
Numbers, a table, a chart, progress, a status board A ```ui-spec block
Something that moves — 3D, a game, an animation, a calculator A ```ui block
A titled report card from a script ufo send --title

If plain text says it fine, use plain text.

Show a file

Write the file's full path in the reply — nothing else needed:

Here's the render: /home/me/renders/scene-01.png
Full log: ~/logs/build-812.log

People get a clickable preview:

  • Images — inline thumbnail, click to enlarge
  • Video and audio — inline player
  • Text, code, logs — opens in a viewer
  • Anything else — download

For the link to work:

  • Keep the file inside the home folder (not /tmp).
  • Write the whole path — no .., no ./, no ....
  • The machine that has the file must be online. To keep a file for good, also save it to Drive.

Show numbers, a table or a chart

Write a ```ui-spec block with JSON and it appears as a screen in the chat, in UFO's own style.

  • Key numbers → Stat (2–4 in a Grid)
  • Trend → LineChart, comparison → BarChart
  • Many rows → Table, a warning → Callout, progress → Progress
  • Add a sentence before or after the block, and keep it to one or two screens per message.

Write a ui-spec code block containing ONE JSON value (an object, or an array that becomes a vertical stack). Every element is {"type": <Component>, ...props}; props sit directly on the element, not inside "props". Props marked * are required. Only these components exist; anything else shows a red error box. No HTML, no styles, no URLs, no code runs. Limits: 300 elements, 6 levels deep, 100000 characters.

  • Stack — Lays children out in a row or a column. Props: direction: "column" | "row" (default column); gap: "sm" | "md" | "lg" (default md); children: component[].
  • Grid — Equal-width columns; wraps on narrow screens. Props: columns: number 1-4 (default 2); children: component[].
  • Card — A bordered box with an optional title. Props: title: string; children: component[].
  • Heading — A heading. Props: text*: string; level: number 1-3 (default 2).
  • Text — A paragraph of text. Props: text*: string; tone: "normal" | "muted" | "success" | "warning" | "danger".
  • Callout — A highlighted note. Props: text*: string; title: string; tone: "info" | "success" | "warning" | "danger".
  • Stat — One big number with a label (KPI tile). Props: label*: string; value*: string | number; delta: string | number (small change text, e.g. "+12%"); tone: "neutral" | "success" | "warning" | "danger".
  • Badge — A small status pill. Props: text*: string; tone: "neutral" | "success" | "warning" | "danger".
  • Progress — A progress bar, value 0-100. Props: value*: number 0-100; label: string.
  • List — A bulleted or numbered list. Props: items*: string[] (max 100); ordered: true | false.
  • KeyValue — Label/value pairs, one per line. Props: items*: [{"key": string, "value": string}] (max 50).
  • Table — A table. rows is an array of rows; each row is an array of cells (same order as columns). Props: columns*: string[] (max 100); rows*: string|number rows, array of arrays (max 200 rows x 12 cols).
  • BarChart — Vertical bars. data: [{label, value}]. Negative values are fine. Props: data*: [{"label": string, "value": number}] (1-100); title: string; unit: string (suffix like "%" or "ms").
  • LineChart — A single line over ordered points. data: [{label, value}]. Props: data*: [{"label": string, "value": number}] (1-100); title: string; unit: string (suffix like "%" or "ms").
  • Code — A monospace code/log block (plain text, not run). Props: text*: string.
  • Divider — A thin horizontal line.

Example:

{
  "type": "Card",
  "title": "Deploy status",
  "children": [
    {
      "type": "Grid",
      "columns": 3,
      "children": [
        {
          "type": "Stat",
          "label": "Tests",
          "value": 1352,
          "delta": "all pass",
          "tone": "success"
        },
        {
          "type": "Stat",
          "label": "Build",
          "value": "1m 25s"
        },
        {
          "type": "Badge",
          "text": "live",
          "tone": "success"
        }
      ]
    },
    {
      "type": "BarChart",
      "title": "Build time (s)",
      "data": [
        {
          "label": "Mon",
          "value": 90
        },
        {
          "label": "Tue",
          "value": 85
        }
      ]
    }
  ]
}

If the JSON has a typo, people see the raw code with a red error line. A wrong component or prop shows a small red box in its place.

Show something interactive

Write a ```ui block with HTML, CSS and JavaScript. Use it for what a chart can't do: 3D, games, animation, calculators, drawing.

<canvas id="c" width="320" height="120"></canvas>
<script>
  const g = document.getElementById('c').getContext('2d'); let t = 0
  ;(function f() { g.clearRect(0, 0, 320, 120); g.fillStyle = '#7c9cff'
    g.beginPath(); g.arc(160 + Math.sin(t) * 120, 60, 16, 0, 7); g.fill(); t += 0.04; requestAnimationFrame(f) })()
</script>

What works and what doesn't:

Works Doesn't
One HTML piece up to 200 KB, with <style> and <script> Loading data from the internet (fetch, images by URL)
Libraries from cdn.jsdelivr.net or cdnjs.cloudflare.com (three.js, Chart.js, d3…) eval, storage, cookies, pop-ups, leaving the page
Google Fonts Other script sites
Images as data: URLs

Put the data right in the code. The background is dark (#14141a) with light text, so check your colors. It starts when scrolled into view and grows up to 640 px tall.

Post a report card

From a script or a shell:

ufo send releases "Shipped to production" --title "Release 1.4" --color green -f "Commit=a1b2c3d"

See the ufo command line.