{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://docspack.dev/cmdspec/schema/v0.1.json",
  "title": "cmdspec 0.1",
  "description": "A machine-readable description of a command-line interface. The specification is https://docspack.dev/cmdspec — this schema is its normative structure.",
  "type": "object",
  "required": [
    "cmdspec",
    "info"
  ],
  "allOf": [
    {
      "$ref": "#/$defs/commandBody"
    }
  ],
  "properties": {
    "$schema": {
      "type": "string"
    },
    "cmdspec": {
      "description": "The version of this specification the document is written against.",
      "type": "string",
      "pattern": "^0\\.1(\\.[0-9]+)?$"
    },
    "info": {
      "$ref": "#/$defs/info"
    },
    "syntax": {
      "$ref": "#/$defs/syntax"
    },
    "environment": {
      "description": "Environment variables the program reads that no option is bound to.",
      "type": "array",
      "items": {
        "$ref": "#/$defs/environmentVariable"
      }
    },
    "files": {
      "description": "Files the program reads or writes on its own account, config files first in the order they are consulted.",
      "type": "array",
      "items": {
        "$ref": "#/$defs/file"
      }
    },
    "components": {
      "$ref": "#/$defs/components"
    }
  },
  "patternProperties": {
    "^x-": true
  },
  "unevaluatedProperties": false,
  "$defs": {
    "info": {
      "type": "object",
      "required": [
        "name",
        "version"
      ],
      "properties": {
        "name": {
          "description": "The program's name.",
          "type": "string",
          "minLength": 1
        },
        "bin": {
          "description": "The executable a shell runs, when it differs from name.",
          "type": "string",
          "minLength": 1
        },
        "version": {
          "description": "The version of the program this document describes.",
          "type": "string",
          "minLength": 1
        },
        "summary": {
          "$ref": "#/$defs/summary"
        },
        "description": {
          "$ref": "#/$defs/description"
        },
        "homepage": {
          "type": "string",
          "format": "uri"
        },
        "license": {
          "type": "object",
          "properties": {
            "name": {
              "type": "string"
            },
            "identifier": {
              "description": "An SPDX expression.",
              "type": "string"
            },
            "url": {
              "type": "string",
              "format": "uri"
            }
          },
          "additionalProperties": false
        },
        "contact": {
          "type": "object",
          "properties": {
            "name": {
              "type": "string"
            },
            "email": {
              "type": "string"
            },
            "url": {
              "type": "string",
              "format": "uri"
            }
          },
          "additionalProperties": false
        }
      },
      "patternProperties": {
        "^x-": true
      },
      "additionalProperties": false
    },
    "syntax": {
      "description": "How the program's parser reads a command line. Every field has a default, and the defaults are GNU getopt_long's behaviour.",
      "type": "object",
      "properties": {
        "bundling": {
          "description": "Single-character options combine: -abc is -a -b -c.",
          "type": "boolean",
          "default": true
        },
        "shortValue": {
          "description": "How a single-character option takes its value: -ofile, -o file, or either.",
          "enum": [
            "attached",
            "separate",
            "both"
          ],
          "default": "both"
        },
        "longValue": {
          "description": "How a long option takes its value: --out=dir, --out dir, or either.",
          "enum": [
            "equals",
            "separate",
            "both"
          ],
          "default": "both"
        },
        "terminator": {
          "description": "The token after which nothing is read as an option, or null when there is none.",
          "type": [
            "string",
            "null"
          ],
          "default": "--"
        },
        "interspersed": {
          "description": "Options may follow positional arguments.",
          "type": "boolean",
          "default": true
        },
        "abbreviation": {
          "description": "A long option may be shortened to any unambiguous prefix.",
          "type": "boolean",
          "default": false
        },
        "precedence": {
          "description": "Where an option's value comes from, highest priority first.",
          "type": "array",
          "items": {
            "enum": [
              "flag",
              "env",
              "config",
              "default"
            ]
          },
          "uniqueItems": true,
          "default": [
            "flag",
            "env",
            "config",
            "default"
          ]
        }
      },
      "patternProperties": {
        "^x-": true
      },
      "additionalProperties": false
    },
    "commandBody": {
      "description": "Everything a command can declare. The document root is the program's root command and declares these too.",
      "type": "object",
      "properties": {
        "summary": {
          "$ref": "#/$defs/summary"
        },
        "description": {
          "$ref": "#/$defs/description"
        },
        "arguments": {
          "description": "Positional arguments, in the order they are written.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/argumentOrRef"
          }
        },
        "options": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/optionOrRef"
          }
        },
        "commands": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/command"
          }
        },
        "subcommandRequired": {
          "description": "The command does nothing on its own; one of its subcommands must be named.",
          "type": "boolean",
          "default": false
        },
        "plugins": {
          "$ref": "#/$defs/plugins"
        },
        "constraints": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/constraint"
          }
        },
        "exits": {
          "$ref": "#/$defs/exits"
        },
        "io": {
          "$ref": "#/$defs/io"
        },
        "effects": {
          "$ref": "#/$defs/effects"
        },
        "idempotent": {
          "description": "Running the command twice with the same input leaves the same state as running it once.",
          "type": "boolean"
        },
        "interactive": {
          "$ref": "#/$defs/interactive"
        },
        "longRunning": {
          "description": "The command does not return on its own: a server, a watcher, a follow.",
          "type": "boolean",
          "default": false
        },
        "examples": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/example"
          }
        },
        "group": {
          "$ref": "#/$defs/group"
        },
        "deprecated": {
          "$ref": "#/$defs/deprecated"
        },
        "since": {
          "$ref": "#/$defs/since"
        },
        "stability": {
          "$ref": "#/$defs/stability"
        },
        "hidden": {
          "$ref": "#/$defs/hidden"
        }
      }
    },
    "command": {
      "type": "object",
      "required": [
        "name"
      ],
      "allOf": [
        {
          "$ref": "#/$defs/commandBody"
        }
      ],
      "properties": {
        "name": {
          "description": "The word that selects this command. It may look like an option — dotnet's legacy `dotnet new --list` is a command — but not like one this command also accepts.",
          "type": "string",
          "pattern": "^\\S+$"
        },
        "aliases": {
          "type": "array",
          "items": {
            "type": "string",
            "pattern": "^\\S+$"
          },
          "uniqueItems": true
        }
      },
      "patternProperties": {
        "^x-": true
      },
      "unevaluatedProperties": false
    },
    "argumentOrRef": {
      "oneOf": [
        {
          "$ref": "#/$defs/argument"
        },
        {
          "$ref": "#/$defs/reference"
        }
      ]
    },
    "argument": {
      "type": "object",
      "required": [
        "name"
      ],
      "properties": {
        "name": {
          "description": "How the argument is referred to — in the usage line, in constraints, and in prose. Never typed on a command line. It does not begin with - or with space, so it cannot be mistaken for an option; it may contain spaces, as `PROJECT | FILE` does.",
          "type": "string",
          "pattern": "^[^\\s-](.*\\S)?$"
        },
        "summary": {
          "$ref": "#/$defs/summary"
        },
        "description": {
          "$ref": "#/$defs/description"
        },
        "value": {
          "$ref": "#/$defs/value"
        },
        "required": {
          "type": "boolean",
          "default": false
        },
        "variadic": {
          "description": "The argument takes one or more words — zero or more when not required.",
          "type": "boolean",
          "default": false
        },
        "min": {
          "description": "Fewest words a variadic argument takes.",
          "type": "integer",
          "minimum": 0
        },
        "max": {
          "description": "Most words a variadic argument takes.",
          "type": "integer",
          "minimum": 1
        },
        "rest": {
          "description": "Every remaining word is taken verbatim, including words that look like options — the command line of another program.",
          "type": "boolean",
          "default": false
        },
        "afterTerminator": {
          "description": "The argument may be separated from what precedes it by the terminator, and must be when it could be mistaken for something else.",
          "type": "boolean",
          "default": false
        },
        "env": {
          "$ref": "#/$defs/envNames"
        },
        "scopedOptions": {
          "$ref": "#/$defs/scopedOptions"
        },
        "deprecated": {
          "$ref": "#/$defs/deprecated"
        },
        "since": {
          "$ref": "#/$defs/since"
        },
        "hidden": {
          "$ref": "#/$defs/hidden"
        }
      },
      "patternProperties": {
        "^x-": true
      },
      "additionalProperties": false
    },
    "optionOrRef": {
      "oneOf": [
        {
          "$ref": "#/$defs/option"
        },
        {
          "$ref": "#/$defs/reference"
        }
      ]
    },
    "option": {
      "type": "object",
      "required": [
        "name"
      ],
      "properties": {
        "name": {
          "description": "The option's canonical spelling, exactly as typed: --output, -o, -name, +x.",
          "$ref": "#/$defs/spelling"
        },
        "aliases": {
          "description": "Other spellings of the same option, exactly as typed.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/spelling"
          },
          "uniqueItems": true
        },
        "summary": {
          "$ref": "#/$defs/summary"
        },
        "description": {
          "$ref": "#/$defs/description"
        },
        "value": {
          "description": "What the option takes. An option without one is a switch.",
          "$ref": "#/$defs/value"
        },
        "values": {
          "description": "What the option takes when it is followed by several words, in order: jq's --arg name value. An option has value or values, never both.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/value"
          },
          "minItems": 2
        },
        "variadic": {
          "description": "The value takes one or more words — dotnet's --property a=1 b=2. Zero or more when the value is optional.",
          "type": "boolean",
          "default": false
        },
        "min": {
          "description": "Fewest words a variadic value takes.",
          "type": "integer",
          "minimum": 0
        },
        "max": {
          "description": "Most words a variadic value takes.",
          "type": "integer",
          "minimum": 1
        },
        "qualifier": {
          "description": "A suffix the option may carry that narrows what it applies to: ffmpeg's -c:v, where :v selects the video streams.",
          "type": "object",
          "required": [
            "separator",
            "value"
          ],
          "properties": {
            "separator": {
              "type": "string",
              "minLength": 1
            },
            "value": {
              "$ref": "#/$defs/value"
            }
          },
          "patternProperties": {
            "^x-": true
          },
          "additionalProperties": false
        },
        "required": {
          "type": "boolean",
          "default": false
        },
        "repeat": {
          "description": "What giving the option more than once does.",
          "enum": [
            "error",
            "last",
            "list",
            "count"
          ],
          "default": "last"
        },
        "repeatMin": {
          "description": "Fewest times a list option must be given.",
          "type": "integer",
          "minimum": 0
        },
        "repeatMax": {
          "description": "Most times a list option may be given.",
          "type": "integer",
          "minimum": 1
        },
        "negation": {
          "description": "Spellings that undo the option, exactly as typed: --no-color turns a switch off, --no-author discards an earlier value.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/spelling"
          },
          "minItems": 1,
          "uniqueItems": true
        },
        "inherited": {
          "description": "The option is accepted by every command below the one that declares it.",
          "type": "boolean",
          "default": false
        },
        "env": {
          "$ref": "#/$defs/envNames"
        },
        "config": {
          "description": "The key, in the program's config files, that sets this option. Dots separate nested keys.",
          "type": "string",
          "minLength": 1
        },
        "secret": {
          "description": "The value is a credential: prefer the environment variable, and never echo it.",
          "type": "boolean",
          "default": false
        },
        "effects": {
          "description": "The command's effects instead, whenever the option is given with a value other than its default — how --dry-run is described.",
          "$ref": "#/$defs/effects"
        },
        "longRunning": {
          "description": "Given, the command no longer returns on its own: --follow, --watch.",
          "type": "boolean",
          "default": false
        },
        "scopedOptions": {
          "$ref": "#/$defs/scopedOptions"
        },
        "group": {
          "$ref": "#/$defs/group"
        },
        "deprecated": {
          "$ref": "#/$defs/deprecated"
        },
        "since": {
          "$ref": "#/$defs/since"
        },
        "stability": {
          "$ref": "#/$defs/stability"
        },
        "hidden": {
          "$ref": "#/$defs/hidden"
        }
      },
      "patternProperties": {
        "^x-": true
      },
      "additionalProperties": false,
      "not": {
        "required": [
          "value",
          "values"
        ]
      }
    },
    "spelling": {
      "type": "string",
      "pattern": "^[-+/][^\\s=]*[^\\s=-]$"
    },
    "value": {
      "description": "A value an argument or option takes. Its constraint keywords are JSON Schema's, so an argument's value converts to a tool's input schema without translation.",
      "type": "object",
      "properties": {
        "name": {
          "description": "The placeholder shown in a usage line: file, n, when.",
          "type": "string",
          "minLength": 1
        },
        "type": {
          "enum": [
            "string",
            "integer",
            "number",
            "boolean"
          ],
          "default": "string"
        },
        "format": {
          "description": "What the string denotes. Known formats: path, file, directory, url, email, date, date-time, duration, size, regex, glob, json, key-value, command. Others are allowed and mean nothing to a consumer that does not know them.",
          "type": "string",
          "minLength": 1
        },
        "enum": {
          "type": "array",
          "items": {
            "type": [
              "string",
              "number",
              "boolean"
            ]
          },
          "minItems": 1,
          "uniqueItems": true
        },
        "default": {
          "type": [
            "string",
            "number",
            "boolean"
          ]
        },
        "defaultDescription": {
          "description": "The default when it is computed rather than fixed: \"the number of CPUs\".",
          "type": "string",
          "minLength": 1
        },
        "minimum": {
          "type": "number"
        },
        "maximum": {
          "type": "number"
        },
        "pattern": {
          "type": "string",
          "format": "regex"
        },
        "minLength": {
          "type": "integer",
          "minimum": 0
        },
        "maxLength": {
          "type": "integer",
          "minimum": 0
        },
        "optional": {
          "description": "The option may be given without its value: --color or --color=always. syntax.longValue and syntax.shortValue say which forms can carry one.",
          "type": "boolean",
          "default": false
        },
        "separator": {
          "description": "One word carries several values split on this string: --tags a,b,c.",
          "type": "string",
          "minLength": 1
        },
        "stdio": {
          "description": "The word - means standard input (or output) rather than a file of that name.",
          "type": "boolean",
          "default": false
        },
        "valuesFrom": {
          "description": "A command whose output, one value per line, is the values currently valid: kubectl get namespaces -o name.",
          "type": "string",
          "minLength": 1
        }
      },
      "patternProperties": {
        "^x-": true
      },
      "additionalProperties": false
    },
    "scopedOptions": {
      "description": "Options that, written before this argument or option, apply to it alone — ffmpeg's per-file options.",
      "type": "array",
      "items": {
        "$ref": "#/$defs/optionOrRef"
      }
    },
    "envNames": {
      "description": "Environment variables that supply the value when it is not given on the command line, first match wins.",
      "type": "array",
      "items": {
        "type": "string",
        "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
      },
      "minItems": 1,
      "uniqueItems": true
    },
    "constraint": {
      "description": "A rule over options and arguments, each named by its canonical spelling or argument name.",
      "type": "object",
      "properties": {
        "exclusive": {
          "description": "At most one of these.",
          "$ref": "#/$defs/names"
        },
        "oneOf": {
          "description": "Exactly one of these.",
          "$ref": "#/$defs/names"
        },
        "anyOf": {
          "description": "At least one of these.",
          "$ref": "#/$defs/names"
        },
        "requires": {
          "description": "All of these, whenever `when` is given.",
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          },
          "minItems": 1,
          "uniqueItems": true
        },
        "when": {
          "description": "The option or argument whose presence triggers `requires`.",
          "type": "string",
          "minLength": 1
        },
        "summary": {
          "$ref": "#/$defs/summary"
        }
      },
      "oneOf": [
        {
          "required": [
            "exclusive"
          ]
        },
        {
          "required": [
            "oneOf"
          ]
        },
        {
          "required": [
            "anyOf"
          ]
        },
        {
          "required": [
            "requires",
            "when"
          ]
        }
      ],
      "patternProperties": {
        "^x-": true
      },
      "additionalProperties": false
    },
    "names": {
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1
      },
      "minItems": 2,
      "uniqueItems": true
    },
    "exits": {
      "description": "Exit statuses, keyed by code. `*` stands for every non-zero status not listed. A command inherits its ancestors' entries and may override them.",
      "type": "object",
      "propertyNames": {
        "pattern": "^([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]|\\*)$"
      },
      "additionalProperties": {
        "$ref": "#/$defs/exit"
      }
    },
    "exit": {
      "type": "object",
      "required": [
        "meaning"
      ],
      "properties": {
        "meaning": {
          "description": "What this status tells the caller, in one line.",
          "type": "string",
          "minLength": 1
        },
        "description": {
          "$ref": "#/$defs/description"
        },
        "retryable": {
          "description": "Running the same command again, unchanged, may succeed.",
          "type": "boolean"
        }
      },
      "patternProperties": {
        "^x-": true
      },
      "additionalProperties": false
    },
    "io": {
      "type": "object",
      "properties": {
        "stdin": {
          "$ref": "#/$defs/streams"
        },
        "stdout": {
          "$ref": "#/$defs/streams"
        },
        "stderr": {
          "$ref": "#/$defs/streams"
        }
      },
      "patternProperties": {
        "^x-": true
      },
      "additionalProperties": false
    },
    "streams": {
      "description": "What the stream carries. More than one entry means it carries different things depending on `selectedBy`.",
      "type": "array",
      "items": {
        "$ref": "#/$defs/stream"
      },
      "minItems": 1
    },
    "stream": {
      "type": "object",
      "required": [
        "mediaType"
      ],
      "properties": {
        "mediaType": {
          "description": "text/plain, text/markdown, application/json, application/jsonl, text/csv, application/octet-stream…",
          "type": "string",
          "pattern": "^[a-z]+/[a-zA-Z0-9.+-]+$"
        },
        "summary": {
          "$ref": "#/$defs/summary"
        },
        "selectedBy": {
          "description": "The option that selects this content, as typed: --json, --output=yaml. Absent on the default.",
          "type": "string",
          "minLength": 1
        },
        "schema": {
          "description": "A JSON Schema for the content, inline or as a $ref.",
          "type": "object"
        }
      },
      "patternProperties": {
        "^x-": true
      },
      "additionalProperties": false
    },
    "effects": {
      "description": "What running the command can change. An empty list means nothing beyond its output; an absent one means it is not stated.",
      "type": "array",
      "items": {
        "enum": [
          "read",
          "write",
          "destructive",
          "network",
          "exec"
        ]
      },
      "uniqueItems": true
    },
    "interactive": {
      "type": "object",
      "required": [
        "when"
      ],
      "properties": {
        "when": {
          "description": "When the command prompts: never, only on a terminal, or always.",
          "enum": [
            "never",
            "tty",
            "always"
          ]
        },
        "disable": {
          "description": "The option that answers every prompt, as typed: --yes.",
          "type": "string",
          "minLength": 1
        }
      },
      "patternProperties": {
        "^x-": true
      },
      "additionalProperties": false
    },
    "plugins": {
      "description": "An unknown subcommand runs another executable: git foo runs git-foo.",
      "type": "object",
      "required": [
        "executable"
      ],
      "properties": {
        "executable": {
          "description": "The executable's name, with {name} standing for the subcommand.",
          "type": "string",
          "pattern": "\\{name\\}"
        },
        "summary": {
          "$ref": "#/$defs/summary"
        }
      },
      "patternProperties": {
        "^x-": true
      },
      "additionalProperties": false
    },
    "example": {
      "type": "object",
      "required": [
        "run"
      ],
      "properties": {
        "run": {
          "description": "The command line, exactly as typed.",
          "type": "string",
          "minLength": 1
        },
        "summary": {
          "$ref": "#/$defs/summary"
        },
        "output": {
          "description": "What it prints.",
          "type": "string"
        },
        "exit": {
          "description": "The status it exits with.",
          "type": "integer",
          "minimum": 0,
          "maximum": 255,
          "default": 0
        }
      },
      "patternProperties": {
        "^x-": true
      },
      "additionalProperties": false
    },
    "environmentVariable": {
      "type": "object",
      "required": [
        "name"
      ],
      "properties": {
        "name": {
          "type": "string",
          "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
        },
        "summary": {
          "$ref": "#/$defs/summary"
        },
        "description": {
          "$ref": "#/$defs/description"
        },
        "value": {
          "$ref": "#/$defs/value"
        },
        "secret": {
          "type": "boolean",
          "default": false
        }
      },
      "patternProperties": {
        "^x-": true
      },
      "additionalProperties": false
    },
    "file": {
      "type": "object",
      "required": [
        "path"
      ],
      "properties": {
        "path": {
          "description": "The path, with ~ for the home directory and $VAR for a variable.",
          "type": "string",
          "minLength": 1
        },
        "role": {
          "enum": [
            "config",
            "data",
            "cache",
            "state",
            "log"
          ]
        },
        "format": {
          "description": "json, yaml, toml, ini, sqlite…",
          "type": "string",
          "minLength": 1
        },
        "summary": {
          "$ref": "#/$defs/summary"
        }
      },
      "patternProperties": {
        "^x-": true
      },
      "additionalProperties": false
    },
    "components": {
      "description": "Definitions referenced from elsewhere in the document by $ref: \"#/components/options/<key>\".",
      "type": "object",
      "properties": {
        "options": {
          "type": "object",
          "additionalProperties": {
            "$ref": "#/$defs/option"
          }
        },
        "arguments": {
          "type": "object",
          "additionalProperties": {
            "$ref": "#/$defs/argument"
          }
        },
        "schemas": {
          "type": "object",
          "additionalProperties": {
            "type": "object"
          }
        }
      },
      "patternProperties": {
        "^x-": true
      },
      "additionalProperties": false
    },
    "reference": {
      "type": "object",
      "required": [
        "$ref"
      ],
      "properties": {
        "$ref": {
          "type": "string",
          "pattern": "^#/components/(options|arguments)/[^/]+$"
        }
      },
      "additionalProperties": false
    },
    "summary": {
      "description": "One line, plain text, no trailing full stop.",
      "type": "string",
      "minLength": 1
    },
    "description": {
      "description": "CommonMark.",
      "type": "string",
      "minLength": 1
    },
    "group": {
      "description": "The heading this is listed under in help.",
      "type": "string",
      "minLength": 1
    },
    "deprecated": {
      "oneOf": [
        {
          "type": "boolean"
        },
        {
          "type": "object",
          "properties": {
            "since": {
              "type": "string",
              "minLength": 1
            },
            "use": {
              "description": "What to use instead, as typed.",
              "type": "string",
              "minLength": 1
            },
            "summary": {
              "$ref": "#/$defs/summary"
            }
          },
          "additionalProperties": false
        }
      ]
    },
    "since": {
      "description": "The program version that introduced this.",
      "type": "string",
      "minLength": 1
    },
    "stability": {
      "enum": [
        "experimental",
        "stable"
      ],
      "default": "stable"
    },
    "hidden": {
      "description": "Accepted, but left out of help.",
      "type": "boolean",
      "default": false
    }
  }
}
