machinemode.io
/
aoi‑cli · v0.2 · draft

AOI‑CLI#

The Agent-Operable Interface · Command‑Line Profile

The first profile of the Machine Mode standard. A typed, streamable, discoverable, bounded, safe contract for command‑line tools used by autonomous systems.


Executive summary#

AOI‑CLI is the command‑line profile of the Machine Mode standard. A conforming tool exposes:

A stable machine mode that emits UTF‑8 newline‑delimited JSON event objects to stdout, never mixes human prose into that stream, provides a credential‑free schema and capability discovery command, uses a standard event envelope and error taxonomy, has explicit completion and failure semantics, and makes side effects safe, bounded, and retry‑aware.

The analogy is not "POSIX for agents." POSIX standardizes operating‑system interfaces. AOI is closer to OpenAPI, AsyncAPI, or gRPC/protobuf for command‑line tools: a typed interface contract for local process execution.

Roles in a pipeline#

An AOI‑CLI tool plays one of three roles depending on whether it consumes JSONL input, produces JSONL output, or both:

RoleReads JSONL on stdin?Emits JSONL on stdout?Example
Sourcenoyesoutline search … produces hits
Transformeryesyessummarize --input-jsonl - reads hits, emits abstracts
Sinkyesyes (audit + summary only)outline import --input-jsonl - performs side effects

Every conforming tool — source, transformer, or sink — still emits meta first and a terminal summary last, and follows every other characteristic of the standard. The role only describes the tool's position in a pipeline; it is not a separate conformance category.

The spec uses these terms throughout where the producer/consumer distinction matters: § 12 Input contract applies to transformers and sinks; § 13 Pipeline composition describes how the three roles compose.

Relationship to Machine Mode#

Machine Mode is the public concept and the manifesto: the standard for agent‑operable tools. AOI is the formal specification of that standard. AOI‑CLI, defined in this document, is the first formal profile — it specifies how the ten Machine Mode characteristics (Typed, Discoverable, Streamable, Bounded, Safe, Idempotent, Auditable, Verifiable, Composable, Versioned) apply at the command‑line process boundary.

Forthcoming profiles for other interface surfaces, alternate modes beyond batch invocation (such as a JSON‑RPC session mode), alternative wire formats, and the relationships to adjacent standards (OpenAPI, MCP) are described in Direction — beyond v0.2. Nothing in that document is normative for v0.2.

Design goals#

A conforming AOI‑CLI tool is:

  • Stable — explicit compatibility contract for machine output.
  • Discoverable — schema and capabilities can be fetched without credentials, network, or side effects.
  • Parseable — machine mode stdout is only JSONL event objects.
  • Streamable — consumers can process records incrementally.
  • Verifiable — successful completion, partial failure, truncation, and crashes have defined semantics.
  • Composable — pipes and process exit status behave predictably.
  • Safe — read‑only by default, destructive work explicit, secrets redacted.
  • Bounded — large reads and searches have limits, cursors, and truncation signals.
  • Retry‑aware — errors say whether retry may help; writes can be idempotent.
  • Language‑neutral — no Python or Nushell‑specific behavior is normative.

Non‑goals#

AOI‑CLI does not standardize:

  • operating‑system process APIs,
  • package‑manager layouts,
  • programming‑language frameworks,
  • human CLI UX,
  • transport protocols beyond local process stdin/stdout/stderr,
  • a universal schema‑registry requirement.

Normative keywords#

The words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are used in the RFC 2119 sense.

Relationship to existing standards#

AOI‑CLI builds on existing conventions:

AOI‑CLI is a typed CLI interface convention, not an OS standard.


1. Machine mode#

A conforming tool MUST expose at least one stable machine mode that emits AOI events.

Recommended invocation pattern:

outline --output jsonl ...

or equivalently:

outline --format jsonl ...

A compatibility wrapper such as outline-jsonl MAY exist, but the suffix is not the normative contract.

1.1 Machine‑mode stability#

