agent-first-data 0.37.0

A naming convention that lets AI agents understand your data without being told what it means, plus the afdata CLI and a Rust library for reading Markdown structure and safely editing structured JSON, TOML, YAML, dotenv, and INI documents.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
# afdata CLI reference

<!-- Generated by `afdata --docs`. Do not edit by hand. -->

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

`afdata` is compiled from a closed `cli-spec-v1` registry: one source for argv parsing, typed invocation values, which parameter combinations are legal, output contracts, and help. An invocation runs only when it matches exactly one registered combination.

## Global arguments

AFDATA registers these itself, so the syntax in [Commands](#commands) leaves them out.

| Argument | Where | What it does |
|---|---|---|
| `--help` | every command | Every legal shape of that command, complete, plus its subcommands. JSON by default; `--output plain` for a terminal. |
| `--version` | afdata only | Name, version, and build identity as one protocol result. |
| `--docs` | afdata only | This document, rendered from the registry. |
| `--output <FORMAT>` | per output contract | Render as json, yaml, plain (default `json`). |
| `--output-to <DESTINATION>` | per output contract | Route results and diagnostics to split, stdout, stderr (default `split`). |
| `--stdout-file <PATH>`, `--stderr-file <PATH>` | per output contract | Append that stream to a file instead. |

Success output is protocol events, on those terms, unless a command's own **Output** line says otherwise.

A **shape** is one legal set of arguments that may appear together, under a stable id. Where a command has more than one, each id is a heading below. `--help` returns them all at once, so discovering a command costs one call; there is no recursive mode across commands, and this document is that view.

## Commands

- [`afdata add`](#afdata-add) — Add an element to a keyed list
- [`afdata array`](#afdata-array) — Build one JSON array from arguments, as one line of compact JSON with a trailing newline
- [`afdata count`](#afdata-count) — Print how many direct children the container at KEY has, as a decimal integer with no trailing newline
- [`afdata emit error`](#afdata-emit-error) — Emit a terminal error event
- [`afdata emit log`](#afdata-emit-log) — Emit a diagnostic log event
- [`afdata emit result`](#afdata-emit-result) — Emit a terminal result event
- [`afdata get`](#afdata-get) — Read a document or one value as an AFDATA result
- [`afdata guard`](#afdata-guard) — Validate a value against a destructive-verb operand type and print its normalized absolute path as raw stdout bytes
- [`afdata has`](#afdata-has) — Print true or false, with no trailing newline, for whether KEY exists; null counts as present
- [`afdata json`](#afdata-json) — Read a document or subtree as one line of compact JSON with a trailing newline, no envelope; secrets are redacted as in get
- [`afdata keys`](#afdata-keys) — List child names as raw lines, without their parent path
- [`afdata lint`](#afdata-lint) — Lint structured data for deterministic AFDATA issues
- [`afdata object`](#afdata-object) — Build one JSON object from arguments, as one line of compact JSON with a trailing newline
- [`afdata path`](#afdata-path) — Join literal key segments into one dot-path, escaping backslash, `.` and `*`; raw stdout bytes with no trailing newline
- [`afdata paths`](#afdata-paths) — List each child's full dot-path as raw lines
- [`afdata remove`](#afdata-remove) — Remove a keyed-list element by slug
- [`afdata render`](#afdata-render) — Render JSON or JSONL through AFDATA redaction and formatting
- [`afdata set`](#afdata-set) — Set a value at a dot-path, creating missing object parents
- [`afdata shell bash`](#afdata-shell-bash) — Print the sourceable Bash authoring kit
- [`afdata skill install`](#afdata-skill-install) — Install the bundled Agent Skill
- [`afdata skill status`](#afdata-skill-status) — Report whether the bundled Agent Skill is installed and current
- [`afdata skill uninstall`](#afdata-skill-uninstall) — Remove an afdata-managed Agent Skill
- [`afdata skill validate`](#afdata-skill-validate) — Validate an Agent Skill
- [`afdata unset`](#afdata-unset) — Remove one document entry
- [`afdata validate`](#afdata-validate) — Validate protocol-v1 events or a finite event stream
- [`afdata value`](#afdata-value) — Read one scalar as raw stdout bytes: exact bytes, no trailing newline; for one value per line use values
- [`afdata values`](#afdata-values) — Read many scalars as raw lines, from one parse of the document

### `afdata add`

Add an element to a keyed list

```
afdata add <FILE> <KEY> <SLUG> [<FIELD=VALUE>...] --slug-field <FIELD> [--input-format <json|toml|yaml|yml|dotenv|env|ini|toml-frontmatter|yaml-frontmatter>] [--max-file-bytes <BYTES>]
```

| Argument | Meaning |
|---|---|
| `FILE` | Document file to mutate |
| `KEY` | Dot-path to the keyed list |
| `SLUG` | New element slug |
| `FIELD=VALUE` | Additional string fields |
| `--slug-field` | Field that identifies each list element |
| `--input-format` | Document format override |
| `--max-file-bytes` | Largest document file to read, in bytes |

### `afdata array`

Build one JSON array from arguments, as one line of compact JSON with a trailing newline

```
afdata array [<VALUE>...] [--json]
```

Output: raw bytes on success; rejects `--output` and `--output-to`; redirect with `--stdout-file` or `--stderr-file`. Failures are still strict JSON on stderr.

| Argument | Meaning |
|---|---|
| `VALUE` | Element; a string unless --json is given |
| `--json` | Parse every VALUE as JSON text |

### `afdata count`

Print how many direct children the container at KEY has, as a decimal integer with no trailing newline

#### `count-root` — Count the document root's immediate children

```
afdata count <FILE> [--input-format <json|toml|yaml|yml|dotenv|env|ini|toml-frontmatter|yaml-frontmatter|markdown|plist>] [--max-file-bytes <BYTES>]
```

#### `count-container` — Count one addressed container's immediate children

```
afdata count <FILE> <KEY> [--missing-ok] [--input-format <json|toml|yaml|yml|dotenv|env|ini|toml-frontmatter|yaml-frontmatter|markdown|plist>] [--max-file-bytes <BYTES>] [--slug-field <FIELD>]
```

Output: raw bytes on success; rejects `--output` and `--output-to`; redirect with `--stdout-file` or `--stderr-file`. Failures are still strict JSON on stderr.

Arguments across every shape above:

| Argument | Meaning |
|---|---|
| `FILE` | Document file, or - for stdin (read as JSON unless --input-format is given) |
| `KEY` | Optional dot-path to an object or array; omitted means the document root |
| `--missing-ok` | Print 0 when the path leads to no container (missing, through a scalar, or null) |
| `--input-format` | Document format override |
| `--max-file-bytes` | Largest document file to read, in bytes |
| `--slug-field` | Field naming each element of an array on the path |

### `afdata emit error`

Emit a terminal error event

```
afdata emit error <CODE> <MESSAGE> [--hint <HINT>] [--retryable]
```

| Argument | Meaning |
|---|---|
| `CODE` | Stable error code |
| `MESSAGE` | Error message |
| `--hint` | Suggested corrective action |
| `--retryable` | Mark the failure safe to retry |

### `afdata emit log`

Emit a diagnostic log event

```
afdata emit log <debug|info|warn|error> <MESSAGE>
```

| Argument | Meaning |
|---|---|
| `LEVEL` | debug, info, warn, or error |
| `MESSAGE` | Human-readable message |

### `afdata emit result`

Emit a terminal result event

#### `emit-result-json` — Emit a structured JSON payload

```
afdata emit result --json <JSON>
```

#### `emit-result` — Emit a text message

```
afdata emit result <MESSAGE>
```

Arguments across every shape above:

| Argument | Meaning |
|---|---|
| `MESSAGE` | Result message |
| `--json` | Structured result payload; redaction applies |

### `afdata get`

Read a document or one value as an AFDATA result

#### `get-document` — Read the entire document

```
afdata get <FILE> [--input-format <json|toml|yaml|yml|dotenv|env|ini|toml-frontmatter|yaml-frontmatter|markdown|plist>] [--max-file-bytes <BYTES>] [--secret-name <FIELD>...]
```

#### `get-value` — Read one value at KEY

```
afdata get <FILE> <KEY> [--input-format <json|toml|yaml|yml|dotenv|env|ini|toml-frontmatter|yaml-frontmatter|markdown|plist>] [--max-file-bytes <BYTES>] [--slug-field <FIELD>] [--secret-name <FIELD>...]
```

Arguments across every shape above:

| Argument | Meaning |
|---|---|
| `FILE` | Document file, or - for stdin (read as JSON unless --input-format is given) |
| `KEY` | Optional dot-path |
| `--input-format` | Document format override |
| `--max-file-bytes` | Largest document file to read, in bytes |
| `--slug-field` | Field naming each element of an array on the path |
| `--secret-name` | Extra exact field name to redact |

### `afdata guard`

Validate a value against a destructive-verb operand type and print its normalized absolute path as raw stdout bytes

```
afdata guard <path|tmp_path|cwd_path> <VALUE> [--under <ROOT>]
```

Output: raw bytes on success; rejects `--output` and `--output-to`; redirect with `--stdout-file` or `--stderr-file`. Failures are still strict JSON on stderr.

| Argument | Meaning |
|---|---|
| `TYPE` | path: no containment beyond the shared reject set; tmp_path: must sit strictly under the system temp area; cwd_path: must sit strictly under the current directory |
| `VALUE` | Value to validate, e.g. the contents of a shell variable |
| `--under` | Additional containment root VALUE must sit strictly under, for a target anchored to a directory the caller knows and the TYPE vocabulary cannot name |

### `afdata has`

Print true or false, with no trailing newline, for whether KEY exists; null counts as present

```
afdata has <FILE> <KEY> [--input-format <json|toml|yaml|yml|dotenv|env|ini|toml-frontmatter|yaml-frontmatter|markdown|plist>] [--max-file-bytes <BYTES>] [--slug-field <FIELD>]
```

Output: raw bytes on success; rejects `--output` and `--output-to`; redirect with `--stdout-file` or `--stderr-file`. Failures are still strict JSON on stderr.

| Argument | Meaning |
|---|---|
| `FILE` | Document file, or - for stdin (read as JSON unless --input-format is given) |
| `KEY` | Dot-path to test |
| `--input-format` | Document format override |
| `--max-file-bytes` | Largest document file to read, in bytes |
| `--slug-field` | Field naming each element of an array on the path |

### `afdata json`

Read a document or subtree as one line of compact JSON with a trailing newline, no envelope; secrets are redacted as in get

#### `json-document` — Read the entire document

```
afdata json <FILE> [--reveal-secret] [--input-format <json|toml|yaml|yml|dotenv|env|ini|toml-frontmatter|yaml-frontmatter|markdown|plist>] [--max-file-bytes <BYTES>] [--secret-name <FIELD>...]
```

#### `json-value` — Read the subtree at KEY

```
afdata json <FILE> <KEY> [--reveal-secret] [--default <JSON>] [--input-format <json|toml|yaml|yml|dotenv|env|ini|toml-frontmatter|yaml-frontmatter|markdown|plist>] [--max-file-bytes <BYTES>] [--slug-field <FIELD>] [--secret-name <FIELD>...]
```

Output: raw bytes on success; rejects `--output` and `--output-to`; redirect with `--stdout-file` or `--stderr-file`. Failures are still strict JSON on stderr.

Arguments across every shape above:

| Argument | Meaning |
|---|---|
| `FILE` | Document file, or - for stdin (read as JSON unless --input-format is given) |
| `KEY` | Optional dot-path; omitted means the whole document |
| `--reveal-secret` | Allow secret values and URL credentials |
| `--default` | Fallback JSON text, printed as compact JSON, when the path leads to no value (missing, through a scalar, or null) |
| `--input-format` | Document format override |
| `--max-file-bytes` | Largest document file to read, in bytes |
| `--slug-field` | Field naming each element of an array on the path |
| `--secret-name` | Extra exact field name to redact |

### `afdata keys`

List child names as raw lines, without their parent path

#### `keys-root` — List the document root's immediate children

```
afdata keys <FILE> [--input-format <json|toml|yaml|yml|dotenv|env|ini|toml-frontmatter|yaml-frontmatter|markdown|plist>] [--max-file-bytes <BYTES>] [--null]
```

#### `keys-container` — List one addressed container's immediate children

```
afdata keys <FILE> <KEY> [--input-format <json|toml|yaml|yml|dotenv|env|ini|toml-frontmatter|yaml-frontmatter|markdown|plist>] [--max-file-bytes <BYTES>] [--slug-field <FIELD>] [--missing-ok] [--null]
```

#### `keys-slugs` — List one addressed array's elements by their --slug-field value

```
afdata keys <FILE> <KEY> [--input-format <json|toml|yaml|yml|dotenv|env|ini|toml-frontmatter|yaml-frontmatter|markdown|plist>] [--max-file-bytes <BYTES>] --slug-field <FIELD> [--missing-ok] --slugs [--null]
```

Output: raw bytes on success; rejects `--output` and `--output-to`; redirect with `--stdout-file` or `--stderr-file`. Failures are still strict JSON on stderr.

Arguments across every shape above:

| Argument | Meaning |
|---|---|
| `FILE` | Document file, or - for stdin (read as JSON unless --input-format is given) |
| `KEY` | Optional container dot-path |
| `--input-format` | Document format override |
| `--max-file-bytes` | Largest document file to read, in bytes |
| `--slug-field` | Field naming each element of an array on the path |
| `--missing-ok` | Print nothing and succeed when the path leads to no container (missing, through a scalar, or null) |
| `--slugs` | When KEY is an array, list each element's --slug-field value instead of its index |
| `--null` | Use NUL separators |

### `afdata lint`

Lint structured data for deterministic AFDATA issues

```
afdata lint <INPUT> [--input-format <json|toml|yaml|yml|dotenv|env|ini|toml-frontmatter|yaml-frontmatter|plist>] [--max-file-bytes <BYTES>] [--min-severity <warning|error>]
```

| Argument | Meaning |
|---|---|
| `INPUT` | Input file, or - for stdin |
| `--input-format` | Document format override |
| `--max-file-bytes` | Largest document file to read, in bytes |
| `--min-severity` | Lowest severity to report; `error` drops the heuristic checks |

### `afdata object`

Build one JSON object from arguments, as one line of compact JSON with a trailing newline

```
afdata object [<FIELD=VALUE>...] [--json <FIELD=JSON>...]
```

Output: raw bytes on success; rejects `--output` and `--output-to`; redirect with `--stdout-file` or `--stderr-file`. Failures are still strict JSON on stderr.

| Argument | Meaning |
|---|---|
| `FIELD=VALUE` | String field; the key is literal and split at the first = |
| `--json` | Field whose value is embedded JSON text (number, bool, null, array, object) |

### `afdata path`

Join literal key segments into one dot-path, escaping backslash, `.` and `*`; raw stdout bytes with no trailing newline

```
afdata path <SEGMENT>...
```

Output: raw bytes on success; rejects `--output` and `--output-to`; redirect with `--stdout-file` or `--stderr-file`. Failures are still strict JSON on stderr.

| Argument | Meaning |
|---|---|
| `SEGMENT` | One literal key or index; must not be empty |

### `afdata paths`

List each child's full dot-path as raw lines

#### `paths-root` — List the document root's immediate children

```
afdata paths <FILE> [--input-format <json|toml|yaml|yml|dotenv|env|ini|toml-frontmatter|yaml-frontmatter|markdown|plist>] [--max-file-bytes <BYTES>] [--null]
```

#### `paths-container` — List one addressed container's immediate children

```
afdata paths <FILE> <KEY> [--input-format <json|toml|yaml|yml|dotenv|env|ini|toml-frontmatter|yaml-frontmatter|markdown|plist>] [--max-file-bytes <BYTES>] [--slug-field <FIELD>] [--missing-ok] [--null]
```

#### `paths-slugs` — List one addressed array's elements by their --slug-field value

```
afdata paths <FILE> <KEY> [--input-format <json|toml|yaml|yml|dotenv|env|ini|toml-frontmatter|yaml-frontmatter|markdown|plist>] [--max-file-bytes <BYTES>] --slug-field <FIELD> [--missing-ok] --slugs [--null]
```

Output: raw bytes on success; rejects `--output` and `--output-to`; redirect with `--stdout-file` or `--stderr-file`. Failures are still strict JSON on stderr.

Arguments across every shape above:

| Argument | Meaning |
|---|---|
| `FILE` | Document file, or - for stdin (read as JSON unless --input-format is given) |
| `KEY` | Optional container dot-path |
| `--input-format` | Document format override |
| `--max-file-bytes` | Largest document file to read, in bytes |
| `--slug-field` | Field naming each element of an array on the path |
| `--missing-ok` | Print nothing and succeed when the path leads to no container (missing, through a scalar, or null) |
| `--slugs` | When KEY is an array, list each element's --slug-field value instead of its index |
| `--null` | Use NUL separators |

### `afdata remove`

Remove a keyed-list element by slug

```
afdata remove <FILE> <KEY> <SLUG> --slug-field <FIELD> [--input-format <json|toml|yaml|yml|dotenv|env|ini|toml-frontmatter|yaml-frontmatter>] [--max-file-bytes <BYTES>]
```

| Argument | Meaning |
|---|---|
| `FILE` | Document file to mutate |
| `KEY` | Dot-path to the keyed list |
| `SLUG` | Element slug |
| `--slug-field` | Field that identifies each list element |
| `--input-format` | Document format override |
| `--max-file-bytes` | Largest document file to read, in bytes |

### `afdata render`

Render JSON or JSONL through AFDATA redaction and formatting

```
afdata render <INPUT> [--secret-name <FIELD>...]
```

| Argument | Meaning |
|---|---|
| `INPUT` | Input file, or - for stdin |
| `--secret-name` | Extra exact field name to redact |

**Output:** Rendered input values or JSONL, without a `result` envelope. Successful values follow `--output-to`; `split` writes them to stdout.

### `afdata set`

Set a value at a dot-path, creating missing object parents

#### `set-value` — Set one typed scalar or JSON value

```
afdata set <FILE> <KEY> <VALUE> [--value-type <string|number|bool|json>] [--input-format <json|toml|yaml|yml|dotenv|env|ini|toml-frontmatter|yaml-frontmatter>] [--max-file-bytes <BYTES>] [--slug-field <FIELD>]
```

#### `set-null` — Set the key to null; takes no VALUE

```
afdata set <FILE> <KEY> --value-type null [--input-format <json|toml|yaml|yml|dotenv|env|ini|toml-frontmatter|yaml-frontmatter>] [--max-file-bytes <BYTES>] [--slug-field <FIELD>]
```

#### `set-secret` — Set the key from a secret source, never from argv

```
afdata set <FILE> <KEY> --secret-from <SOURCE> [--input-format <json|toml|yaml|yml|dotenv|env|ini|toml-frontmatter|yaml-frontmatter>] [--max-file-bytes <BYTES>] [--slug-field <FIELD>]
```

Arguments across every shape above:

| Argument | Meaning |
|---|---|
| `FILE` | Document file to mutate |
| `KEY` | Dot-path to set |
| `VALUE` | Value to write |
| `--value-type` | Exact VALUE type |
| `--secret-from` | Read a secret string from stdin, prompt, fd:N, or env:VAR |
| `--input-format` | Document format override |
| `--max-file-bytes` | Largest document file to read, in bytes |
| `--slug-field` | Field naming each element of an array on the path |

### `afdata shell bash`

Print the sourceable Bash authoring kit

```
afdata shell bash
```

Output: raw bytes on success; rejects `--output` and `--output-to`; redirect with `--stdout-file` or `--stderr-file`. Failures are still strict JSON on stderr.

### `afdata skill install`

Install the bundled Agent Skill

#### `skill-install-all` — Install into every agent that supports the scope

```
afdata skill install [--agent all] [--scope <personal|workspace>] [--force]
```

#### `skill-install-agent` — Install into one named agent; only this shape accepts --skills-dir

```
afdata skill install --agent <codex|claude-code|opencode|hermes> [--scope <personal|workspace>] [--skills-dir <PATH>] [--force]
```

Arguments across every shape above:

| Argument | Meaning |
|---|---|
| `--agent` | Target agent |
| `--scope` | Skill scope |
| `--skills-dir` | Explicit directory; only valid with one concrete agent |
| `--force` | Overwrite or remove an unmanaged skill |

### `afdata skill status`

Report whether the bundled Agent Skill is installed and current

#### `skill-status-all` — Report on every agent that supports the scope

```
afdata skill status [--agent all] [--scope <personal|workspace>]
```

#### `skill-status-agent` — Report on one named agent; only this shape accepts --skills-dir

```
afdata skill status --agent <codex|claude-code|opencode|hermes> [--scope <personal|workspace>] [--skills-dir <PATH>]
```

Arguments across every shape above:

| Argument | Meaning |
|---|---|
| `--agent` | Target agent |
| `--scope` | Skill scope |
| `--skills-dir` | Explicit directory; only valid with one concrete agent |

### `afdata skill uninstall`

Remove an afdata-managed Agent Skill

#### `skill-uninstall-all` — Remove from every agent that supports the scope

```
afdata skill uninstall [--agent all] [--scope <personal|workspace>] [--force]
```

#### `skill-uninstall-agent` — Remove from one named agent; only this shape accepts --skills-dir

```
afdata skill uninstall --agent <codex|claude-code|opencode|hermes> [--scope <personal|workspace>] [--skills-dir <PATH>] [--force]
```

Arguments across every shape above:

| Argument | Meaning |
|---|---|
| `--agent` | Target agent |
| `--scope` | Skill scope |
| `--skills-dir` | Explicit directory; only valid with one concrete agent |
| `--force` | Overwrite or remove an unmanaged skill |

### `afdata skill validate`

Validate an Agent Skill

```
afdata skill validate <INPUT>
```

| Argument | Meaning |
|---|---|
| `INPUT` | SKILL.md file, directory, or - for stdin |

### `afdata unset`

Remove one document entry

```
afdata unset <FILE> <KEY> [--input-format <json|toml|yaml|yml|dotenv|env|ini|toml-frontmatter|yaml-frontmatter>] [--max-file-bytes <BYTES>] [--slug-field <FIELD>]
```

| Argument | Meaning |
|---|---|
| `FILE` | Document file to mutate |
| `KEY` | Dot-path to remove |
| `--input-format` | Document format override |
| `--max-file-bytes` | Largest document file to read, in bytes |
| `--slug-field` | Field naming each element of an array on the path |

### `afdata validate`

Validate protocol-v1 events or a finite event stream

```
afdata validate <INPUT> [--strict] [--per-event]
```

| Argument | Meaning |
|---|---|
| `INPUT` | Input file, or - for stdin |
| `--strict` | Enforce the strict protocol profile |
| `--per-event` | Validate values independently without stream lifecycle rules |

### `afdata value`

Read one scalar as raw stdout bytes: exact bytes, no trailing newline; for one value per line use values

```
afdata value <FILE> <KEY> [--reveal-secret] [--default <VALUE>] [--input-format <json|toml|yaml|yml|dotenv|env|ini|toml-frontmatter|yaml-frontmatter|markdown|plist>] [--max-file-bytes <BYTES>] [--slug-field <FIELD>] [--secret-name <FIELD>...]
```

Output: raw bytes on success; rejects `--output` and `--output-to`; redirect with `--stdout-file` or `--stderr-file`. Failures are still strict JSON on stderr.

| Argument | Meaning |
|---|---|
| `FILE` | Document file, or - for stdin (read as JSON unless --input-format is given) |
| `KEY` | Dot-path to one scalar |
| `--reveal-secret` | Allow secret values and URL credentials |
| `--default` | Fallback when the path leads to no value (missing, through a scalar, or null) |
| `--input-format` | Document format override |
| `--max-file-bytes` | Largest document file to read, in bytes |
| `--slug-field` | Field naming each element of an array on the path |
| `--secret-name` | Extra exact field name to redact |

### `afdata values`

Read many scalars as raw lines, from one parse of the document

```
afdata values <FILE> <KEY>... [--reveal-secret] [--default <VALUE>] [--missing-ok] [--null] [--input-format <json|toml|yaml|yml|dotenv|env|ini|toml-frontmatter|yaml-frontmatter|markdown|plist>] [--max-file-bytes <BYTES>] [--slug-field <FIELD>] [--secret-name <FIELD>...]
```

Output: raw bytes on success; rejects `--output` and `--output-to`; redirect with `--stdout-file` or `--stderr-file`. Failures are still strict JSON on stderr.

| Argument | Meaning |
|---|---|
| `FILE` | Document file, or - for stdin (read as JSON unless --input-format is given) |
| `KEY` | Dot-path to one scalar; repeat for each value wanted. A `*` segment expands to every child in index or key order, so `items.*.name` yields one line per element; with --default a missing or null field gives an empty line and the line count stays aligned |
| `--reveal-secret` | Allow secret values and URL credentials |
| `--default` | Fallback when the path leads to no value (missing, through a scalar, or null) |
| `--missing-ok` | A KEY whose `*` container, or any segment before it, is missing contributes no records; a missing leaf is still governed by --default |
| `--null` | Use NUL separators, so values containing newlines are framed intact |
| `--input-format` | Document format override |
| `--max-file-bytes` | Largest document file to read, in bytes |
| `--slug-field` | Field naming each element of an array on the path |
| `--secret-name` | Extra exact field name to redact |

## Exit codes

| Code | Meaning |
|---|---|
| 0 | The command ran and succeeded. |
| 1 | The command ran and failed. The event carries a domain `error.code`. |
| 2 | The invocation was rejected before anything ran. `error.code` is one of the `cli_*` codes below. |
| 4 | Output could not be written (`output_write_failed`); a closed reader is success. |

The split is the useful one for a caller: exit 2 means the call was never made, so retrying it unchanged cannot help, while exit 1 means it was.

## CLI errors

Every structural failure emits one strict JSON `kind:"error"` event on stderr, leaves stdout empty, and exits 2. The `code` names the failure — `cli_unknown_argument` for an unknown spelling, `cli_unregistered_combination` for registered arguments in a mixture that is not, and one each for `cli_unknown_command`, `cli_missing_argument_value`, `cli_invalid_argument_value`, `cli_duplicate_argument`, `cli_unexpected_positional`, and `cli_invalid_utf8`. `message` identifies a safe argument spelling or the failure category and `hint` gives the command to run next; neither ever quotes a raw value, including secrets. These are decided before any config, secret source, filesystem, network, or domain I/O.

`cli_invocation_invalid` (exit 1) describes the tool itself rather than the call: the program misread its own resolved invocation — an unknown action id, or an argument id the selected combination cannot produce. It is a defect to report, not an input to correct; retrying the same command cannot help. `output_setup_failed` (exit 1) means an output sink (`--stdout-file`, `--stderr-file`, stream redirection) could not be established; the message names the flag and path, which is usually a missing directory or a permission to fix.

A tool may also exit 2 with a code of its own for a usage error its argument grammar cannot express — two arguments that are each valid but cannot be combined, or a value whose meaning depends on the target. Such a call is likewise refused before anything is changed.

Domain failures (exit 1) carry their own stable `error.code` instead, drawn from whatever this tool defines rather than from the `cli_*` set. Error events are routinely logged, so a message never quotes a value that came from an argument able to carry a credential — a header, a cookie, a token, a password, a proxy URL, an environment value, a form field. Such an error names the argument and the shape that was wrong, and stops there. A value that cannot be a secret — a path, a dot-path, a session name — is quoted, because an error that cannot say which one it means is not worth logging either.