agent-first-data 0.20.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -33,7 +33,7 @@ import { installStreamRedirectFromRawArgs } from "agent-first-data/stream-redire
33
33
  - YAML keeps original keys and values (structure-preserving, like JSON), sorting keys by UTF-16 code unit order and quoting/escaping unsafe keys and string scalars. Plain strips formatting suffixes, formats values, sorts the same way, and renders nested objects/arrays as canonical JSON.
34
34
  - Logging records use `kind:"log"` with a nested `log` payload and a separate `level` field, so error-level logs are not terminal protocol errors.
35
35
  - `buildCliError(message, hint?)` returns a strict-ready CLI error with `error.retryable:false` and `trace:{}`.
36
- - Use `cliHandleVersionOrContinue()` before argument parsing so bare `--version` stays conventional and `--version --output json|yaml|plain` stays structured.
36
+ - Use `cliHandleVersionOrContinue()` before argument parsing so `--version`/`-V` always answers with a structured protocol-v1 `kind:"result"` version event — JSON by default, or `--output yaml|plain`/`--json` for another format; there is no conventional bare-text form. Pass your own value-taking global flag names so their value is not mistaken for the subcommand boundary.
37
37
  - Use `installStreamRedirectFromRawArgs()` (from `agent-first-data/stream-redirect`) before version/help handling if a CLI exposes `--stdout-file` or `--stderr-file`; stderr is redirected as native diagnostics, not JSON. Node fd-level redirection is process-lifetime because the standard library does not expose `dup2` restoration.
38
38
 
39
39
  ## Reference
@@ -61,7 +61,8 @@ assert!(rendered.contains("\"api_key_secret\":\"***\""));
61
61
  For a finite CLI execution implemented in Rust, create one `CliEmitter`, select
62
62
  `OutputFormat` through `cli_parse_output`, enable strict protocol, and emit one
63
63
  terminal result or error. Handle version before clap with
64
- `cli_handle_version_or_continue(raw_args, name, version)`.
64
+ `cli_handle_version_or_continue(raw_args, cmd, name, display_name, version, build)`,
65
+ which always answers `--version`/`-V` with a structured version event.
65
66
  For non-CLI HTTP/MCP/SSE serialization, call `redacted_value()` or
66
67
  `redact_url_secrets()` explicitly at the boundary.
67
68
 
@@ -284,7 +284,7 @@ Required cross-language behavior to rely on:
284
284
  - Use `redact_url_secrets()` for URLs embedded in log messages or prose before interpolating them into output.
285
285
  - Use `cli_parse_output()`, `cli_parse_log_filters()`, `render()`, `build_cli_error()`, and the version helper for CLI tools instead of custom parsing/error envelopes.
286
286
  - `build_cli_error(message, hint?)` returns a strict-ready CLI error with `error.retryable:false` and `trace:{}`.
287
- - Rust CLIs should call `cli_handle_version_or_continue(raw_args, name, version)` before clap parsing so `--version --output json|yaml|plain` emits a structured `kind:"result"` event with `result.code == "version"` and `result.version` instead of clap's plain text. Bare `--version` always stays human `<name> <version>` text; explicit `--output` is always structured — there is no configuration.
287
+ - Rust CLIs (feature `cli`/`cli-help`) should call `cli_handle_version_or_continue(raw_args, cmd, name, display_name, version, build)` before clap parsing so `--version`/`-V` always emits a structured `kind:"result"` event with `result.code == "version"`, `result.name`, and `result.version` (plus optional `result.display_name`/`result.build`) instead of clap's plain text — JSON by default, `--output yaml|plain` or `--json` for another format; there is no conventional bare-text form. Pass the caller's own `clap::Command` (e.g. `Cli::command()`) so any value-taking global flag it defines (e.g. `--stdout-file`) is recognized and its value is never mistaken for the subcommand boundary.
288
288
  - Use the protocol reader `decode_protocol_event(text)` to parse and strict-validate a single JSON text line, receiving a typed decoded event.
289
289
 
290
290
  ## AFDATA Logging
package/dist/cli.d.ts CHANGED
@@ -165,29 +165,42 @@ export declare class CliEmitter {
165
165
  */
166
166
  finishResult(payload: JsonValue): number;
167
167
  }
168
- /** Build a standard CLI version value. */
169
- export declare function buildCliVersion(version: string): Event;
170
168
  /**
171
- * Render CLI version output.
172
- * Pass an OutputFormat for AFDATA JSON/YAML/plain. Pass null to preserve
173
- * conventional "<name> <version>" text.
169
+ * Build a standard CLI version event: a `kind:"result"` event whose payload is
170
+ * `{ code: "version", name, version }`, plus `display_name`/`build` when given.
171
+ * `name` is the short/bin identity (e.g. `"afdata"`); `displayName` is an
172
+ * optional human-facing product name (e.g. `"Agent-First Data"`); `build` is an
173
+ * opaque caller-supplied identifier (a git commit SHA, for example) — its
174
+ * meaning is entirely up to the caller. Both are `undefined` when unavailable,
175
+ * and simply absent from the payload. Note the JSON field is `display_name`
176
+ * (snake_case) even though the parameter is `displayName`.
174
177
  */