If a tool advertises AOI conformance, its machine mode MUST be a stable, schema‑governed API. It is not "best effort JSON." The project MUST document compatibility rules for event types, field names, field types, and versioning.

1.2 Human mode#

The default unsuffixed command MAY remain human‑readable. Human output MAY include tables, color, progress bars, and prose. Machine mode MUST NOT.

1.3 Interactive flows#

Machine mode is non‑interactive by contract. A conforming tool MUST NOT prompt for input, open a browser, or otherwise require human attention while running in machine mode. This is what makes the contract callable from an agent or a non‑terminal automation context.

Interactive flows that a CLI legitimately needs — OAuth authorization, first‑run config, MFA approval, interactive credential prompts — belong in the unsuffixed human‑mode invocation. The human runs tool auth login once, completes the flow, and the resulting credential lands in the user's existing credential ecosystem (keychain, config file, environment, kubeconfig, ssh agent, etc.). After that, subsequent machine‑mode calls inherit the credential by inheriting the process environment.

If a machine‑mode invocation reaches a point where it would otherwise prompt, it MUST instead emit a structured error:

{
  "type": "aoi:error",
  "category": "authn",
  "code": "INTERACTIVE_REQUIRED",
  "message": "Interactive authentication required. Run `outline auth login` to complete the flow.",
  "retryable": false,
  "hint": "outline auth login"
}

The hint field is informational only; consumers MUST NOT parse it as a command to execute automatically. A reasonable agent surfaces the hint to a human operator.

This carve‑out is what lets AOI work with the full breadth of real auth ecosystems (browser‑bound OAuth, hardware MFA, enterprise SSO) without dragging interactive‑flow handling into the wire protocol.

2. JSONL event stream#

Machine‑mode stdout MUST be UTF‑8 newline‑delimited JSON.

Each line MUST be one complete JSON object.

Every event object MUST include:

{"type":"..."}

Machine‑mode stdout MUST NOT include:

  • banners,
  • markdown headings,
  • color or ANSI escape codes,
  • progress bars,
  • human prose,
  • stack traces,
  • mixed plain‑text warnings.

Recoverable diagnostics belong in structured warning events. Fatal diagnostics should be structured error events when possible; usage errors may use stderr before stream startup.

3. Discovery without credentials or network#

A conforming tool MUST provide a credential‑free, network‑free way to discover its schema and capabilities.

Recommended subcommands:

outline schema --output json
outline capabilities --output json

Equivalent flags MAY exist:

outline --schema
outline --capabilities

If both flags and subcommands exist, they MUST return equivalent information.

3.1 Schema sidecars#

Sidecar schema files MAY be provided but MUST NOT be required for conformance.

A sidecar is a packaging convenience, not the wire contract. Real installation layouts include npm/pnpm shims, Homebrew Cellar paths, immutable Nix store paths, single‑file Go/Rust binaries, container images with stripped filesystems, FHS share/<package>/ resources, and embedded schemas.

Consumers MUST be able to rely on the schema and capability command. They MAY opportunistically read local sidecars when available.

3.2 Schema command requirements#

schema MUST NOT require:

  • authentication,
  • config files beyond local install metadata,
  • network access,
  • environment secrets,
  • a valid business query.

Startup cost is acceptable.

4. Completion semantics#

A normal finite operation SHOULD emit meta first and MUST emit a terminal summary event before exiting 0.

A consumer MUST treat EOF without a terminal summary as an incomplete stream unless the operation is explicitly documented as unbounded.

A producer that crashes cannot guarantee a terminal sentinel. Therefore the absence of summary on EOF is the cross‑language crash and truncation signal.

4.1 Finite streams#

For finite commands such as list, search, get, create, and doctor:

  • exit 0 + terminal summary.ok=true means success,
  • exit 0 without terminal summary MUST be treated as protocol failure,
  • non‑zero exit with partial events MUST be treated as failure unless the caller explicitly requested partial tolerance,
  • summary.partial=true MAY indicate successful partial results when partial is a valid outcome.

