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 +29 -2
- package/README.md +7 -3
- package/docs/api-server.md +1 -1
- package/docs/cli-program.md +36 -5
- package/docs/config-schema.md +3 -3
- package/docs/output-schema.md +26 -24
- package/examples/full-example/README.md +6 -6
- package/examples/full-example/src/commands/status/types.ts +19 -1
- package/examples/full-example/src/program.ts +1 -1
- package/index.d.ts +57 -6
- package/package.json +1 -1
- package/src/cli-tool/program.ts +2 -1
- package/src/cli-tool/schemagen/cleanup.ts +3 -3
- package/src/cli-tool/schemagen/discover-schema-roots.ts +85 -15
- package/src/cli-tool/schemagen/index.ts +1 -1
- package/src/cli-tool/schemagen/names.ts +3 -3
- package/src/cli-tool/schemagen/run.ts +2 -1
- package/src/cli-tool/schemagen/schemagen.test.ts +35 -17
- package/src/cli.ts +54 -1
- package/src/context.ts +45 -44
- package/src/help.ts +2 -0
- package/src/index.ts +8 -0
- package/src/leaf-inputs.test.ts +279 -0
- package/src/leaf-inputs.ts +195 -0
- package/src/mcp/tools.ts +5 -0
- package/src/parse.ts +11 -0
- package/src/types.ts +8 -1
- package/src/validate.ts +43 -0
- package/examples/full-example/src/commands/status/schema.ts +0 -14
- /package/examples/full-example/src/config/{schema.ts → types.ts} +0 -0
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
|
|
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.
|
|
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.
|
|
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/
|
|
271
|
-
| `outputSchema` | `src/commands/status/
|
|
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 |
|
package/docs/api-server.md
CHANGED
|
@@ -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.
|
|
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.
|
package/docs/cli-program.md
CHANGED
|
@@ -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
|
-
**`
|
|
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.
|
|
210
|
-
//
|
|
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
|
-
**`
|
|
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
|
-
|
|
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
|
|
package/docs/config-schema.md
CHANGED
|
@@ -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 [
|
|
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/
|
|
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/
|
|
174
|
+
// src/config/types.ts
|
|
175
175
|
export interface AppConfig {
|
|
176
176
|
apiToken: string;
|
|
177
177
|
}
|
package/docs/output-schema.md
CHANGED
|
@@ -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 [
|
|
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
|
|
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/**/
|
|
78
|
-
| Artifacts | `src/**/__generated__/` — gitignored; run schemagen after clone or when `
|
|
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 **`
|
|
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/
|
|
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
|
-
|
|
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/
|
|
117
|
-
export interface
|
|
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
|
|
123
|
-
|
|
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/
|
|
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**
|
|
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
|
-
|
|
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 `
|
|
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 `
|
|
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 `
|
|
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 `
|
|
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/**/
|
|
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 — `
|
|
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/**/
|
|
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 `
|
|
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`
|
|
68
|
-
| `export type outputType = …` | `__generated__/outputSchema.json` | `{ outputSchema }` from `__generated__/index.ts`
|
|
69
|
-
| `export type inputType = …` | `__generated__/inputSchema.json` | `{ inputSchema }` from `__generated__/index.ts`
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
/**
|
|
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
|
-
|
|
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),
|
|
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
package/src/cli-tool/program.ts
CHANGED
|
@@ -95,7 +95,8 @@ export const program = {
|
|
|
95
95
|
},
|
|
96
96
|
{
|
|
97
97
|
key: "schemagen",
|
|
98
|
-
description:
|
|
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,
|
|
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
|
|
42
|
-
const roots = existsSync(join(projectRoot,
|
|
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 });
|