argsbarg 6.0.2 → 6.1.1

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/CHANGELOG.md CHANGED
@@ -7,11 +7,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [6.1.1] - 2026-07-23
11
+
12
+ ### Added
13
+
14
+ - **`ctx.inputs`** — coerced, pre-validated leaf inputs (getter; preferred over `readLeafInputs()`).
15
+ - **`ctx.inputsAs<T>()`** — `ctx.inputs` cast to a schemagen or app input type (`T` unconstrained; consumer-asserted).
16
+
17
+ ### Changed
18
+
19
+ - **`leaf.inputSchema` validation** — runs before the leaf handler in `Cli.run()` and `Cli.invoke()` (same JSON Schema subset as `program.appConfig`). `LeafInputError` prints contextual help on CLI. `ctx.inputs` returns the cached, pre-validated result.
20
+ - **`ctx.readLeafInputs()`** — deprecated; use `ctx.inputs` or `ctx.inputsAs()`.
21
+
22
+ ## [6.1.0] - 2026-07-22
23
+
24
+ ### Added
25
+
26
+ - **`CliOptionKind.Json`** and **`pipable`** — JSON object options with CLI `--name '<json>'` or piped stdin when the flag is omitted (flag wins if set). MCP/API values merge from `toolArgs`.
27
+ - **`ctx.jsonOpt(name)`** — parsed Json option from flag, preloaded stdin, or MCP/API `toolArgs`.
28
+ - **`ctx.readLeafInputs()`** — all coerced inputs; validates against `leaf.inputSchema` when set (stdin preloaded before handler).
29
+ - **`ctx.readLeafInputsAsync()`** — deprecated alias for sync `readLeafInputs()`.
30
+
31
+ ### Changed
32
+
33
+ - **Schemagen discovery** — role exports (`configType` / `inputType` / `outputType`) live in `src/**/types.ts` instead of thin `schema.ts` manifests.
34
+
10
35
  ## [6.0.2] - 2026-07-22
11
36
 
12
37
  ### Added
13
38
 
14
- - **`argsbarg schemagen`** — centralized JSON Schema generation from `src/**/schema.ts` into colocated `__generated__/` directories (`configSchema.json`, `inputSchema.json`, `outputSchema.json`, plus `index.ts` re-exports). Removes orphan `__generated__/` trees and stale JSON when schema roots or kinds are removed.
39
+ - **`argsbarg schemagen`** — centralized JSON Schema generation from thin `src/**/schema.ts` manifests into colocated `__generated__/` directories. Follows role aliases (`configType` / `inputType` / `outputType`) to types in sibling modules (typically `types.ts`).
15
40
  - **`argsbarg/schemagen`** export — `runSchemagen`, `discoverSchemaRoots`, and naming helpers (`src/cli-tool/schemagen/`).
16
41
 
17
42
  ### Changed
@@ -739,7 +764,9 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
739
764
  - Migrate schemas: rename every `children` property to **`commands`**; move positional definitions to **`CliPositional`** objects on `positionals` and strip `positional` / `argMin` / `argMax` from flag definitions under `options` (flags only carry `name`, `description`, `kind`, and optional `shortName`).
740
765
  - Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
741
766
 
