Skip to main content
Every token an agent spends reading CLI output is a token it can’t spend reasoning. incur defaults to TOON (Terse Object Oriented Notation)—a format that’s as readable as YAML but with no quoting, no braces, and no redundant syntax.

What is TOON?

TOON is a data serialization format designed for LLMs and humans. It uses up to 60% fewer tokens compared to JSON while remaining easy to parse.

Comparison

JSON output (verbose)
$ my-cli hikes --location Boulder --season spring_2025
{
  "context": {
    "task": "Our favorite hikes together",
    "location": "Boulder",
    "season": "spring_2025"
  },
  "friends": ["ana", "luis", "sam"],
  "hikes": [
    {
      "id": 1,
      "name": "Blue Lake Trail",
      "distanceKm": 7.5,
      "elevationGain": 320,
      "companion": "ana",
      "wasSunny": true
    },
    {
      "id": 2,
      "name": "Ridge Overlook",
      "distanceKm": 9.2,
      "elevationGain": 540,
      "companion": "luis",
      "wasSunny": false
    },
    {
      "id": 3,
      "name": "Wildflower Loop",
      "distanceKm": 5.1,
      "elevationGain": 180,
      "companion": "sam",
      "wasSunny": true
    }
  ]
}
TOON output (concise)
$ my-cli hikes --location Boulder --season spring_2025
context:
  task: Our favorite hikes together
  location: Boulder
  season: spring_2025
friends[3]: ana,luis,sam
hikes[3]{id,name,distanceKm,elevationGain,companion,wasSunny}:
  1,Blue Lake Trail,7.5,320,ana,true
  2,Ridge Overlook,9.2,540,luis,false
  3,Wildflower Loop,5.1,180,sam,true

Token Savings

The TOON format eliminates:
  • Quotation marks around keys and strings
  • Braces {} for objects
  • Brackets [] around arrays (replaced with inline headers)
  • Redundant commas between object properties
Result: 40-60% fewer tokens for typical CLI output.

Format Options

incur supports multiple output formats. Switch between them using global options.

Available Formats

FormatDescriptionUse Case
toonTerse Object Oriented Notation (default)Agent consumption, human readability
jsonStandard JSON with 2-space indentationProgrammatic parsing, debugging
yamlYAML formatHuman editing, config files
mdMarkdown tablesDocumentation, reports
jsonlJSON Lines (streaming)Log aggregation, streaming data

Switch Formats

Using --format
$ my-cli status --format json
{
  "clean": true
}

$ my-cli status --format yaml
clean: true

$ my-cli status --format md
| Key   | Value |
|-------|-------|
| clean | true  |
Using --json shorthand
$ my-cli status --json
{
  "clean": true
}

Implementation

TOON output is automatic. Just return data from your command:
import { Cli, z } from 'incur'

Cli.create('my-cli', { description: 'My CLI' })
  .command('status', {
    description: 'Show repo status',
    run() {
      return { clean: true, branch: 'main', ahead: 0 }
    },
  })
  .serve()
Default output (TOON)
$ my-cli status
clean: true
branch: main
ahead: 0
JSON output
$ my-cli status --json
{
  "clean": true,
  "branch": "main",
  "ahead": 0
}

Markdown Tables

The md format automatically renders objects as tables:

Key-Value Tables

Cli.create('my-cli', { description: 'My CLI' })
  .command('config', {
    run() {
      return { name: 'my-app', version: '1.0.0', author: 'Alice' }
    },
  })
  .serve()
$ my-cli config --format md
| Key     | Value   |
|---------|--------|
| name    | my-app  |
| version | 1.0.0   |
| author  | Alice   |

Columnar Tables

Arrays of objects become columnar tables:
Cli.create('my-cli', { description: 'My CLI' })
  .command('users', {
    run() {
      return {
        users: [
          { id: 1, name: 'Alice', role: 'admin' },
          { id: 2, name: 'Bob', role: 'user' },
        ],
      }
    },
  })
  .serve()
$ my-cli users --format md
## users

| id | name  | role  |
|----|-------|-------|
| 1  | Alice | admin |
| 2  | Bob   | user  |

JSONL for Streaming

When streaming data, use --format jsonl for line-delimited JSON:
Cli.create('my-cli', { description: 'My CLI' })
  .command('logs', {
    async *run() {
      yield { timestamp: Date.now(), level: 'info', message: 'Starting' }
      yield { timestamp: Date.now(), level: 'info', message: 'Processing' }
      yield { timestamp: Date.now(), level: 'info', message: 'Done' }
    },
  })
  .serve()
$ my-cli logs --format jsonl
{"type":"chunk","data":{"timestamp":1709856000000,"level":"info","message":"Starting"}}
{"type":"chunk","data":{"timestamp":1709856001000,"level":"info","message":"Processing"}}
{"type":"chunk","data":{"timestamp":1709856002000,"level":"info","message":"Done"}}

Default Format

Set a default format for your CLI:
import { Cli, z } from 'incur'

Cli.create('my-cli', {
  description: 'My CLI',
  format: 'json', // Default to JSON instead of TOON
})
  .command('status', {
    run() {
      return { clean: true }
    },
  })
  .serve()
$ my-cli status
{
  "clean": true
}
Users can still override with --format or --json.

Format Source Code

The formatter implementation is in src/Formatter.ts:
import { format } from 'incur/Formatter'

const data = { name: 'Alice', role: 'admin' }

format(data, 'toon') // → "name: Alice\nrole: admin"
format(data, 'json') // → "{\n  \"name\": \"Alice\",\n  \"role\": \"admin\"\n}"
format(data, 'yaml') // → "name: Alice\nrole: admin\n"
format(data, 'md')   // → "| Key  | Value |..."
Use TOON (the default) for the best token efficiency and agent experience.

Build docs developers (and LLMs) love