4.2 Unbounded streams#

For commands like watch, tail, or event subscriptions:

  • the command MUST document that the stream is unbounded,
  • it MAY emit heartbeat or checkpoint events,
  • on graceful cancellation it SHOULD emit summary.ok=false with reason:"interrupted" when possible,
  • consumers cannot require a terminal summary until EOF.

5. Versioning#

AOI versioning has three separate concepts and they MUST NOT be conflated:

  1. Tool version — implementation release, e.g. outline 1.8.2.
  2. AOI version — the version of this interface convention, e.g. aoi_version: "0.2".
  3. Schema version — the version of a specific event/input schema, e.g. schema_version: "1.0.0".

The meta event SHOULD include all three when known:

{
  "type": "aoi:meta",
  "tool": "outline",
  "tool_version": "1.8.2",
  "aoi_version": "0.2",
  "schema_name": "com.example.outline.events",
  "schema_version": "1.0.0"
}

5.1 Breaking changes#

A breaking machine‑output change MUST change schema_version major version.

A tool MAY expose multiple schema versions through one binary:

outline --output jsonl --schema-version 1 ...
outline --output jsonl --schema-version 2 ...

or through content negotiation:

outline --output jsonl --accept-schema com.example.outline.events@2

5.2 Rollout rule#

Consumers SHOULD branch on schema_name and schema_version, not executable name.

6. Exit status#

AOI does not invent low‑number exit meanings that conflict with existing conventions.

Required baseline:

  • 0 — process completed successfully at the process layer. For finite streams, a terminal summary.ok=true is still required for application‑level success.
  • Non‑zero — process failed or was interrupted. Inspect structured events if present.

Recommended mapping using sysexits‑compatible codes:

CodeConstantMeaning
64EX_USAGEusage error
65EX_DATAERRinput or data validation error
69EX_UNAVAILABLEservice or dependency unavailable
70EX_SOFTWAREinternal software error
73EX_CANTCREATcannot create output
74EX_IOERRI/O error
75EX_TEMPFAILtemporary failure, retry may succeed
77EX_NOPERMpermission or authz failure
78EX_CONFIGconfiguration error
124timeout (when produced by timeout wrappers or documented)
130interrupted by SIGINT
141SIGPIPE / downstream closed pipe

Tools on platforms without sysexits MAY use local conventions but MUST document them. Consumers MUST NOT rely on exit code alone for application semantics when structured summary or error events are available.

7. Signals and cancellation#

All AOI implementations SHOULD exit cleanly on SIGPIPE/downstream pipe close without printing stack traces to stderr. This requirement is language‑neutral.

On SIGINT/SIGTERM, a tool SHOULD emit, if safe and possible:

{"type":"aoi:summary","ok":false,"reason":"interrupted","partial":true}

then exit with the conventional signal code. If emitting a final summary would corrupt state or hang, exiting promptly is more important.


8. Event model#

8.1 Common envelope#

Every event MUST include type.

Recommended fields:

FieldMeaning
typeevent discriminator — required
schema_namestable event schema name — recommended in meta
schema_versionevent schema version — recommended in meta
toolstable tool name — recommended in meta
tool_versiontool release — recommended in meta
aoi_versionAOI version — recommended in meta
timestampISO 8601 timestamp when useful
trace_idcorrelates events from one run when useful

8.2 Type namespace#

Event type values live in one of three namespaces, and the namespace is visible on the wire by prefix convention.

