afdata CLI Reference

afdata - A naming convention that lets AI agents understand your data without being told what it means, plus a CLI and library for reading and safely editing structured JSON, TOML, YAML, dotenv, and INI documents.

Commands are grouped into two families: protocol tools that operate on AFDATA protocol-v1 JSON (lint, validate, render), and document tools that read and edit JSON/TOML/YAML/dotenv/INI documents by dot-path (get, value, paths, keys, set, unset, add, remove). Every command’s first positional is its input; - reads stdin. Mutation commands (set/unset/add/remove) never read stdin.

Usage: afdata [OPTIONS] <COMMAND>

Commands:
  lint      Lint a JSON/JSONL stream, a JSON Schema, or a document for deterministic AFDATA issues
  validate  Validate one protocol event or a finite protocol event stream (JSON only)
  render    Render JSON or JSONL through AFDATA output formatting and redaction (JSON only)
  skill     Validate an Agent Skill, or manage the bundled Agent Skill
  get       Read a document as a whole, or the value at a dot-path
  value     Read the scalar at a dot-path as raw bytes on stdout — no AFDATA envelope
  paths     List a container's child dot-paths, one per line — feeds back into afdata
  keys      List a container's child key names or array indices, one per line — for external tools
  set       Set a value at a dot-path, preserving the document's source formatting
  unset     Remove one entry from a document entirely
  add       Add an element to a keyed list (an array of objects addressed by a slug field)
  remove    Remove an element from a keyed list by slug

Options:
      --output <OUTPUT>
          Output format: json, yaml, or plain (help also accepts markdown)

          [default: json]

      --output-to <OUTPUT_TO>
          Where protocol events go: split (default), stdout, or stderr.

          `split` (default, finite one-shot mode) sends `result` to stdout and `error`/`progress`/`log` to stderr, so a shell capture or pipe never mistakes a failure for data. `stdout`/`stderr` (event-stream mode) collapse every event, including `error`, onto that one stream for a consumer that reads it in order and branches on `kind`. Orthogonal to `--output` (which selects format, not destination). A file sink is `--output-to stdout` plus `--stdout-file <PATH>`.

          [default: split]

      --stdout-file <PATH>
          Redirect stdout to this file

      --stderr-file <PATH>
          Redirect stderr to this file

  -h, --help
          Print help. Add --recursive to expand every nested subcommand; add --output json|yaml|markdown to render this help in another format.

  -V, --version
          Print version

AFDATA: 0.18.0

afdata lint - Lint a JSON/JSONL stream, a JSON Schema, or a document for deterministic AFDATA issues

JSON/JSONL input (the default when no document format is detected) keeps its existing dual-mode behavior: a single JSON value, or one value per line. --input-format toml|yaml|yml|dotenv|env|ini (or a recognized file extension) lints a document as a single value instead — the AFDATA naming/suffix rules apply equally there. toml-frontmatter/yaml-frontmatter address only the +++/--- metadata block of a Markdown file, leaving its body untouched (never auto-detected — the format must be named explicitly).

Usage: lint [OPTIONS] <INPUT>

Arguments:
  <INPUT>
          Input file, or `-` for stdin

Options:
      --input-format <FORMAT>
          Document format override; unset means JSON/JSONL unless the file extension names a document format

  -h, --help
          Print help

afdata validate - Validate one protocol event or a finite protocol event stream (JSON only)

Usage: validate [OPTIONS] <INPUT>

Arguments:
  <INPUT>
          Input file, or `-` for stdin

Options:
      --strict
          Enforce the recommended strict protocol profile

      --per-event
          Validate each input value as an independent event, without stream lifecycle rules

  -h, --help
          Print help

afdata render - Render JSON or JSONL through AFDATA output formatting and redaction (JSON only)

Usage: render [OPTIONS] <INPUT>

Arguments:
  <INPUT>
          Input file, or `-` for stdin

Options:
      --secret-name <FIELD>
          Extra field name to redact (beyond the `_secret` suffix convention). Repeatable

  -h, --help
          Print help

afdata skill - Validate an Agent Skill, or manage the bundled Agent Skill

Usage: skill <COMMAND>

Commands:
  validate   Validate a SKILL.md file or skill directory against the Agent Skills spec
  status     Report whether the bundled Agent Skill is installed for each target agent
  install    Install the bundled Agent Skill for each target agent
  uninstall  Uninstall the bundled Agent Skill for each target agent