175
- export declare function cliRenderVersion(name: string, version: string, format?: OutputFormat | null): string;
178
+ export declare function buildCliVersion(name: string, displayName: string | undefined, version: string, build: string | undefined): Event;
179
+ /**
180
+ * Render a CLI version response as a protocol-v1 event in `format`. There is no
181
+ * conventional-text path — `format` is required.
182
+ */
183
+ export declare function cliRenderVersion(name: string, displayName: string | undefined, version: string, build: string | undefined, format: OutputFormat): string;
176
184
  /**
177
185
  * Render version output if --version/-V is present; otherwise return undefined.
178
186
  * Throws for malformed version requests, for example `--version --output xml`.
179
187
  *
180
- * One blessed behavior, not configurable: a bare version request renders
181
- * conventional "<name> <version>" text; `--json` or `--output <fmt>` /
182
- * `--output=<fmt>` alongside it selects the structured AFDATA payload
183
- * instead.
188
+ * The one blessed behavior: `--version` always answers with a protocol-v1
189
+ * `kind:"result"` version event — JSON by default, or `--output yaml|plain` (or
190
+ * `--json`) for another format. There is no conventional bare-text path.
191
+ *
192
+ * `valueFlags` is the caller's own value-taking global long flags (with or
193
+ * without leading dashes; the leading dashes are stripped internally). It is the
194
+ * TS analog of Rust's `&clap::Command`: the pre-parser consults it so a global
195
+ * flag's space-separated value (e.g. `--log request,startup`) is never mistaken
196
+ * for the subcommand boundary, which would hide a later `--version`/`--output`.
184
197
  *
185
198
  * Only a top-level version request is recognized: scanning stops at the first
186
199
  * positional argument (the subcommand) or `--`, so `tool sub --version <value>`
187
200
  * leaves `--version` for the subcommand's parser rather than printing the
188
201
  * tool version.
189
202
  */