742
- [Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v6.0.2...HEAD
767
+ [Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v6.1.1...HEAD
768
+ [6.1.1]: https://github.com/bdombro/bun-argsbarg/releases/tag/v6.1.1
769
+ [6.1.0]: https://github.com/bdombro/bun-argsbarg/releases/tag/v6.1.0
743
770
  [6.0.2]: https://github.com/bdombro/bun-argsbarg/releases/tag/v6.0.2
744
771
  [6.0.1]: https://github.com/bdombro/bun-argsbarg/releases/tag/v6.0.1
745
772
  [6.0.0]: https://github.com/bdombro/bun-argsbarg/releases/tag/v6.0.0
package/README.md CHANGED
@@ -207,7 +207,11 @@ Add `CliPositional` entries to the command’s `positionals` list (separate from
207
207
  - `ctx.durationOpt("timeout")` — duration options (`format: CliValueFormat.Duration`) as milliseconds.
208
208
  - `ctx.commaListOpt("services")` — comma-list options as `string[] | undefined`.
209
209
  - `ctx.dateOpt("on")` / `ctx.dateTimeOpt("since")` — ISO date / date-time options.
210
- - `ctx.readLeafInputs()` — coerced option and positional values for the current leaf (schema-driven).
210
+ - `ctx.inputs` — coerced option and positional values for the current leaf; when `inputSchema` is set, validated before the handler runs and cached on `ctx`.
211
+ - `ctx.inputsAs<T>()` — `ctx.inputs` cast to a schemagen or app input type.
212
+ - `ctx.readLeafInputs()` — deprecated; use `ctx.inputs` or `ctx.inputsAs()`.
213
+ - `ctx.jsonOpt(name)` — parsed Json option (flag, preloaded stdin, or MCP/API `toolArgs`).
214
+ - `ctx.readLeafInputsAsync()` — deprecated alias for `readLeafInputs()`.
211
215
  - `ctx.typedOpt<T>("custom", parseFn)` — custom parsing for type-safe option resolution.
212
216
  - `ctx.args` — positional words in order as `string[]`.
213
217
  - `ctx.positional("name")` — named positional lookup; varargs slots return `string[]`, single slots return `string | undefined`.
@@ -267,8 +271,8 @@ To refresh the Cursor rule in an existing consumer: `bun scripts/merge-cli-progr
267
271
  | Area | Files / wiring |
268
272
  | --------------------- | -------------------------------------------------------------------------------------- |
269
273
  | All builtins | `completion`, `version`, `configure`, `docs`, `mcp`, `api`, `configure get`/`set` |
270
- | `program.appConfig` | `src/config/schema.ts` → `configSchema` from `__generated__/` |
271
- | `outputSchema` | `src/commands/status/schema.ts` → `outputSchema` from `__generated__/` |
274
+ | `program.appConfig` | `src/config/types.ts` → `configSchema` from `__generated__/` |
275
+ | `outputSchema` | `src/commands/status/types.ts` → `outputSchema` from `__generated__/` |
272
276
  | Schemagen | `just schemagen` → `argsbarg schemagen` (justfile exports `node_modules/.bin` on `PATH`) |
273
277
  | Command layout | `src/commands/<name>/command.ts`; registration in `src/program.ts` |
274
278
  | MCP doc topics | `docs.topics` auto-exposed as `<key>://docs/<topic>` resources when docs + MCP enabled |
@@ -138,4 +138,4 @@ Call `generateOpenApi(program)` or `openApiJson(program)` from the package, fetc
138
138
 
139
139
  ## Complex tool inputs
140
140
 
141
- For nested request bodies (e.g. invoice template data), set `inputSchema` on the leaf and read `ctx.toolArgs` in the handler — it contains the original flat JSON body from `POST /tools/:name`.
141
+ For nested request bodies (e.g. invoice template data), set `inputSchema` on the leaf, declare a matching `kind: Json` option (optionally `pipable: true` for CLI stdin), and read inputs with `ctx.jsonOpt(...)` or `ctx.readLeafInputs()` — piped stdin is loaded before the handler runs.
@@ -203,14 +203,17 @@ handler: async (ctx) => {
203
203
 
204
204
  Read varargs with `ctx.positional("uids")` (returns `string[]`) or `ctx.args`. Do not comma-split argv tokens or use `format` on positionals.
205
205
 
206
- **`readLeafInputs()`** — for leaves with several flags, one schema-driven read instead of hand-rolled `hasFlag` / `stringOpt` lines:
206
+ **`ctx.inputs`** — coerced option and positional values for the current leaf. When `leaf.inputSchema` is set, argsbarg validates **before the handler runs** and caches the result on `ctx`:
207
207
 
208
208
  ```typescript
209
- const { limit, "skip-readiness": skipReadiness, timeout } = ctx.readLeafInputs();
210
- // duration → number (ms); comma-list → string[]; presence → boolean; number → number
209
+ const { limit, "skip-readiness": skipReadiness, timeout } = ctx.inputs;
210
+ // or, with a schemagen type:
211
+ const { format, invoice } = ctx.inputsAs<RenderInvoiceInput>();
211
212
  ```
212
213
 
213
- **`CliLeafInputs`** — return type of `readLeafInputs()` (exported from `"argsbarg"`). A flat record keyed by **schema option and positional names** (hyphens preserved, e.g. `"skip-readiness"`). Values are coerced per kind/format:
214
+ **`readLeafInputs()`** — deprecated alias for `ctx.inputs`.
215
+
216
+ **`CliLeafInputs`** — return type of `ctx.inputs` (exported from `"argsbarg"`). A flat record keyed by **schema option and positional names** (hyphens preserved, e.g. `"skip-readiness"`). Values are coerced per kind/format:
214
217
 
215
218
  | Schema | Value in `CliLeafInputs` |
216
219
  | --- | --- |
@@ -223,8 +226,36 @@ const { limit, "skip-readiness": skipReadiness, timeout } = ctx.readLeafInputs()
223
226
  | `format: date-time` | `string` (normalized UTC ISO) |
224
227
  | Single positional | `string` or `undefined` |
225
228
  | Varargs positional | `string[]` or `undefined` |
229
+ | `kind: json` | parsed object/array or `undefined` (`ctx.jsonOpt(name)`; piped stdin preloaded before handler) |
230
+
231
+ Omitted options appear as `undefined` (not absent keys). Options with `default` are filled in post-parse before handlers run, so `ctx.inputs` and `durationOpt` see defaults. **`ctx.opts` always holds raw strings** — use typed accessors or `ctx.inputs` for coerced values.
232
+
233
+ ### Json options and piped stdin
234
+
235
+ For nested tool bodies (e.g. invoice template data), declare a matching property in schemagen `inputType`, wire `inputSchema` on the leaf, and add a **`kind: Json`** option with the same name:
236
+
237
+ ```typescript
238
+ {
239
+ name: "invoice",
240
+ description: "Invoice template data. Pass JSON via --invoice or pipe to stdin.",
241
+ kind: CliOptionKind.Json,
242
+ pipable: true,
243
+ required: true,
244
+ }
245
+ ```
246
+
247
+ | Surface | How `invoice` is supplied |
248
+ | --- | --- |
249
+ | CLI | `--invoice '<json>'` **or** omit the flag and pipe JSON to stdin (preloaded before the handler) |
250
+ | MCP / HTTP | `invoice` object in the tool JSON body (`ctx.toolArgs`) |
251
+
252
+ **Precedence:** if `--invoice` is set, the flag value wins and stdin is not read.
253
+
254
+ Use **`ctx.jsonOpt("invoice")`**, **`ctx.inputs`**, or **`ctx.inputsAs<MyInput>()`** — all synchronous. Argsbarg reads piped stdin before calling the handler when a `pipable` Json flag is omitted. When `leaf.inputSchema` is set, argsbarg validates merged inputs **before the handler runs** (same JSON Schema subset as `program.appConfig`); `ctx.inputs` returns the cached result.
255
+
256
+ At most one `pipable` Json option per leaf. Json option names must appear in `inputSchema.properties` when a custom `inputSchema` is set.
226
257
 
227
- Omitted options appear as `undefined` (not absent keys). Options with `default` are filled in post-parse before handlers run, so `readLeafInputs()` and `durationOpt` see defaults. **`ctx.opts` always holds raw strings** — use typed accessors or `readLeafInputs()` for coerced values.
258
+ See [output-schema.md](output-schema.md) for schemagen `inputType` and [api-server.md](api-server.md) for HTTP tool bodies.
228
259
 
229
260
  `CliLeafInputs` is intentionally untyped at the framework level. Narrow in your app (`read*Flags(ctx)` returning a typed struct) rather than expecting inference from `satisfies CliLeaf`.
230
261
 
@@ -142,7 +142,7 @@ Mirror the [output-schema.md](output-schema.md) pattern for config:
142
142
 
143
143
  ```mermaid
144
144
  flowchart LR
145
- subgraph types [schema.ts]
145
+ subgraph types [types.ts]
146
146
  Marker["export type configType = AppConfig"]
147
147
  end
148
148
  subgraph gen [argsbarg schemagen]
@@ -164,14 +164,14 @@ flowchart LR
164
164
  | Piece | Convention |
165
165
  | --- | --- |
166
166
  | Generator | [`ts-json-schema-generator`](https://github.com/vega/ts-json-schema-generator) (bundled with argsbarg) |
167
- | Discovery | `export type configType = …` in `src/config/schema.ts` (type defined in same file) |
167
+ | Discovery | `export type configType = …` in `src/config/types.ts` |
168
168
  | Artifacts | `src/config/__generated__/` — gitignored; run `just schemagen` after clone |
169
169
  | Consumer CI | Optional: `ajv` + `ajv-formats` against the same committed JSON (not an argsbarg runtime dep) |
170
170
 
171
171
  Example:
172
172
 
173
173
  ```typescript
174
- // src/config/schema.ts
174
+ // src/config/types.ts
175
175
  export interface AppConfig {
176
176
  apiToken: string;
177
177
  }
@@ -50,13 +50,13 @@ Reference implementations: **sqsp-qa-manager-poc**, **sqsp-workspaces**, **sqsp-
50
50
 
51
51
  ```mermaid
52
52
  flowchart LR
53
- subgraph types [schema.ts]
53
+ subgraph types [types.ts]
54
54
  Config["export type configType = AppConfig"]
55
55
  Output["export type outputType = StatusJsonOutput"]
56
56
  Input["export type inputType = ToolInput (optional)"]
57
57
  end
58
58
  subgraph gen [argsbarg schemagen]
59
- Discover["discover schema.ts roots"]
59
+ Discover["discover types.ts roots"]
60
60
  Gen["ts-json-schema-generator"]
61
61
  end
62
62
  subgraph artifacts [Gitignored __generated__]
@@ -74,8 +74,8 @@ flowchart LR
74
74
  | Piece | Convention |
75
75
  | --- | --- |
76
76
  | Generator | [`ts-json-schema-generator`](https://github.com/vega/ts-json-schema-generator) (bundled as an argsbarg dependency) |
77
- | Discovery | Walk `src/**/schema.ts`; generate from `export type outputType = …` / `inputType` / `configType` when the target type is **defined in that file** |
78
- | Artifacts | `src/**/__generated__/` — gitignored; run schemagen after clone or when `schema.ts` changes |
77
+ | Discovery | Walk `src/**/types.ts` for `export type outputType` / `inputType` / `configType`; generate from the aliased type in the same file |
78
+ | Artifacts | `src/**/__generated__/` — gitignored; run schemagen after clone or when `types.ts` changes |
79
79
  | Invocation | `just schemagen` — justfile exports `node_modules/.bin` on `PATH` for local `argsbarg` |
80
80
  | tsconfig | `"resolveJsonModule": true` |
81
81
  | CI | `just check`: `schemagen` → typecheck (no git diff on generated files) |
@@ -84,12 +84,10 @@ flowchart LR
84
84
 
85
85
  ### Declaring a schema root
86
86
 
87
- Put schema-facing interfaces in **`schema.ts`** next to the command (or in a shared module when several commands reuse one shape). Export a role alias:
87
+ Put schema-facing interfaces and role exports in **`types.ts`** (or `core/types.ts` for shared shapes):
88
88
 
89
89
  ```typescript
90
- // src/commands/status/schema.ts
91
- import type { WorkspaceStatus } from "./types.ts";
92
-
90
+ // src/commands/status/types.ts
93
91
  /** JSON stdout for `myapp status --json`. */
94
92
  export interface StatusJsonOutput {
95
93
  workspaces: WorkspaceStatus[];
@@ -99,10 +97,10 @@ export interface StatusJsonOutput {
99
97
  export type outputType = StatusJsonOutput;
100
98
  ```
101
99
 
102
- ```typescript
103
- // src/ui/runHeadless/schema.ts — shared by many mutating commands
104
- import type { HeadlessTaskResult } from "./types.ts";
100
+ Handlers and other modules import from the same **`types.ts`** module.
105
101
 
102
+ ```typescript
103
+ // src/ui/runHeadless/types.ts — shared by many mutating commands
106
104
  export interface HeadlessOpResult {
107
105
  command: string;
108
106
  exitCode: number;
@@ -113,31 +111,35 @@ export type outputType = HeadlessOpResult;
113
111
  ```
114
112
 
115
113
  ```typescript
116
- // src/commands/render-invoice/schema.ts — custom HTTP/MCP body (pdf-gen)
117
- export interface RenderInvoiceToolInput {
114
+ // src/commands/render-invoice/types.ts — custom HTTP/MCP body (pdf-gen)
115
+ export interface RenderInvoiceInput {
118
116
  format: "pdf" | "html";
119
117
  invoice: InvoiceData;
120
118
  }
121
119
 
122
- export type inputType = RenderInvoiceToolInput;
123
- export type outputType = RenderInvoiceWrittenOutput;
120
+ export interface RenderInvoiceOutput {
121
+ bytes: number;
122
+ }
123
+
124
+ export type inputType = RenderInvoiceInput;
125
+ export type outputType = RenderInvoiceOutput;
124
126
  ```
125
127
 
126
128
  | Export | Role |
127
129
  | --- | --- |
128
- | `export type configType = AppConfig` | `program.appConfig.jsonSchema` (one per repo, typically `src/config/schema.ts`) |
130
+ | `export type configType = AppConfig` | `program.appConfig.jsonSchema` (one per repo, typically `src/config/types.ts`) |
129
131
  | `export type outputType = …` | `leaf.outputSchema` |
130
132
  | `export type inputType = …` | `leaf.inputSchema` (only when the tool body is not flat CLI flags) |
131
133
 
132
- **Domain helpers** stay in `types.ts` (or `core/types.ts`). Discovery scans only `schema.ts`. Re-export-only files (`export type outputType = HeadlessOpResult` pointing at another module) are **not** generation roots — generate once from the canonical definition file.
134
+ **Domain helpers** and **role exports** live in `types.ts`. Commands without structured JSON omit role exports. Shared shapes (e.g. `HeadlessOpResult`) are defined once in `types.ts` with a single `outputType`; other commands import `{ outputSchema }` from that module’s `__generated__/index.ts`.
133
135
 
134
- Commands without structured JSON omit `schema.ts`. Shared shapes (e.g. `HeadlessOpResult`) live in one canonical `schema.ts`; other commands import `{ outputSchema }` from that module’s `__generated__/index.ts`.
136
+ For nested MCP/HTTP bodies, add a `kind: Json` option (same name as the `inputType` property) and use `ctx.jsonOpt(...)` or `ctx.readLeafInputs()` — see [cli-program.md](cli-program.md#json-options-and-piped-stdin).
135
137
 
136
138
  When you do **not** set `inputSchema`, argsbarg builds tool input from CLI `options` + `positionals`.
137
139
 
138
140
  ### Generated artifacts
139
141
 
140
- Schemagen writes under `__generated__/` beside each `schema.ts`:
142
+ Schemagen writes under `__generated__/` beside each `types.ts` with role exports:
141
143
 
142
144
  | Kind | Generated file | Exported const (from `__generated__/index.ts`) |
143
145
  | --- | --- | --- |
@@ -174,15 +176,15 @@ appConfig: { jsonSchema: configSchema, entries: { … } },
174
176
 
175
177
  **Goal:** generated schemas match what handlers actually print, with descriptions agents can read in `docs api`.
176
178
 
177
- 1. **Schema roots** — `export interface` in `schema.ts`, with `export type outputType = …` (or `inputType` / `configType`).
179
+ 1. **Schema roots** — `export interface` in `types.ts`, with `export type outputType = …` (or `inputType` / `configType`).
178
180
  2. **Per property** — `/** … */` on every field that should appear in JSON Schema `properties` (including nested named types).
179
181
  3. **Unions / enums** — document the alias; generator emits `enum` / `anyOf` with type-level description.
180
182
  4. **Formats** — property JSDoc can include `@format date-time` for ISO timestamps; add a smoke test that the generated property has `format: "date-time"`.
181
- 5. **Do not hand-edit** `__generated__/` — change types/JSDoc in `schema.ts`, run `just schemagen`.
183
+ 5. **Do not hand-edit** `__generated__/` — change types/JSDoc in `types.ts`, run `just schemagen`.
182
184
 
183
185
  ### Narrowing when runtime ≠ stdout
184
186
 
185
- When a shared runtime type is **wider** than one command’s JSON, add a **schema-facing** root in `schema.ts`:
187
+ When a shared runtime type is **wider** than one command’s JSON, add a **schema-facing** root in `types.ts`:
186
188
 
187
189
  ```typescript
188
190
  /** JSON stdout for `myapp pr` and `myapp file`. */
@@ -211,7 +213,7 @@ Per consumer repo (optional):
211
213
 
212
214
  ## Contributor workflow
213
215
 
214
- 1. Add or edit schema roots in `src/**/schema.ts` with `outputType` / `inputType` / `configType` and per-field JSDoc.
216
+ 1. Add or edit schema roots in `src/**/types.ts` with `outputType` / `inputType` / `configType` and per-field JSDoc.
215
217
  2. `just schemagen` — refresh `src/**/__generated__/`.
216
218
  3. Import `{ outputSchema }` / `{ inputSchema }` / `{ configSchema }` from the relevant `__generated__/index.ts`.
217
219
  4. `just docgen` / `myapp docs api --save` — refresh consumer docs.
@@ -219,7 +221,7 @@ Per consumer repo (optional):
219
221
 
220
222
  Add a bullet under your app’s `**… conventions:**` block in `.cursor/rules/cli-program.mdc` pointing at `node_modules/argsbarg/docs/output-schema.md`.
221
223
 
222
- **Reference implementation:** [`examples/full-example/`](../examples/full-example/) in this repo — `schema.ts` roots, `__generated__/`, and `status` leaf with `outputSchema`.
224
+ **Reference implementation:** [`examples/full-example/`](../examples/full-example/) in this repo — `types.ts` roots, `__generated__/`, and `status` leaf with `outputSchema`.
223
225
 
224
226
  ## Out of scope
225
227
 
@@ -9,7 +9,7 @@ From a git checkout at this directory (requires [Homebrew](https://brew.sh), [ju
9
9
  ```bash
10
10
  brew install just bun
11
11
  just setup
12
- just schemagen # after changing src/**/schema.ts
12
+ just schemagen # after changing src/**/types.ts
13
13
  just run status --json
14
14
  just run docs readme
15
15
  ```
@@ -62,13 +62,13 @@ Undo a local dev install: `just uninstall` (formula + agent artifacts; app confi
62
62
 
63
63
  ## Schemagen roots
64
64
 
65
- | Export in `schema.ts` | Generated artifact | Import on leaf / program |
65
+ | Export in `types.ts` | Generated artifact | Import on leaf / program |
66
66
  | --- | --- | --- |
67
- | `export type configType = …` | `__generated__/configSchema.json` | `{ configSchema }` from `config/__generated__/index.ts` → `program.appConfig.jsonSchema` |
68
- | `export type outputType = …` | `__generated__/outputSchema.json` | `{ outputSchema }` from `__generated__/index.ts` → `leaf.outputSchema` |
69
- | `export type inputType = …` | `__generated__/inputSchema.json` | `{ inputSchema }` from `__generated__/index.ts` → `leaf.inputSchema` |
67
+ | `export type configType = …` | `config/__generated__/configSchema.json` | `{ configSchema }` from `config/__generated__/index.ts` |
68
+ | `export type outputType = …` | `__generated__/outputSchema.json` | `{ outputSchema }` from `__generated__/index.ts` |
69
+ | `export type inputType = …` | `__generated__/inputSchema.json` | `{ inputSchema }` from `__generated__/index.ts` |
70
70
 
71
- Discovery walks `src/**/schema.ts` only. Domain helpers stay in sibling `types.ts`. `__generated__/` is gitignored — run `argsbarg schemagen` (via `just schemagen` or `just setup`).
71
+ Type definitions and schemagen role exports live in `types.ts`. Run `argsbarg schemagen` (via `just schemagen` or `just setup`).
72
72
 
73
73
  ## Consumer docs
74
74
 
@@ -1 +1,19 @@
1
- export type { StatusJsonOutput } from "./schema.ts";
1
+ /** JSON stdout for `full-example status --json`. */
2
+ export interface StatusJsonOutput {
3
+ /** Resolved AWS region. */
4
+ defaultRegion?: string;
5
+ /** Resolved retry count. */
6
+ maxRetries?: number;
7
+ /** Whether apiToken is set (value never included). */
8
+ apiTokenSet: boolean;
9
+ /** App version from program root. */
10
+ version: string;
11
+ }
12
+
13
+ /** Returns status JSON (identity helper for schema generation). */
14
+ export function buildStatusJson(output: StatusJsonOutput): StatusJsonOutput {
15
+ return output;
16
+ }
17
+
18
+ /** Schemagen root for leaf outputSchema. */
19
+ export type outputType = StatusJsonOutput;
@@ -4,10 +4,10 @@ Kitchen-sink CliProgram — every argsbarg builtin enabled; command registration
4
4
 
5
5
  import type { CliAppConfig, CliAppConfigEntry, CliProgram } from "argsbarg";
6
6
  import readmeText from "../README.md" with { type: "text" };
7
- import { configSchema } from "./config/__generated__/index.ts";
8
7
  import { createIdentity } from "../scripts/create-identity.ts";
9
8
  import { echoCommand } from "./commands/echo/command.ts";
10
9
  import { statusCommand } from "./commands/status/command.ts";
10
+ import { configSchema } from "./config/__generated__/index.ts";
11
11
 
12
12
  const configEntries = {
13
13
  apiToken: {
package/index.d.ts CHANGED
@@ -45,7 +45,7 @@ declare class AppConfigSnapshot {
45
45
  }
46
46
  export type AnyAppConfigSnapshot = AppConfigSnapshot | EmptyAppConfigSnapshot;
47
47
  /** Coerced leaf inputs keyed by option and positional names. */
48
- export type CliLeafInputs = Record<string, boolean | number | string | string[] | undefined>;
48
+ export type CliLeafInputs = Record<string, boolean | number | string | string[] | unknown | undefined>;
49
49
  /**
50
50
  * Values passed to a leaf command handler after parsing: app name, routed path, args, and merged options.
51
51
  */
@@ -59,9 +59,12 @@ export declare class CliContext {
59
59
  readonly appConfig: AnyAppConfigSnapshot;
60
60
  /** Original flat tool arguments for API/MCP invocations (when provided). */
61
61
  readonly toolArgs?: Record<string, unknown>;
62
+ /** Pipable Json option values read from stdin before the handler (CLI only). */
63
+ readonly preloadedJson: Record<string, unknown>;
62
64
  private response?;
65
+ private leafInputsCache?;
63
66
  /** Captures the program root, routed path, positional words, and option map for a leaf handler. */
64
- constructor(appName: string, commandPath: string[], args: string[], opts: Record<string, string>, program: CliProgram, invocation?: CliInvocation, appConfig?: AnyAppConfigSnapshot, toolArgs?: Record<string, unknown>);
67
+ constructor(appName: string, commandPath: string[], args: string[], opts: Record<string, string>, program: CliProgram, invocation?: CliInvocation, appConfig?: AnyAppConfigSnapshot, toolArgs?: Record<string, unknown>, preloadedJson?: Record<string, unknown>);
65
68
  /**
66
69
  * Sets the machine-readable response for API/MCP invocations, or writes to stdout in CLI mode.
67
70
  * May only be called once per invocation.
@@ -88,11 +91,30 @@ export declare class CliContext {
88
91
  dateOpt(name: string): string | undefined;
89
92
  /** Date-time option as normalized ISO 8601 UTC (post-parse validated). */
90
93
  dateTimeOpt(name: string): string | undefined;
94
+ /**
95
+ * Parsed Json option: `--name '<json>'`, preloaded piped stdin (when `pipable`), or MCP/API toolArgs.
96
+ * Flag wins over stdin and toolArgs.
97
+ */
98
+ jsonOpt(name: string): unknown | undefined;
91
99
  /** Returns the value(s) for a named positional slot. Varargs slots return string[]; single slots return string | undefined. */
92
100
  positional(name: string): string | string[] | undefined;
93
- /** Reads coerced option and positional values for the current leaf from schema metadata. */
101
+ /**
102
+ * Coerced option and positional values for the current leaf.
103
+ * When `leaf.inputSchema` is set, argsbarg validates before the handler runs; this returns the cached result.
104
+ */
105
+ get inputs(): CliLeafInputs;
106
+ /**
107
+ * {@link inputs} cast to a schemagen or app-defined input type (consumer-asserted; not inferred from `inputSchema`).
108
+ */
109
+ inputsAs<T = CliLeafInputs>(): T;
110
+ /**
111
+ * @deprecated Use {@link inputs} or {@link inputsAs}.
112
+ */
94
113
  readLeafInputs(): CliLeafInputs;
95
- private _readOptionValue;
114
+ /**
115
+ * @deprecated Use sync {@link readLeafInputs} — stdin is preloaded before the handler runs.
116
+ */
117
+ readLeafInputsAsync(): Promise<CliLeafInputs>;
96
118
  private _leafNode;
97
119
  private _posMap;
98
120
  private _positionalMap;
@@ -102,7 +124,7 @@ export declare class CliContext {
102
124
  */
103
125
  export type CliInvocation = "cli" | "mcp" | "api";
104
126
  /**
105
- * Option kinds: presence (boolean flag), string (free-form text), number (strict double), or enum (fixed choices).
127
+ * Option kinds: presence (boolean flag), string (free-form text), number (strict double), enum (fixed choices), or json (parsed JSON object/array).
106
128
  */
107
129
  export declare enum CliOptionKind {
108
130
  /** Boolean flag: no value token (may be implicit `"1"` when set). */
@@ -112,7 +134,9 @@ export declare enum CliOptionKind {
112
134
  /** Strict floating-point value (parsed at validation time). */
113
135
  Number = "number",
114
136
  /** Fixed set of allowed string values. Requires non-empty `choices` on the option. */
115
- Enum = "enum"
137
+ Enum = "enum",
138
+ /** JSON object or array (parsed from `--name '<json>'`, piped stdin when `pipable`, or MCP/API tool body). */
139
+ Json = "json"
116
140
  }
117
141
  /**
118
142
  * Named validation/coercion for string options (`format` on `CliOption`).
@@ -176,6 +200,11 @@ export interface CliOption {
176
200
  default?: string;
177
201
  /** Regex pattern for string options. Mutually exclusive with `format`. */
178
202
  pattern?: string;
203
+ /**
204
+ * When `true` on a `Json` option, CLI may omit `--name` and supply JSON via stdin instead.
205
+ * If `--name` is set, the flag value wins and stdin is not read.
206
+ */
207
+ pipable?: boolean;
179
208
  }
180
209
  /**
181
210
  * An ordered positional argument slot, listed on leaf `positionals`.
@@ -584,6 +613,8 @@ export declare class Cli {
584
613
  }): Promise<CliInvokeResult>;
585
614
  serveMcp(): Promise<never>;
586
615
  serveApi(): Promise<never>;
616
+ private ensureValidatedLeafInputs;
617
+ private exitLeafInputError;
587
618
  private prepareDispatch;
588
619
  private buildAppConfigSnapshot;
589
620
  }
@@ -631,6 +662,26 @@ dryRun?: boolean,
631
662
  interactive?: boolean): void;
632
663
  /** Prefixes a success message when running in dry-run mode. */
633
664
  export declare function formatDryRunMessage(message: string, dryRun: boolean): string;
665
+ /** Thrown when leaf input resolution or validation fails. */
666
+ export declare class LeafInputError extends Error {
667
+ constructor(message: string);
668
+ }
669
+ /** Resolves a Json option from argv, preloaded stdin, or toolArgs (flag wins). */
670
+ export declare function readJsonOptionValue(ctx: CliContext, name: string): unknown | undefined;
671
+ /**
672
+ * Reads piped stdin for a pipable Json option when the flag is omitted (CLI only).
673
+ * Call from {@link Cli.run} before constructing the handler context.
674
+ */
675
+ export declare function preloadPipableJson(program: CliProgram, commandPath: string[], opts: Record<string, string>, invocation: CliInvocation): Promise<Record<string, unknown>>;
676
+ /**
677
+ * Loads coerced leaf inputs and validates against `leaf.inputSchema` when set.
678
+ * Used by {@link CliContext.inputs}; prefer `ctx.inputs` or `ctx.inputsAs()` in handlers.
679
+ */
680
+ export declare function loadLeafInputs(ctx: CliContext): CliLeafInputs;
681
+ /** @deprecated Use {@link CliContext.inputs} or {@link loadLeafInputs} via `ctx.inputs`. */
682
+ export declare function readLeafInputs(ctx: CliContext): CliLeafInputs;
683
+ /** @deprecated Use sync {@link readLeafInputs} — stdin is preloaded before the handler runs. */
684
+ export declare function readLeafInputsAsync(ctx: CliContext): Promise<CliLeafInputs>;
634
685
  /** Resolved paths for `mcp bundle`. */
635
686
  export interface McpBundlePaths {
636
687
  binaryPath: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "argsbarg",
3
- "version": "6.0.2",
3
+ "version": "6.1.1",
4
4
  "main": "./src/index.ts",
5
5
  "module": "./src/index.ts",
6
6
  "dependencies": {
@@ -95,7 +95,8 @@ export const program = {
95
95
  },
96
96
  {
97
97
  key: "schemagen",
98
- description: "Generate JSON Schema artifacts from src/**/schema.ts into colocated __generated__/ directories.",
98
+ description:
99
+ "Generate JSON Schema artifacts from src/**/types.ts role exports into colocated __generated__/ directories.",
99
100
  options: [
100
101
  {
101
102
  name: "root",
@@ -5,7 +5,7 @@ Remove stale __generated__/ directories and files after schemagen runs.
5
5
  import { existsSync, readdirSync, rmSync, statSync } from "node:fs";
6
6
  import { dirname, join, relative } from "node:path";
7
7
  import type { SchemaRoot } from "./discover-schema-roots.ts";
8
- import { GENERATED_DIR, SCHEMA_FILE, schemaJsonBasename } from "./names.ts";
8
+ import { GENERATED_DIR, schemaJsonBasename, TYPES_FILE } from "./names.ts";
9
9
 
10
10
  function listGeneratedDirs(srcDir: string, projectRoot: string, out: string[]): void {
11
11
  for (const ent of readdirSync(srcDir)) {
@@ -38,8 +38,8 @@ export function cleanStaleGenerated(
38
38
 
39
39
  for (const relGeneratedDir of generatedDirs) {
40
40
  const generatedDir = join(projectRoot, relGeneratedDir);
41
- const relSchemaPath = relative(projectRoot, join(dirname(generatedDir), SCHEMA_FILE));
42
- const roots = existsSync(join(projectRoot, relSchemaPath)) ? (activeBySchemaFile.get(relSchemaPath) ?? []) : [];
41
+ const relTypesPath = relative(projectRoot, join(dirname(generatedDir), TYPES_FILE));
42
+ const roots = existsSync(join(projectRoot, relTypesPath)) ? (activeBySchemaFile.get(relTypesPath) ?? []) : [];
43
43
 
44
44
  if (roots.length === 0) {
45
45
  rmSync(generatedDir, { recursive: true, force: true });