Options:
  -h, --help
          Print help

afdata skill validate - Validate a SKILL.md file or skill directory against the Agent Skills spec

Usage: validate <INPUT>

Arguments:
  <INPUT>
          SKILL.md file or skill directory, or `-` for SKILL.md text on stdin

Options:
  -h, --help
          Print help

afdata skill status - Report whether the bundled Agent Skill is installed for each target agent

Usage: status [OPTIONS]

Options:
      --agent <AGENT>
          Agent target: all, codex, claude-code, opencode, or hermes

          [default: all]

      --scope <SCOPE>
          Skill scope: personal or workspace

          [default: personal]

      --skills-dir <SKILLS_DIR>
          Explicit skills directory; requires a single concrete --agent

  -h, --help
          Print help

afdata skill install - Install the bundled Agent Skill for each target agent

Usage: install [OPTIONS]

Options:
      --agent <AGENT>
          Agent target: all, codex, claude-code, opencode, or hermes

          [default: all]

      --scope <SCOPE>
          Skill scope: personal or workspace

          [default: personal]

      --skills-dir <SKILLS_DIR>
          Explicit skills directory; requires a single concrete --agent

      --force
          Overwrite a skill this tool did not manage

  -h, --help
          Print help

afdata skill uninstall - Uninstall the bundled Agent Skill for each target agent

Usage: uninstall [OPTIONS]

Options:
      --agent <AGENT>
          Agent target: all, codex, claude-code, opencode, or hermes

          [default: all]

      --scope <SCOPE>
          Skill scope: personal or workspace

          [default: personal]

      --skills-dir <SKILLS_DIR>
          Explicit skills directory; requires a single concrete --agent

      --force
          Remove a skill this tool did not manage

  -h, --help
          Print help

afdata get - Read a document as a whole, or the value at a dot-path

With no KEY, emits {"code":"document","format":...,"value":...} — the whole document. With KEY, adds "key" and narrows "value" to that dot-path. _secret-suffixed fields (and any --secret-name) are redacted to "***" anywhere in the output, including a directly-targeted secret leaf — use value --reveal-secret to read a secret’s real value.

Usage: get [OPTIONS] <FILE> [KEY]

Arguments:
  <FILE>
          Document file, or `-` for stdin

  [KEY]
          Dot-separated key path (`\.` escapes a literal dot, `\\` a backslash); omit for the whole document

Options:
      --input-format <FORMAT>
          Document format override; unset means extension detection

      --secret-name <FIELD>
          Extra field name to redact (beyond the `_secret` suffix convention). Repeatable

  -h, --help
          Print help

afdata value - Read the scalar at a dot-path as raw bytes on stdout — no AFDATA envelope

Only scalars (string/bool/integer/float/null) are supported; arrays and objects are rejected, as are non-finite floats. A secret-named leaf is rejected unless --reveal-secret is passed. On failure, stdout is always empty — the error envelope goes to stderr instead (so x=$(afdata value f k) never captures a JSON error as data).

Usage: value [OPTIONS] <FILE> <KEY>

Arguments:
  <FILE>
          Document file, or `-` for stdin

  <KEY>
          Dot-separated key path

Options:
      --reveal-secret
          Print a secret-named scalar instead of erroring

      --default <VALUE>
          Print this instead of erroring when KEY's path does not exist or its value is null (an empty string is a real value and does not trigger the default)

      --input-format <FORMAT>
          Document format override; unset means extension detection

      --secret-name <FIELD>
          Extra field name to redact (beyond the `_secret` suffix convention). Repeatable

  -h, --help
          Print help

afdata paths - List a container’s child dot-paths, one per line — feeds back into afdata

With no KEY, enumerates the document’s top-level children. Each line is a full dot-path from the root (grammar-escaped), so it can be piped straight back into get/value/unset/… or extended with "$p.field". A scalar leaf (nothing to enumerate) is an error, the dual of value. On failure, stdout is always empty (same contract as value). Rejects --output json — read a container’s structured JSON via get instead.

Usage: paths [OPTIONS] <FILE> [KEY]

Arguments:
  <FILE>
          Document file, or `-` for stdin

  [KEY]
          Dot-separated key path to the container; omit for the top level

Options:
      --input-format <FORMAT>
          Document format override; unset means extension detection

      --missing-ok
          Empty output + exit 0 when KEY's path does not exist (other errors still fail)

  -0, --null
          Separate lines with NUL instead of newline (for `xargs -0`/`read -d ''`)

  -h, --help
          Print help