190
- export declare function cliHandleVersionOrContinue(rawArgs: string[], name: string, version: string): string | undefined;
203
+ export declare function cliHandleVersionOrContinue(rawArgs: string[], valueFlags: string[], name: string, displayName: string | undefined, version: string, build: string | undefined): string | undefined;
191
204
  /**
192
205
  * Build a standard CLI parse error value. This function cannot fail.
193
206
  * Use when flag parsing fails or a flag value is invalid.
package/dist/cli.js CHANGED
@@ -274,34 +274,70 @@ export class CliEmitter {
274
274
  return this.finish(jsonResult(payload).build(), 0);
275
275
  }
276
276
  }
277
- /** Build a standard CLI version value. */
278
- export function buildCliVersion(version) {
279
- return jsonResult({ code: "version", version }).build();
277
+ /**
278
+ * Build a standard CLI version event: a `kind:"result"` event whose payload is
279
+ * `{ code: "version", name, version }`, plus `display_name`/`build` when given.
280
+ * `name` is the short/bin identity (e.g. `"afdata"`); `displayName` is an
281
+ * optional human-facing product name (e.g. `"Agent-First Data"`); `build` is an
282
+ * opaque caller-supplied identifier (a git commit SHA, for example) — its
283
+ * meaning is entirely up to the caller. Both are `undefined` when unavailable,
284
+ * and simply absent from the payload. Note the JSON field is `display_name`
285
+ * (snake_case) even though the parameter is `displayName`.
286
+ */
287
+ export function buildCliVersion(name, displayName, version, build) {
288
+ return jsonResult({
289
+ code: "version",
290
+ name,
291
+ version,
292
+ ...(displayName !== undefined ? { display_name: displayName } : {}),
293
+ ...(build !== undefined ? { build } : {}),
294
+ }).build();
280
295
  }
281
296
  /**
282
- * Render CLI version output.
283
- * Pass an OutputFormat for AFDATA JSON/YAML/plain. Pass null to preserve
284
- * conventional "<name> <version>" text.
297
+ * Render a CLI version response as a protocol-v1 event in `format`. There is no
298
+ * conventional-text path — `format` is required.
285
299
  */
286
- export function cliRenderVersion(name, version, format = null) {
287
- const rendered = format === null ? `${name} ${version}` : render(buildCliVersion(version), format);
300
+ export function cliRenderVersion(name, displayName, version, build, format) {
301
+ const rendered = render(buildCliVersion(name, displayName, version, build), format);
288
302
  return `${rendered.replace(/\n+$/u, "")}\n`;
289
303
  }
304
+ /**
305
+ * Split a flag argument into its long name (leading dashes stripped, any
306
+ * `=value` suffix dropped) and inline value. Returns `undefined` name for a
307
+ * non-flag or the bare `-`. `--output=` yields an empty-string inline value.
308
+ */
309
+ function splitFlag(arg) {
310
+ if (!arg.startsWith("-") || arg === "-")
311
+ return { name: undefined, inlineValue: undefined };
312
+ const eq = arg.indexOf("=");
313
+ const flag = eq === -1 ? arg : arg.slice(0, eq);
314
+ const inlineValue = eq === -1 ? undefined : arg.slice(eq + 1);
315
+ const name = flag.replace(/^-+/u, "");
316
+ if (name === "")
317
+ return { name: undefined, inlineValue: undefined };
318
+ return { name, inlineValue };
319
+ }
290
320
  /**
291
321
  * Render version output if --version/-V is present; otherwise return undefined.
292
322
  * Throws for malformed version requests, for example `--version --output xml`.
293
323
  *
294
- * One blessed behavior, not configurable: a bare version request renders
295
- * conventional "<name> <version>" text; `--json` or `--output <fmt>` /
296
- * `--output=<fmt>` alongside it selects the structured AFDATA payload
297
- * instead.
324
+ * The one blessed behavior: `--version` always answers with a protocol-v1
325
+ * `kind:"result"` version event — JSON by default, or `--output yaml|plain` (or
326
+ * `--json`) for another format. There is no conventional bare-text path.
327
+ *
328
+ * `valueFlags` is the caller's own value-taking global long flags (with or
329
+ * without leading dashes; the leading dashes are stripped internally). It is the
330
+ * TS analog of Rust's `&clap::Command`: the pre-parser consults it so a global
331
+ * flag's space-separated value (e.g. `--log request,startup`) is never mistaken
332
+ * for the subcommand boundary, which would hide a later `--version`/`--output`.
298
333
  *
299
334
  * Only a top-level version request is recognized: scanning stops at the first
300
335
  * positional argument (the subcommand) or `--`, so `tool sub --version <value>`
301
336
  * leaves `--version` for the subcommand's parser rather than printing the
302
337
  * tool version.
303
338
  */
304
- export function cliHandleVersionOrContinue(rawArgs, name, version) {
339
+ export function cliHandleVersionOrContinue(rawArgs, valueFlags, name, displayName, version, build) {
340
+ const valueFlagSet = new Set(valueFlags.map((flag) => flag.replace(/^-+/u, "")));
305
341
  let versionRequested = false;
306
342
  let outputFormat;
307
343
  let outputError;
@@ -314,6 +350,7 @@ export function cliHandleVersionOrContinue(rawArgs, name, version) {
314
350
  // git/cargo/clap: this pre-parser only owns a top-level version request.
315
351
  if (!arg.startsWith("-"))
316
352
  break;
353
+ const { name: flagName, inlineValue } = splitFlag(arg);
317
354
  if (arg === "--version" || arg === "-V") {
318
355
  versionRequested = true;
319
356
  i += 1;
@@ -329,15 +366,21 @@ export function cliHandleVersionOrContinue(rawArgs, name, version) {
329
366
  i += 1;
330
367
  continue;
331
368
  }
332
- if (arg === "--output" || arg.startsWith("--output=")) {
369
+ // `--output-to` takes a value but does not affect version text output.
370
+ // Consume its space-separated value so it is not mistaken for the
371
+ // subcommand boundary (which would hide a later `--version`/`--output`).
372
+ if (flagName === "output-to") {
373
+ const hasSpaceValue = inlineValue === undefined && rawArgs[i + 1] !== undefined && !rawArgs[i + 1].startsWith("-");
374
+ i += hasSpaceValue ? 2 : 1;
375
+ continue;
376
+ }
377
+ if (flagName === "output") {
333
378
  let value;
334
- let step = 1;
335
- if (arg.startsWith("--output=")) {
336
- value = arg.slice("--output=".length);
379
+ if (inlineValue !== undefined) {
380
+ value = inlineValue;
337
381
  }
338
382
  else if (rawArgs[i + 1] !== undefined && !rawArgs[i + 1].startsWith("-")) {
339
383
  value = rawArgs[i + 1];
340
- step = 2;
341
384
  }
342
385
  if (value === undefined) {
343
386
  outputError = new Error("missing value for --output: expected json, yaml, or plain");
@@ -356,16 +399,20 @@ export function cliHandleVersionOrContinue(rawArgs, name, version) {
356
399
  outputError = e;
357
400
  }
358
401
  }
359
- i += step;
402
+ i += inlineValue !== undefined || value === undefined ? 1 : 2;
360
403
  continue;
361
404
  }
362
- i += 1;
405
+ // Any other flag: consult the caller's own value-taking global flags so a
406
+ // flag's space-separated value is never mistaken for the subcommand
407
+ // boundary above.
408
+ const hasSpaceValue = inlineValue === undefined && rawArgs[i + 1] !== undefined && !rawArgs[i + 1].startsWith("-");
409
+ i += hasSpaceValue && flagName !== undefined && valueFlagSet.has(flagName) ? 2 : 1;
363
410
  }
364
411
  if (!versionRequested)
365
412
  return undefined;
366
413
  if (outputError !== undefined)
367
414
  throw outputError;
368
- return cliRenderVersion(name, version, outputFormat ?? null);
415
+ return cliRenderVersion(name, displayName, version, build, outputFormat ?? "json");
369
416
  }
370
417
  /**
371
418
  * Build a standard CLI parse error value. This function cannot fail.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-first-data",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "description": "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.",
5
5
  "license": "MIT",
6
6
  "repository": {