LayerConventionExamples
Framework events (defined by this spec)aoi: prefixaoi:meta, aoi:summary, aoi:warning, aoi:error, aoi:heartbeat, aoi:plan, aoi:check, aoi:progress
Recommended domain patterns (common shapes for common operations; each tool's schema owns them)unprefixedentry, hit, match
Tool‑specific domain eventsunprefixed; scoped by meta.schema_namewhatever the tool defines, e.g. created, deleted, abstract, compiled, transcoded

Rationale:

  • The aoi: prefix on framework events prevents wire‑level collision with domain events that share generic words. A tool that emits a textual document summary ({"type":"summary","text":"…"}) does not conflict with the framework's terminal {"type":"aoi:summary","ok":true,…} — they coexist in the same stream without ambiguity.
  • Domain types stay unprefixed because they are already scoped by the schema declared in meta.schema_name. The full coordinate for a domain event is schema_name#type (e.g. com.example.outline.events#hit).
  • The visual asymmetry is a feature, not an inconsistency. A consumer reading a stream knows at a glance whether a line is framework control‑plane (parser cares) or domain data‑plane (domain code cares). grep aoi: extracts control events from a tee'd log.
  • No aliases. A framework type is always written with the aoi: prefix. Aliases would re‑introduce the ambiguity the prefix exists to prevent.

Framework type names are reserved: a domain schema MUST NOT define an unprefixed event type with a framework name (e.g. a domain event type: "meta" is forbidden — the domain may use meta only as the aoi:meta framework event). Domain events may otherwise use any unreserved name.

Verbosity cost: 4 bytes per framework event. A typical AOI stream emits ~5 framework events per invocation, so total overhead is ~20 bytes per stream — negligible against the clarity benefit.

8.3 Standard event types#

aoi:meta#

First successful event for finite commands. Describes schema, command, version, and source context. MUST NOT echo raw argv or environment wholesale.

{
  "type": "aoi:meta",
  "tool": "outline",
  "tool_version": "1.8.2",
  "aoi_version": "0.2",
  "schema_name": "com.example.outline.events",
  "schema_version": "1.0.0",
  "command": "search"
}

entry#

One resource in a list or inventory.

hit#

One search result. Should include source, stable ID/path/URL, rank/score where meaningful, and snippet/text.

match#

One exact match inside a source, such as a file line match. Recommended fields: path, line_number, column, text, submatches.

aoi:check#

One readiness or doctor check.

{
  "type": "aoi:check",
  "name": "config_file",
  "ok": true,
  "severity": "info",
  "detail": "found"
}

aoi:plan#

One planned side effect, usually emitted during --dry-run or before apply.

aoi:progress#

Long‑running operation progress. Use sparingly. Progress events MUST NOT be required for correctness.

aoi:heartbeat#

Optional for unbounded streams. Lets consumers distinguish a quiet but alive stream from an idle transport when process supervision is not enough.

aoi:warning#

Recoverable issue. The operation continues.

aoi:error#

Structured failure. May be item‑level or process‑level.

aoi:summary#

Terminal event for finite commands. Required for process‑level success.

{
  "type": "aoi:summary",
  "ok": true,
  "count": 42,
  "warning_count": 1,
  "error_count": 0,
  "partial": false,
  "truncated": false,
  "next_cursor": null,
  "elapsed_ms": 384
}

9. Error taxonomy#

AOI needs portable retry and handling logic. Tools MAY define domain‑specific codes, but SHOULD map them to a standard category.

{
  "type": "aoi:error",
  "code": "FILE_NOT_FOUND",
  "category": "not_found",
  "message": "No file exists at path.",
  "retryable": false,
  "target": "/Files/kubo/missing.md"
}

9.1 Standard categories#

CategoryRetry?Meaning
usagenoInvalid CLI usage or missing required operand.
validationnoInput parsed but failed schema/domain validation.
authnmaybeMissing or expired credentials. Retry after reauth.
authznoCredentials valid but action not allowed.
not_foundnoTarget resource does not exist.
conflictmaybeETag/version/precondition/destination conflict.
rate_limitedyesRate limit or quota throttling. Include retry hint if known.
temporaryyesTransient dependency/network/service failure.
timeoutyesOperation exceeded timeout.
cancelledmaybeInterrupted by caller or system.
partialmaybeSome items failed; inspect item errors.
internalmaybeTool bug or unexpected exception.
confignoMissing or invalid configuration.
iomaybeFilesystem/stdin/stdout I/O issue.

Domain‑specific code values should be stable and uppercase snake case. Portable consumers should branch on category and retryable first.


10. Schema rules#

10.1 Discovery#

The schema command SHOULD emit JSON Schema to stdout:

outline schema --output json

It MAY support:

outline schema --output json --schema-version 1
outline schema --output json --name com.example.outline.events

10.2 $id#

Schema $id MUST NOT be a machine‑local file URI unless the schema is explicitly local‑only.

Preferred:

{
  "$id": "https://schemas.example.com/outline/events/1.0.0/schema.json"
}

10.3 Strictness#

Recommended policy:

  • Event/output schemas SHOULD be permissive for forward‑compatible extension: allow unknown fields unless a specific event class has a reason to forbid them.
  • Input schemas SHOULD be strict: reject unknown fields to catch agent typos and hallucinated parameters.

10.4 Event discriminators#

Prefer discriminator‑style schemas using type with if/then or $defs keyed by event type. Avoid examples that produce opaque oneOf matched zero schemas errors.


11. Confidentiality and argument redaction#

meta.args is optional and dangerous. A conforming framework SHOULD NOT echo raw argv by default.

If arguments are included, the tool MUST redact sensitive values.

Sensitive patterns include at least:

  • flags containing token, secret, password, passwd, key, credential, cookie, authorization, auth, bearer, private, client-secret,
  • URLs with credentials, tokens, signatures, SAS parameters, or query keys such as sig, token, access_token, code, key,
  • custom headers,
  • environment variable values,
  • config file contents.

Preferred meta pattern:

{
  "type": "aoi:meta",
  "command": "search",
  "input_fingerprint": "sha256:...optional...",
  "args_redacted": true
}

The caller already knows what it passed. Do not leak secrets for convenience.


12. Input contract#

Output typing is not enough. Tools acting as transformers or sinks (those that consume JSONL on stdin) need equal rigor on the input side.

12.1 Input modes#

A tool MAY accept:

  • ordinary CLI operands and options,
  • stdin text via -,
  • JSON input via --input-json -,
  • JSONL input via --input-jsonl -,
  • file paths.

If accepting structured stdin, the tool SHOULD provide an input schema:

outline input-schema --output json --command import

12.2 Malformed JSONL input#

For JSONL input streams, tools MUST document whether failures are:

  • whole‑input fail‑fast — any malformed line aborts the whole command,
  • per‑line tolerant — malformed lines emit item‑level errors and processing continues,
  • configurable--fail-fast / --continue-on-error.

Recommended default:

  • Mutating imports — fail fast before side effects where possible.
  • Read‑only transforms — item‑level errors may continue if --continue-on-error is set.

13. Pipeline composition#

An AOI pipeline composes sourcestransformerssinks. A source produces events. A transformer reads upstream events and produces its own. A sink reads events and produces side effects (with a corresponding audit trail in its own output). Any role may be a conforming AOI‑CLI tool; standard shell pipes are the canonical transport.

A transformer or sink that accepts AOI JSONL input SHOULD:

  • accept and parse upstream meta, warning, error, and summary events,
  • ignore unknown event types unless explicitly configured to fail,
  • decide whether upstream errors are data or control signals.

Recommended default:

  • Upstream entry, hit, match, and domain events are data.
  • Upstream warning events are propagated or counted.
  • Upstream error events cause downstream summary.ok=false unless --continue-on-error is set.
  • Upstream missing terminal summary causes downstream failure if EOF occurs.

13.1 Pipefail#

Shell pipelines should use process exit status and terminal summary checks together:

set -o pipefail
producer --output jsonl ... \
  | consumer --input-jsonl - --output jsonl ... \
  | tee output.jsonl \
  | jq -e 'select(.type=="aoi:summary") | .ok == true'

A library wrapper should validate:

  • every stdout line is valid JSON,
  • EOF includes terminal summary for finite commands,
  • process exit is acceptable,
  • summary.ok is true unless partial success was explicitly accepted.

14. Safety and side effects#

14.1 Read‑only by default#

Commands SHOULD default to read‑only behavior. Mutating operations must be obvious in the command or options.

14.2 Dry‑run#

Non‑trivial mutating operations SHOULD support --dry-run and emit plan events.

14.3 Confirmation#

Destructive operations MUST require explicit confirmation such as --confirm.

Bulk destructive operations SHOULD require --confirm-count N or an equivalent guard to prevent accidental broad deletes.

14.4 Idempotency#

Retryable writes SHOULD support:

--idempotency-key KEY

or a documented natural key.

14.5 Auditability#

Mutating operations SHOULD emit stable IDs/paths/URLs for resources created, updated, or deleted.


15. Bounds, pagination, and truncation#

List, search, and read operations SHOULD be bounded by default. Recommended options:

--limit N
--cursor TOKEN
--timeout SECONDS
--max-bytes N
--max-lines N

If output is truncated, summary.truncated MUST be true and summary.next_cursor SHOULD be set when more data can be fetched.

{"type":"aoi:summary","ok":true,"count":100,"truncated":true,"next_cursor":"opaque-token"}

16. Standard CLI surface#

16.1 Global options#

OptionStatusMeaning
--help, -hMUSTHuman‑readable help.
--versionSHOULDTool version.
--output jsonl / --format jsonlMUSTEnter AOI JSONL mode unless the tool is AOI‑only.
--schema-version VERSIONSHOULDSelect supported schema version where applicable.
--timeout SECONDSSHOULDBound external calls.
--limit NSHOULDBound result count.
--cursor TOKENSHOULDContinue from prior cursor.
--dry-runSHOULDPlan without side effects.
--idempotency-key KEYSHOULDSafe retry key.
--confirmMUSTRequired for destructive operations.
--confirm-count NSHOULDGuard expected affected count.
--no-colorSHOULDDisable color. Machine mode never uses color.
--debugMAYExtra diagnostics, redacted, not corrupting JSONL stdout.

16.2 Discovery subcommands#

SubcommandMeaning
schemaEmit event schema JSON. Credential‑free and network‑free.
input-schemaEmit input schema JSON for a command or input mode.
capabilitiesEmit supported commands, schema versions, event types, input modes, auth requirements.
doctorCheck credentials, dependencies, connectivity. Emits AOI events in machine mode.

16.3 Resource subcommands#

Use boring verbs:

SubcommandMeaning
listBounded collection.
getOne resource by stable key.
searchRanked or filtered results.
createCreate resource.
updateUpdate resource.
deleteDelete resource. Confirmation required.
moveMove or rename resource.
copyCopy resource.
sendSend message, email, notification.
syncReconcile state. Must state direction and planned changes.
watchUnbounded stream. Must document completion semantics.
statusCurrent state.

16.4 Flag names are conventions, not prefixed reservations#

The flag and subcommand names this section lists — --output, --format, --limit, --cursor, --dry-run, --confirm, --idempotency-key, schema, capabilities, doctor, and so on — are conventions intended to make a conforming tool feel the same to an agent across every implementation. They are deliberately not namespaced (e.g. --aoi-output, aoi-schema).

This is a different design call from the one made for event types (§ 8.2). Event types appear inline in the output stream where parser ambiguity is real; the aoi: prefix prevents collision with domain payloads. Flag names appear in the input the operator or agent writes; collisions there are configuration questions, not parse questions, and the per‑invocation byte cost of a prefix would land on every call.

Where a tool already uses a flag name AOI also defines (for example, an existing --output FILE for an output destination), the tool MAY:

  • Use the listed equivalent — e.g. --format jsonl instead of --output jsonl (already a recommended alternate in § 16.1).
  • Document a tool‑specific alias and advertise both in capabilities.
  • Resolve the conflict locally however it sees fit; AOI does not mandate a global rename.

The standard's leverage comes from consistent flag names where possible, not from prefixed reservations. Tools whose existing surface conflicts with AOI's recommended names should use the listed alternates rather than ship --aoi-output, which would defeat the convention‑is‑the‑value‑proposition that motivates AOI's flag vocabulary in the first place.

The same logic applies to subcommand names. A tool whose domain already includes a schema subcommand (a database CLI managing literal database schemas) may rename its conformance discovery to aoi-schema and document the alias; AOI does not prescribe a global subcommand prefix.


17. Capability manifest#

capabilities --output json SHOULD return a single JSON object, not JSONL, because it is discovery metadata.

{
  "tool": "outline",
  "tool_version": "1.8.2",
  "aoi_versions": ["0.2"],
  "outputs": ["jsonl"],
  "schemas": [
    {
      "name": "com.example.outline.events",
      "versions": ["1.0.0", "2.0.0"],
      "default": "2.0.0"
    }
  ],
  "commands": [
    {
      "name": "search",
      "read_only": true,
      "bounded": true,
      "supports_cursor": true,
      "event_types": ["aoi:meta", "hit", "aoi:warning", "aoi:error", "aoi:summary"]
    },
    {
      "name": "delete",
      "read_only": false,
      "destructive": true,
      "requires_confirm": true,
      "supports_dry_run": true,
      "supports_idempotency_key": true
    }
  ]
}

18. Conformance — aoi-lint#

18.1 Per‑characteristic, not per‑level#

Conformance in AOI is asserted per characteristic and per command, not as an aggregate level. A tool is Typed for search, or Safe for delete, or not yet Auditable. There is no "AOI Level 2"; there is the set of (characteristic × command) pairs the tool passes.

This is intentional. A read‑only tool that lacks Idempotent is still a legitimate, useful, conforming tool — there is nothing to be idempotent about. Aggregating characteristics into levels would either hide that nuance or force every tool to implement requirements it doesn't need. The granularity is the truth; aoi-lint reports it.

18.2 Lint checks#

aoi-lint runs the following minimum checks against a command invocation. Each one maps to one or more characteristics:

#CheckCharacteristic(s)
1schema --output json succeeds without credentials or network. Schema is valid JSON Schema. capabilities --output json succeeds if advertised.Discoverable
2Machine mode emits only valid JSONL objects to stdout. Every event has type. No ANSI/control/prose pollution.Typed
3Finite success exits 0 with terminal summary.ok=true. EOF without summary is detected as failure.Verifiable
4aoi:meta, aoi:summary, aoi:warning, aoi:error, and aoi:check events validate against the declared schema when emitted.Typed · Verifiable
5Malformed usage exits non‑zero. Structured errors include category, code, message, and retryable when emitted.Verifiable
6Destructive commands refuse to run without confirmation. Dry‑run emits plan events and performs no side effects when advertised.Safe
7Mutations accept an --idempotency-key and replay returns the prior result without doubling the effect.Idempotent
8Mutating events carry stable IDs for created/updated/deleted resources.Auditable
9Known secret‑looking args are not echoed raw in output.Safe
10Early pipe close does not produce stack traces. SIGINT/SIGTERM produce an interrupted summary when safe.Composable
11Commands advertising --limit report truncated and cursor behavior consistently.Bounded · Streamable
12Malformed input JSONL line handling matches declared mode.Composable
13schema_version is reported in meta and is honored when negotiated.Versioned

A tool is conforming for the (characteristic × command) pairs it passes. It claims conformance via the Machine Mode Ready badge and the entries in its capabilities manifest. Verification is aoi-lint's job — the badge is the claim, not the proof.


19. Examples#

outline search "agent operable" --output jsonl --limit 2
{"type":"aoi:meta","tool":"outline","tool_version":"1.8.2","aoi_version":"0.2","schema_name":"com.example.outline.events","schema_version":"1.0.0","command":"search"}
{"type":"hit","rank":1,"id":"doc_123","title":"Agent-Operable Tools","snippet":"A conforming tool exposes a stable interface..."}
{"type":"aoi:summary","ok":true,"count":1,"warning_count":0,"error_count":0,"partial":false,"truncated":false}

Consumer rule: if EOF arrives before the summary, the stream failed.

19.2 Schema discovery#

outline schema --output json --schema-version 1

Returns JSON Schema to stdout. No credentials. No network. No business query.

19.3 Doctor#

outline doctor --output jsonl
{"type":"aoi:meta","tool":"outline","aoi_version":"0.2","schema_name":"com.example.outline.events","schema_version":"1.0.0","command":"doctor"}
{"type":"aoi:check","name":"config_file","ok":true,"severity":"info","detail":"found"}
{"type":"aoi:check","name":"api_token","ok":false,"severity":"error","detail":"missing"}
{"type":"aoi:summary","ok":false,"count":2,"error_count":1}

19.4 Input JSONL error#

outline import --input-jsonl - --output jsonl --continue-on-error
{"type":"aoi:meta","tool":"outline","command":"import","input_mode":"jsonl","continue_on_error":true}
{"type":"aoi:error","category":"validation","code":"INPUT_JSONL_PARSE_ERROR","line_number":17,"message":"Invalid JSON on input line 17","retryable":false}
{"type":"aoi:summary","ok":false,"count":29,"error_count":1,"partial":true}

20. Implementation guidance (non‑normative)#

Python#

  • Compact JSON per line.
  • Handle BrokenPipeError/SIGPIPE cleanly.
  • Do not dump raw argparse.Namespace into meta.
  • Usage errors can exit before meta.

Node / Bun#

  • Handle EPIPE on stdout and stderr.
  • Avoid console logging in machine mode except through the event emitter.
  • Ensure async streams flush before exit when emitting terminal summary.

Go#

  • Treat broken pipe as normal termination.
  • Keep event structs versioned.
  • Avoid logging to stdout in machine mode.

Rust#

  • Handle BrokenPipe without panic output.
  • Serialize event enums with type discriminator.
  • Keep stderr diagnostics redacted.

Shell wrappers#

  • Prefer wrapping upstream JSON APIs over parsing human tables.
  • Use jq -c for emitted objects.
  • Keep stderr separate.
  • Be careful with set -o pipefail and partial output.

21. Anti‑patterns#

  • --json sometimes emits prose, warnings, or pretty tables.
  • Machine output is a giant JSON array, blocking all results until completion.
  • Schema discovery requires login, network, or a real query.
  • Schema $id is file:///opt/data/bin/... in a public spec.
  • Raw argv or env is echoed into meta.
  • Consumers treat EOF without summary as success.
  • Exit code alone is used as application semantics.
  • Every tool invents error codes with no category mapping.
  • Destructive bulk operations run without preview or count confirmation.
  • Language‑specific stack traces appear on pipe close.
  • Wrapper names become the only versioning mechanism.

22. Adoption path#

  1. Tighten this into a public AOI-CLI.md specification.
  2. Build aoi-lint around testable conformance checks.
  3. Provide reference implementations in Python, Node, Go, and Rust.
  4. Local *-jsonl wrappers remain a useful convenience profile, not the universal rule.
  5. Publish a small example repo with schema discovery, finite search, doctor checks, input JSONL import, cancellation/pipe tests, and redaction tests.


23. Open questions#

  • Should --output jsonl or --format jsonl be the preferred flag name?
  • Should summary be mandatory for every finite non‑zero failure once meta has emitted, or only best effort?
  • How strict should the default capabilities schema be?
  • Should standard error categories be a fixed enum or a recommended registry?
  • Should schema emit all event schemas by default or require a schema name?
  • Should the protocol define a standard watch heartbeat interval hint?

Open questions concerning forthcoming modes, profiles, and adjacent‑standard relationships are tracked separately in Direction § 06.


Cite as#

Hunt, K. (2026). AOI‑CLI: Agent-Operable Interface,
command-line profile. Machine Mode v0.2 (draft).
https://machinemode.io/spec