// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========

import { CommandSpec, Operand, OperandKind, Option } from './types.ts'

export function specOf(name: string): CommandSpec {
  const spec = BUILTIN_SPECS[name]
  if (spec === undefined) throw new Error(`no builtin spec: ${name}`)
  return spec
}

export const BUILTIN_SPECS: Readonly<Record<string, CommandSpec>> = Object.freeze({
  ls: new CommandSpec({
    options: [
      new Option({ short: '-l' }),
      new Option({ short: '-a' }),
      new Option({ short: '-A' }),
      new Option({ short: '-h' }),
      new Option({ short: '-t' }),
      new Option({ short: '-S' }),
      new Option({ short: '-r' }),
      new Option({ short: '-1' }),
      new Option({ short: '-R' }),
      new Option({ short: '-d' }),
      new Option({ short: '-F' }),
      // Accepted no-op like grep --color (#471).
      new Option({ long: '--color', valueKind: OperandKind.TEXT, valueOptional: true }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  stat: new CommandSpec({
    options: [
      new Option({ short: '-c', valueKind: OperandKind.TEXT }),
      new Option({ short: '-f', valueKind: OperandKind.TEXT }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  pwd: new CommandSpec({
    options: [new Option({ short: '-P' }), new Option({ short: '-L' })],
    rest: new Operand({ kind: OperandKind.TEXT }),
  }),
  find: new CommandSpec({
    options: [
      new Option({ short: '-name', valueKind: OperandKind.TEXT, repeatable: true }),
      new Option({ short: '-type', valueKind: OperandKind.TEXT, repeatable: true }),
      new Option({ short: '-maxdepth', valueKind: OperandKind.TEXT, repeatable: true }),
      new Option({ short: '-size', valueKind: OperandKind.TEXT, repeatable: true }),
      new Option({ short: '-mtime', valueKind: OperandKind.TEXT, repeatable: true }),
      new Option({ short: '-iname', valueKind: OperandKind.TEXT, repeatable: true }),
      new Option({ short: '-path', valueKind: OperandKind.TEXT, repeatable: true }),
      new Option({ short: '-mindepth', valueKind: OperandKind.TEXT, repeatable: true }),
      new Option({ short: '-print' }),
      new Option({ short: '-print0' }),
      new Option({ short: '-delete' }),
      new Option({ short: '-depth' }),
      new Option({ short: '-prune' }),
      new Option({ short: '-ls' }),
      new Option({ short: '-empty' }),
      new Option({ short: '-o' }),
      new Option({ short: '-or' }),
      new Option({ short: '-a' }),
      new Option({ short: '-and' }),
      new Option({ short: '-not' }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
    ignoreTokens: ['(', ')'],
  }),
  tree: new CommandSpec({
    options: [
      new Option({ short: '-a' }),
      new Option({ short: '-L', valueKind: OperandKind.TEXT }),
      new Option({ short: '-I', valueKind: OperandKind.TEXT }),
      new Option({ short: '-d' }),
      new Option({ short: '-P', valueKind: OperandKind.TEXT }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  du: new CommandSpec({
    options: [
      new Option({ short: '-h' }),
      new Option({ short: '-s' }),
      new Option({ short: '-a' }),
      new Option({ long: '--max-depth', valueKind: OperandKind.TEXT }),
      new Option({ short: '-c' }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  cat: new CommandSpec({
    options: [
      new Option({ short: '-n', long: '--number' }),
      new Option({ short: '-b', long: '--number-nonblank' }),
      new Option({ short: '-E', long: '--show-ends' }),
      new Option({ short: '-T', long: '--show-tabs' }),
      new Option({ short: '-v', long: '--show-nonprinting' }),
      new Option({ short: '-e' }),
      new Option({ short: '-t' }),
      new Option({ short: '-A', long: '--show-all' }),
      new Option({ short: '-s', long: '--squeeze-blank' }),
      new Option({ short: '-u' }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  head: new CommandSpec({
    options: [
      new Option({
        short: '-n',
        long: '--lines',
        valueKind: OperandKind.TEXT,
        numericShorthand: true,
      }),
      new Option({ short: '-c', long: '--bytes', valueKind: OperandKind.TEXT }),
      new Option({ short: '-q', long: '--quiet' }),
      new Option({ long: '--silent' }),
      new Option({ short: '-v', long: '--verbose' }),
      new Option({ short: '-z', long: '--zero-terminated' }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  tail: new CommandSpec({
    options: [
      new Option({ short: '-n', valueKind: OperandKind.TEXT, numericShorthand: true }),
      new Option({ short: '-c', valueKind: OperandKind.TEXT }),
      new Option({ short: '-q' }),
      new Option({ short: '-v' }),
      new Option({ short: '-f', long: '--follow' }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  wc: new CommandSpec({
    options: [
      new Option({ short: '-l', long: '--lines' }),
      new Option({ short: '-w', long: '--words' }),
      new Option({ short: '-c', long: '--bytes' }),
      new Option({ short: '-m', long: '--chars' }),
      new Option({ short: '-L', long: '--max-line-length' }),
      new Option({ long: '--total', valueKind: OperandKind.TEXT }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  md5: new CommandSpec({ rest: new Operand({ kind: OperandKind.PATH }) }),
  diff: new CommandSpec({
    options: [
      new Option({ short: '-i' }),
      new Option({ short: '-w' }),
      new Option({ short: '-b' }),
      new Option({ short: '-e' }),
      new Option({ short: '-u' }),
      new Option({ short: '-q' }),
      new Option({ short: '-r' }),
    ],
    positional: [new Operand({ kind: OperandKind.PATH }), new Operand({ kind: OperandKind.PATH })],
  }),
  file: new CommandSpec({
    options: [new Option({ short: '-b' }), new Option({ short: '-i' })],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  python: new CommandSpec({
    options: [new Option({ short: '-c', valueKind: OperandKind.TEXT })],
    rest: new Operand({ kind: OperandKind.TEXT }),
  }),
  python3: new CommandSpec({
    options: [new Option({ short: '-c', valueKind: OperandKind.TEXT })],
    rest: new Operand({ kind: OperandKind.TEXT }),
  }),
  js: new CommandSpec({
    options: [
      new Option({ short: '-e', valueKind: OperandKind.TEXT }),
      new Option({ short: '-m', long: '--module' }),
    ],
    rest: new Operand({ kind: OperandKind.TEXT }),
  }),
  node: new CommandSpec({
    options: [
      new Option({ short: '-e', valueKind: OperandKind.TEXT }),
      new Option({ short: '-m', long: '--module' }),
    ],
    rest: new Operand({ kind: OperandKind.TEXT }),
  }),
  nl: new CommandSpec({
    options: [
      new Option({ short: '-b', valueKind: OperandKind.TEXT }),
      new Option({ short: '-d', valueKind: OperandKind.TEXT }),
      new Option({ short: '-v', valueKind: OperandKind.TEXT }),
      new Option({ short: '-i', valueKind: OperandKind.TEXT }),
      new Option({ short: '-w', valueKind: OperandKind.TEXT }),
      new Option({ short: '-s', valueKind: OperandKind.TEXT }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  grep: new CommandSpec({
    options: [
      new Option({ short: '-r' }),
      new Option({ short: '-R' }),
      new Option({ short: '-i' }),
      new Option({ short: '-I' }),
      new Option({ short: '-v' }),
      new Option({ short: '-n' }),
      new Option({ short: '-c' }),
      new Option({ short: '-l' }),
      new Option({ short: '-w' }),
      new Option({ short: '-F' }),
      new Option({ short: '-E' }),
      new Option({ short: '-o' }),
      new Option({ short: '-q' }),
      new Option({ short: '-H' }),
      new Option({ short: '-h' }),
      new Option({ short: '-m', valueKind: OperandKind.TEXT }),
      new Option({ short: '-A', valueKind: OperandKind.TEXT }),
      new Option({ short: '-B', valueKind: OperandKind.TEXT }),
      new Option({ short: '-C', valueKind: OperandKind.TEXT }),
      new Option({ short: '-e', valueKind: OperandKind.TEXT, repeatable: true }),
      new Option({ short: '-f', valueKind: OperandKind.PATH, repeatable: true }),
      // Accepted no-ops: output is never a tty, so plain output is
      // exactly what GNU produces with --color=auto (#471).
      new Option({ long: '--color', valueKind: OperandKind.TEXT, valueOptional: true }),
      new Option({ long: '--colour', valueKind: OperandKind.TEXT, valueOptional: true }),
      new Option({ long: '--line-buffered' }),
    ],
    positional: [new Operand({ kind: OperandKind.TEXT, providedBy: ['-e', '-f'] })],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  rg: new CommandSpec({
    options: [
      new Option({ short: '-i' }),
      new Option({ short: '-v' }),
      new Option({ short: '-n' }),
      new Option({ short: '-c' }),
      new Option({ short: '-l' }),
      new Option({ short: '-w' }),
      new Option({ short: '-F' }),
      new Option({ short: '-o' }),
      new Option({ short: '-H' }),
      new Option({ short: '-I' }),
      new Option({ short: '-e', valueKind: OperandKind.TEXT, repeatable: true }),
      new Option({ short: '-f', valueKind: OperandKind.PATH, repeatable: true }),
      new Option({ short: '-m', valueKind: OperandKind.TEXT }),
      new Option({ short: '-A', valueKind: OperandKind.TEXT }),
      new Option({ short: '-B', valueKind: OperandKind.TEXT }),
      new Option({ short: '-C', valueKind: OperandKind.TEXT }),
      new Option({ long: '--hidden' }),
      new Option({ long: '--type', valueKind: OperandKind.TEXT }),
      new Option({ long: '--glob', valueKind: OperandKind.TEXT }),
      // Accepted no-op like grep --color (#471).
      new Option({ long: '--color', valueKind: OperandKind.TEXT, valueOptional: true }),
    ],
    positional: [new Operand({ kind: OperandKind.TEXT, providedBy: ['-e', '-f'] })],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  search: new CommandSpec({
    options: [
      new Option({ long: '--method', valueKind: OperandKind.TEXT }),
      new Option({ long: '--top-k', valueKind: OperandKind.TEXT }),
      new Option({ long: '--threshold', valueKind: OperandKind.TEXT }),
    ],
    positional: [new Operand({ kind: OperandKind.TEXT })],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  sort: new CommandSpec({
    options: [
      new Option({ short: '-r', long: '--reverse' }),
      new Option({ short: '-n', long: '--numeric-sort' }),
      new Option({ short: '-u', long: '--unique' }),
      new Option({ short: '-f', long: '--ignore-case' }),
      new Option({ short: '-k', long: '--key', valueKind: OperandKind.TEXT, repeatable: true }),
      new Option({ short: '-t', long: '--field-separator', valueKind: OperandKind.TEXT }),
      new Option({ short: '-h', long: '--human-numeric-sort' }),
      new Option({ short: '-V', long: '--version-sort' }),
      new Option({ short: '-s', long: '--stable' }),
      new Option({ short: '-M', long: '--month-sort' }),
      new Option({ short: '-b', long: '--ignore-leading-blanks' }),
      new Option({ short: '-c' }),
      new Option({ long: '--check', valueKind: OperandKind.TEXT, valueOptional: true }),
      new Option({ short: '-d', long: '--dictionary-order' }),
      new Option({ short: '-g', long: '--general-numeric-sort' }),
      new Option({ short: '-i', long: '--ignore-nonprinting' }),
      new Option({ short: '-m', long: '--merge' }),
      new Option({ short: '-o', long: '--output', valueKind: OperandKind.PATH }),
      new Option({ short: '-z', long: '--zero-terminated' }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  uniq: new CommandSpec({
    options: [
      new Option({ short: '-c', long: '--count' }),
      new Option({ short: '-d', long: '--repeated' }),
      new Option({ short: '-D' }),
      new Option({ long: '--all-repeated', valueKind: OperandKind.TEXT, valueOptional: true }),
      new Option({ long: '--group', valueKind: OperandKind.TEXT, valueOptional: true }),
      new Option({ short: '-u', long: '--unique' }),
      new Option({ short: '-f', long: '--skip-fields', valueKind: OperandKind.TEXT }),
      new Option({ short: '-s', long: '--skip-chars', valueKind: OperandKind.TEXT }),
      new Option({ short: '-i', long: '--ignore-case' }),
      new Option({ short: '-w', long: '--check-chars', valueKind: OperandKind.TEXT }),
      new Option({ short: '-z', long: '--zero-terminated' }),
    ],
    positional: [new Operand({ kind: OperandKind.PATH }), new Operand({ kind: OperandKind.PATH })],
  }),
  cut: new CommandSpec({
    options: [
      new Option({ short: '-f', long: '--fields', valueKind: OperandKind.TEXT }),
      new Option({ short: '-F', valueKind: OperandKind.TEXT }),
      new Option({ short: '-d', long: '--delimiter', valueKind: OperandKind.TEXT }),
      new Option({ short: '-c', long: '--characters', valueKind: OperandKind.TEXT }),
      new Option({ short: '-b', long: '--bytes', valueKind: OperandKind.TEXT }),
      new Option({ short: '-n', long: '--no-partial' }),
      new Option({ long: '--complement' }),
      new Option({ short: '-s', long: '--only-delimited' }),
      new Option({ short: '-O', valueKind: OperandKind.TEXT }),
      new Option({ long: '--output-delimiter', valueKind: OperandKind.TEXT }),
      new Option({ short: '-w' }),
      new Option({
        long: '--whitespace-delimited',
        valueKind: OperandKind.TEXT,
        valueOptional: true,
      }),
      new Option({ short: '-z', long: '--zero-terminated' }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  mkdir: new CommandSpec({
    options: [new Option({ short: '-p' }), new Option({ short: '-v' })],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  touch: new CommandSpec({
    options: [
      new Option({ short: '-c' }),
      new Option({ short: '-r', valueKind: OperandKind.PATH }),
      new Option({ short: '-d', valueKind: OperandKind.TEXT }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  // chmod/chown/chgrp self-parse their flags in the executor builtins, but
  // they still need a spec so the leading MODE/OWNER/GROUP stays TEXT while
  // the FILE operands classify as PATH (and so relative operands resolve
  // against the session cwd, not the mount root).
  chmod: new CommandSpec({
    options: [
      new Option({ short: '-R' }),
      new Option({ short: '-v' }),
      new Option({ short: '-f' }),
    ],
    positional: [new Operand({ kind: OperandKind.TEXT })],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  chown: new CommandSpec({
    options: [
      new Option({ short: '-R' }),
      new Option({ short: '-v' }),
      new Option({ short: '-f' }),
      new Option({ short: '-h' }),
    ],
    positional: [new Operand({ kind: OperandKind.TEXT })],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  chgrp: new CommandSpec({
    options: [
      new Option({ short: '-R' }),
      new Option({ short: '-v' }),
      new Option({ short: '-f' }),
      new Option({ short: '-h' }),
    ],
    positional: [new Operand({ kind: OperandKind.TEXT })],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  cp: new CommandSpec({
    options: [
      new Option({ short: '-r' }),
      new Option({ short: '-R' }),
      new Option({ short: '-a' }),
      new Option({ short: '-f' }),
      new Option({ short: '-n' }),
      new Option({ short: '-v' }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  mv: new CommandSpec({
    options: [
      new Option({ short: '-f' }),
      new Option({ short: '-n' }),
      new Option({ short: '-v' }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  rm: new CommandSpec({
    options: [
      new Option({ short: '-r' }),
      new Option({ short: '-R' }),
      new Option({ short: '-f' }),
      new Option({ short: '-v' }),
      new Option({ short: '-d' }),
      // Non-interactive control plane: -i/-I are accepted no-ops (there is
      // no prompt; removal always proceeds).
      new Option({ short: '-i' }),
      new Option({ short: '-I' }),
      // Mount roots (and /) are structurally protected and never removable,
      // so the root failsafe is always on and cannot be disabled; both
      // spellings are accepted no-ops. Recursion never crosses a mount
      // boundary either, so --one-file-system already matches the default.
      new Option({ long: '--preserve-root' }),
      new Option({ long: '--no-preserve-root' }),
      new Option({ long: '--one-file-system' }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  rmdir: new CommandSpec({
    options: [new Option({ short: '-v' })],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  unlink: new CommandSpec({ rest: new Operand({ kind: OperandKind.PATH }) }),
  sed: new CommandSpec({
    options: [
      new Option({ short: '-i' }),
      // -e takes a script and may repeat; multiple -e are joined with newlines.
      new Option({ short: '-e', valueKind: OperandKind.TEXT, repeatable: true }),
      // -f reads the script from a file and may repeat (like grep -f); its value
      // is a PATH so it routes and is read from the mount.
      new Option({ short: '-f', valueKind: OperandKind.PATH, repeatable: true }),
      new Option({ short: '-n' }),
      new Option({ short: '-E' }),
      new Option({ short: '-r' }),
    ],
    // providedBy lists the flags that can supply this positional slot's value;
    // when any is present the parser skips the slot so the next word is not
    // mis-grabbed. For sed the first operand is the script (TEXT) only when
    // neither -e nor -f gave one (GNU). With -e/-f the slot is skipped and the
    // first operand reflows to a file path in rest.
    positional: [new Operand({ kind: OperandKind.TEXT, providedBy: ['-e', '-f'] })],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  echo: new CommandSpec({
    options: [new Option({ short: '-n' }), new Option({ short: '-e' })],
    rest: new Operand({ kind: OperandKind.TEXT }),
  }),
  tee: new CommandSpec({
    options: [
      new Option({ short: '-a', long: '--append' }),
      new Option({ short: '-i', long: '--ignore-interrupts' }),
      new Option({ short: '-p' }),
      new Option({ long: '--output-error', valueKind: OperandKind.TEXT, valueOptional: true }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  tr: new CommandSpec({
    options: [
      new Option({ short: '-d', long: '--delete' }),
      new Option({ short: '-s', long: '--squeeze-repeats' }),
      new Option({ short: '-c', long: '--complement' }),
      new Option({ short: '-C' }),
      new Option({ short: '-t', long: '--truncate-set1' }),
    ],
    positional: [new Operand({ kind: OperandKind.TEXT }), new Operand({ kind: OperandKind.TEXT })],
  }),
  curl: new CommandSpec({
    description: 'Transfer data from or to a server.',
    options: [
      new Option({
        short: '-H',
        valueKind: OperandKind.TEXT,
        description: 'Add a custom header to the request.',
      }),
      new Option({
        short: '-A',
        valueKind: OperandKind.TEXT,
        description: 'Set the User-Agent header.',
      }),
      new Option({
        short: '-X',
        valueKind: OperandKind.TEXT,
        description: 'Specify the HTTP request method.',
      }),
      new Option({
        short: '-d',
        valueKind: OperandKind.TEXT,
        description: 'Send the given data as the request body.',
      }),
      new Option({
        short: '-F',
        valueKind: OperandKind.TEXT,
        description: 'Submit a multipart/form-data field.',
      }),
      new Option({
        short: '-o',
        valueKind: OperandKind.PATH,
        description: 'Write response body to the given file.',
      }),
      new Option({ short: '-L', description: 'Follow HTTP redirects.' }),
      new Option({ short: '-s', description: 'Run silently with no progress or messages.' }),
      new Option({ short: '-S', description: 'Show errors even when silent.' }),
    ],
    rest: new Operand({ kind: OperandKind.TEXT }),
  }),
  wget: new CommandSpec({
    description: 'Retrieve files from the web.',
    options: [
      new Option({
        short: '-O',
        valueKind: OperandKind.PATH,
        description: 'Write the downloaded content to the given file.',
      }),
      new Option({ short: '-q', description: 'Run quietly with no output.' }),
      new Option({
        long: '--spider',
        description: 'Check that the URL exists without downloading it.',
      }),
    ],
    positional: [new Operand({ kind: OperandKind.TEXT })],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  jq: new CommandSpec({
    options: [
      new Option({ short: '-r' }),
      new Option({ short: '-c' }),
      new Option({ short: '-s' }),
    ],
    positional: [new Operand({ kind: OperandKind.TEXT })],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  awk: new CommandSpec({
    options: [
      new Option({ short: '-F', valueKind: OperandKind.TEXT }),
      new Option({ short: '-v', valueKind: OperandKind.TEXT, repeatable: true }),
      new Option({ short: '-f', valueKind: OperandKind.PATH, repeatable: true }),
    ],
    positional: [new Operand({ kind: OperandKind.TEXT, providedBy: ['-f'] })],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  paste: new CommandSpec({
    options: [
      new Option({ short: '-d', valueKind: OperandKind.TEXT }),
      new Option({ short: '-s' }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  tac: new CommandSpec({ rest: new Operand({ kind: OperandKind.PATH }) }),
  printf: new CommandSpec({
    positional: [new Operand({ kind: OperandKind.TEXT })],
    rest: new Operand({ kind: OperandKind.TEXT }),
  }),
  seq: new CommandSpec({
    description: 'Print a sequence of numbers.',
    options: [
      new Option({
        short: '-s',
        valueKind: OperandKind.TEXT,
        description: 'Use the given separator between numbers.',
      }),
      new Option({
        short: '-w',
        description: 'Pad numbers with zeros to equal width.',
      }),
      new Option({
        short: '-f',
        valueKind: OperandKind.TEXT,
        description: 'Format each number with a printf-style format string.',
      }),
    ],
    positional: [
      new Operand({ kind: OperandKind.TEXT }),
      new Operand({ kind: OperandKind.TEXT }),
      new Operand({ kind: OperandKind.TEXT }),
    ],
  }),
  base64: new CommandSpec({
    options: [
      new Option({ short: '-d' }),
      new Option({ short: '-D' }),
      new Option({ short: '-w', valueKind: OperandKind.TEXT }),
    ],
    positional: [new Operand({ kind: OperandKind.PATH })],
  }),
  md5sum: new CommandSpec({
    options: [new Option({ short: '-c' })],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  sha1sum: new CommandSpec({
    options: [new Option({ short: '-c' })],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  sha256sum: new CommandSpec({
    options: [new Option({ short: '-c' })],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  sha384sum: new CommandSpec({
    options: [new Option({ short: '-c' })],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  sha512sum: new CommandSpec({
    options: [new Option({ short: '-c' })],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  xxd: new CommandSpec({
    options: [
      new Option({ short: '-r' }),
      new Option({ short: '-p' }),
      new Option({ short: '-l', valueKind: OperandKind.TEXT }),
      new Option({ short: '-c', valueKind: OperandKind.TEXT }),
      new Option({ short: '-s', valueKind: OperandKind.TEXT }),
      new Option({ short: '-g', valueKind: OperandKind.TEXT }),
      new Option({ short: '-u' }),
    ],
    positional: [new Operand({ kind: OperandKind.PATH }), new Operand({ kind: OperandKind.PATH })],
  }),
  tar: new CommandSpec({
    options: [
      new Option({ short: '-c' }),
      new Option({ short: '-x' }),
      new Option({ short: '-t' }),
      new Option({ short: '-z' }),
      new Option({ short: '-j' }),
      new Option({ short: '-J' }),
      new Option({ short: '-v' }),
      new Option({ short: '-f', valueKind: OperandKind.PATH }),
      new Option({ short: '-C', valueKind: OperandKind.PATH }),
      new Option({ long: '--strip-components', valueKind: OperandKind.TEXT }),
      new Option({ long: '--exclude', valueKind: OperandKind.TEXT }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  gzip: new CommandSpec({
    options: [
      new Option({ short: '-d' }),
      new Option({ short: '-k' }),
      new Option({ short: '-f' }),
      new Option({ short: '-c' }),
      new Option({ short: '-1' }),
      new Option({ short: '-2' }),
      new Option({ short: '-3' }),
      new Option({ short: '-4' }),
      new Option({ short: '-5' }),
      new Option({ short: '-6' }),
      new Option({ short: '-7' }),
      new Option({ short: '-8' }),
      new Option({ short: '-9' }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  gunzip: new CommandSpec({
    options: [
      new Option({ short: '-k' }),
      new Option({ short: '-f' }),
      new Option({ short: '-c' }),
      new Option({ short: '-t' }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  zip: new CommandSpec({
    options: [
      new Option({ short: '-r' }),
      new Option({ short: '-j' }),
      new Option({ short: '-q' }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  unzip: new CommandSpec({
    options: [
      new Option({ short: '-o' }),
      new Option({ short: '-l' }),
      new Option({ short: '-d', valueKind: OperandKind.PATH }),
      new Option({ short: '-q' }),
      new Option({ short: '-p' }),
      new Option({ short: '-t' }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  basename: new CommandSpec({ rest: new Operand({ kind: OperandKind.TEXT }) }),
  dirname: new CommandSpec({ rest: new Operand({ kind: OperandKind.TEXT }) }),
  realpath: new CommandSpec({
    options: [new Option({ short: '-e' }), new Option({ short: '-m' })],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  readlink: new CommandSpec({
    options: [
      new Option({ short: '-f' }),
      new Option({ short: '-e' }),
      new Option({ short: '-m' }),
      new Option({ short: '-n' }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  ln: new CommandSpec({
    options: [
      new Option({ short: '-s' }),
      new Option({ short: '-f' }),
      new Option({ short: '-n' }),
      new Option({ short: '-v' }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  split: new CommandSpec({
    options: [
      new Option({ short: '-l', valueKind: OperandKind.TEXT }),
      new Option({ short: '-b', valueKind: OperandKind.TEXT }),
      new Option({ short: '-n', valueKind: OperandKind.TEXT }),
      new Option({ short: '-d' }),
      new Option({ short: '-a', valueKind: OperandKind.TEXT }),
    ],
    positional: [new Operand({ kind: OperandKind.PATH }), new Operand({ kind: OperandKind.PATH })],
  }),
  patch: new CommandSpec({
    options: [
      new Option({ short: '-p', valueKind: OperandKind.TEXT }),
      new Option({ short: '-R' }),
      new Option({ short: '-i', valueKind: OperandKind.PATH }),
      new Option({ short: '-N' }),
    ],
    positional: [new Operand({ kind: OperandKind.PATH }), new Operand({ kind: OperandKind.PATH })],
  }),
  shuf: new CommandSpec({
    options: [
      new Option({ short: '-n', valueKind: OperandKind.TEXT }),
      new Option({ short: '-e' }),
      new Option({ short: '-z' }),
      new Option({ short: '-r' }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  comm: new CommandSpec({
    options: [
      new Option({ short: '-1' }),
      new Option({ short: '-2' }),
      new Option({ short: '-3' }),
      new Option({ long: '--check-order' }),
      new Option({ long: '--nocheck-order' }),
    ],
    positional: [new Operand({ kind: OperandKind.PATH }), new Operand({ kind: OperandKind.PATH })],
  }),
  column: new CommandSpec({
    options: [
      new Option({ short: '-t' }),
      new Option({ short: '-s', valueKind: OperandKind.TEXT }),
      new Option({ short: '-o', valueKind: OperandKind.TEXT }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  fold: new CommandSpec({
    options: [
      new Option({ short: '-w', valueKind: OperandKind.TEXT }),
      new Option({ short: '-s' }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  fmt: new CommandSpec({
    options: [new Option({ short: '-w', valueKind: OperandKind.TEXT })],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  cmp: new CommandSpec({
    options: [
      new Option({ short: '-l' }),
      new Option({ short: '-s' }),
      new Option({ short: '-n', valueKind: OperandKind.TEXT }),
      new Option({ short: '-b' }),
      new Option({ short: '-i', valueKind: OperandKind.TEXT }),
    ],
    positional: [new Operand({ kind: OperandKind.PATH }), new Operand({ kind: OperandKind.PATH })],
  }),
  iconv: new CommandSpec({
    options: [
      new Option({ short: '-f', valueKind: OperandKind.TEXT }),
      new Option({ short: '-t', valueKind: OperandKind.TEXT }),
      new Option({ short: '-c' }),
      new Option({ short: '-o', valueKind: OperandKind.PATH }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  strings: new CommandSpec({
    options: [new Option({ short: '-n', valueKind: OperandKind.TEXT })],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  rev: new CommandSpec({ rest: new Operand({ kind: OperandKind.PATH }) }),
  zcat: new CommandSpec({ rest: new Operand({ kind: OperandKind.PATH }) }),
  zgrep: new CommandSpec({
    options: [
      new Option({ short: '-i' }),
      new Option({ short: '-c' }),
      new Option({ short: '-l' }),
      new Option({ short: '-n' }),
      new Option({ short: '-v' }),
      new Option({ short: '-e', valueKind: OperandKind.TEXT, repeatable: true }),
      new Option({ short: '-f', valueKind: OperandKind.PATH, repeatable: true }),
      new Option({ short: '-E' }),
      new Option({ short: '-F' }),
      new Option({ short: '-H' }),
      new Option({ short: '-h' }),
      new Option({ short: '-m', valueKind: OperandKind.TEXT }),
      new Option({ short: '-o' }),
      new Option({ short: '-q' }),
      new Option({ short: '-w' }),
    ],
    positional: [new Operand({ kind: OperandKind.TEXT, providedBy: ['-e', '-f'] })],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  mktemp: new CommandSpec({
    options: [
      new Option({ short: '-d' }),
      new Option({ short: '-p', valueKind: OperandKind.PATH }),
      new Option({ short: '-t' }),
    ],
    positional: [new Operand({ kind: OperandKind.TEXT })],
  }),
  bc: new CommandSpec({
    description: 'Arbitrary precision calculator language.',
    options: [
      new Option({ short: '-l', description: 'Load the standard math library.' }),
      new Option({ short: '-q', description: 'Suppress the welcome banner.' }),
    ],
    rest: new Operand({ kind: OperandKind.TEXT }),
  }),
  expr: new CommandSpec({
    description: 'Evaluate expressions.',
    rest: new Operand({ kind: OperandKind.TEXT }),
  }),
  history: new CommandSpec({
    description: 'Show command history for the session.',
    options: [
      new Option({ short: '-c', description: 'Clear the command history.' }),
      new Option({
        short: '-d',
        valueKind: OperandKind.TEXT,
        description: 'Delete the history entry at the given offset.',
      }),
      new Option({
        short: '-s',
        description: 'Append the args as a single entry without running.',
      }),
      new Option({ short: '-p', description: 'Print the args without storing them.' }),
      new Option({ short: '-a', description: 'Append: no-op (file and store are the same).' }),
      new Option({ short: '-r', description: 'Read: no-op (file and store are the same).' }),
      new Option({ short: '-w', description: 'Write: no-op (file and store are the same).' }),
      new Option({ short: '-n', description: 'Read-new: no-op (file and store are the same).' }),
    ],
    rest: new Operand({ kind: OperandKind.TEXT }),
  }),
  date: new CommandSpec({
    description: 'Print or set the system date and time.',
    options: [
      new Option({
        short: '-d',
        valueKind: OperandKind.TEXT,
        description: 'Display the time described by the given date string.',
      }),
      new Option({ short: '-u', description: 'Use Coordinated Universal Time (UTC).' }),
      new Option({ short: '-I', description: 'Output date in ISO 8601 format.' }),
      new Option({ short: '-R', description: 'Output date in RFC 5322 email format.' }),
    ],
    positional: [new Operand({ kind: OperandKind.TEXT })],
  }),
  csplit: new CommandSpec({
    options: [
      new Option({ short: '-f', valueKind: OperandKind.PATH }),
      new Option({ short: '-n', valueKind: OperandKind.TEXT }),
      new Option({ short: '-b', valueKind: OperandKind.TEXT }),
      new Option({ short: '-k' }),
      new Option({ short: '-s' }),
    ],
    positional: [new Operand({ kind: OperandKind.PATH })],
    rest: new Operand({ kind: OperandKind.TEXT }),
  }),
  expand: new CommandSpec({
    options: [
      new Option({ short: '-t', valueKind: OperandKind.TEXT }),
      new Option({ short: '-i' }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  unexpand: new CommandSpec({
    options: [
      new Option({ short: '-t', valueKind: OperandKind.TEXT }),
      new Option({ short: '-a' }),
    ],
    rest: new Operand({ kind: OperandKind.PATH }),
  }),
  tsort: new CommandSpec({ positional: [new Operand({ kind: OperandKind.PATH })] }),
  look: new CommandSpec({
    options: [new Option({ short: '-f' })],
    positional: [new Operand({ kind: OperandKind.TEXT }), new Operand({ kind: OperandKind.PATH })],
  }),
  sleep: new CommandSpec({
    description: 'Delay for a specified amount of time.',
    rest: new Operand({ kind: OperandKind.TEXT }),
  }),
  bash: new CommandSpec({
    description:
      "Run a command string through Mirage's shell. Only `-c` is meaningful; other flags are accepted and ignored. `bash` and `sh` are aliases.",
    options: [
      new Option({
        short: '-c',
        valueKind: OperandKind.TEXT,
        description: 'Read commands from the next argument and execute them.',
      }),
      new Option({
        short: '-s',
        description: 'Read commands from stdin instead of from an argument.',
      }),
      new Option({
        short: '-l',
        description: '(Ignored) Login shell. Mirage does not source profile files.',
      }),
      new Option({
        short: '-i',
        description: '(Ignored) Interactive flag. Mirage shells are non-interactive.',
      }),
      new Option({ short: '-e', description: '(Ignored) Exit on first error.' }),
      new Option({ short: '-u', description: '(Ignored) Treat unset variables as errors.' }),
      new Option({ short: '-x', description: '(Ignored) Print commands as they execute.' }),
      new Option({ long: '--login', description: '(Ignored) Login shell.' }),
      new Option({ long: '--norc', description: '(Ignored) Skip rc files.' }),
      new Option({ long: '--noprofile', description: '(Ignored) Skip profile files.' }),
      new Option({ long: '--posix', description: '(Ignored) POSIX-conformant mode.' }),
    ],
    rest: new Operand({ kind: OperandKind.TEXT }),
  }),
  join: new CommandSpec({
    options: [
      new Option({ short: '-t', valueKind: OperandKind.TEXT }),
      new Option({ short: '-1', valueKind: OperandKind.TEXT }),
      new Option({ short: '-2', valueKind: OperandKind.TEXT }),
      new Option({ short: '-a', valueKind: OperandKind.TEXT }),
      new Option({ short: '-v', valueKind: OperandKind.TEXT }),
      new Option({ short: '-e', valueKind: OperandKind.TEXT }),
      new Option({ short: '-o', valueKind: OperandKind.TEXT }),
    ],
    positional: [new Operand({ kind: OperandKind.PATH }), new Operand({ kind: OperandKind.PATH })],
  }),
})