afdata keys - List a container’s child key names or array indices, one per line — for external tools

The dual of paths: raw, unescaped, unprefixed key names/indices — exactly what a package manager or another tool expects (lodash.merge, not dependencies.lodash\.merge). Never feed this back into afdata’s own dot-path arguments; use paths for that. Otherwise identical contract to paths (KEY, --input-format, --missing-ok, -0/--null, scalar-leaf error, empty stdout on failure, rejects --output json).

Usage: keys [OPTIONS] <FILE> [KEY]

Arguments:
  <FILE>
          Document file, or `-` for stdin

  [KEY]
          Dot-separated key path to the container; omit for the top level

Options:
      --input-format <FORMAT>
          Document format override; unset means extension detection

      --missing-ok
          Empty output + exit 0 when KEY's path does not exist (other errors still fail)

  -0, --null
          Separate lines with NUL instead of newline (for `xargs -0`/`read -d ''`)

  -h, --help
          Print help

afdata set - Set a value at a dot-path, preserving the document’s source formatting

A bare VALUE is always a string — zero coercion, so 007 or a leading-zero-bearing ID is never silently reinterpreted. Overwriting an existing scalar of a different type with a bare VALUE is an argument error (pass --value-type to keep the type, or --value-type string to convert explicitly); a brand-new key never needs --value-type. --value-type json is the only entry point for arrays, objects, and an exact-type scalar. Idempotency: setting an already-current value is not special-cased — it just writes the same value again.

Usage: set [OPTIONS] <FILE> <KEY> [VALUE]

Arguments:
  <FILE>
          Document file to mutate in place (never reads stdin; rejects `-`)

  <KEY>
          Dot-separated key path

  [VALUE]
          Value to write; interpreted per `--value-type` (default: string, zero coercion)

Options:
      --value-type <TYPE>
          Exact type for VALUE: string (default), number, bool, null, or json

      --secret-from <SRC>
          Read a secret string VALUE from stdin, the controlling terminal, an inherited file descriptor, or an environment variable: stdin|prompt|fd:<N>|env:<VAR>

      --input-format <FORMAT>
          Document format override; unset means extension detection

  -h, --help
          Print help

afdata unset - Remove one entry from a document entirely

Idempotency: removing an absent KEY is an error (document_path_not_found), not a no-op — script around it with afdata unset ... || true if absence should be silent.

Usage: unset [OPTIONS] <FILE> <KEY>

Arguments:
  <FILE>
          Document file to mutate in place (never reads stdin; rejects `-`)

  <KEY>
          Dot-path to the entry to remove

Options:
      --input-format <FORMAT>
          Document format override; unset means extension detection

  -h, --help
          Print help

afdata add - Add an element to a keyed list (an array of objects addressed by a slug field)

Extra FIELD=VALUE pairs are always strings (the same zero-coercion rule as set’s bare VALUE — add does not invent its own type syntax; write an exact type afterwards with set --value-type). Idempotency: adding a SLUG that already exists is an error (document_slug_exists), not a no-op or overwrite.

Usage: add [OPTIONS] --slug-field <SLUG_FIELD> <FILE> <KEY> <SLUG> [FIELD=VALUE]...

Arguments:
  <FILE>
          Document file to mutate in place (never reads stdin; rejects `-`)

  <KEY>
          Dot-path to the keyed list

  <SLUG>
          Slug/ID for the new element

  [FIELD=VALUE]...
          Additional `FIELD=VALUE` pairs to set on the new element (always strings)

Options:
      --slug-field <SLUG_FIELD>
          Field name that identifies each element (the slug field)

      --input-format <FORMAT>
          Document format override; unset means extension detection

  -h, --help
          Print help

afdata remove - Remove an element from a keyed list by slug

Idempotency: removing a SLUG that does not exist is an error (document_slug_not_found), not a no-op.

Usage: remove [OPTIONS] --slug-field <SLUG_FIELD> <FILE> <KEY> <SLUG>

Arguments:
  <FILE>
          Document file to mutate in place (never reads stdin; rejects `-`)

  <KEY>
          Dot-path to the keyed list

  <SLUG>
          Slug/ID of the element to remove

Options:
      --slug-field <SLUG_FIELD>
          Field name that identifies each element (the slug field)

      --input-format <FORMAT>
          Document format override; unset means extension detection

  -h, --help
          Print help