argsbarg 6.0.1 → 6.1.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/CHANGELOG.md +26 -2
- package/README.md +4 -3
- package/docs/api-server.md +1 -1
- package/docs/cli-program.md +28 -0
- package/docs/config-schema.md +15 -13
- package/docs/output-schema.md +76 -73
- package/examples/full-example/README.md +7 -7
- package/examples/full-example/bun.lock +0 -29
- package/examples/full-example/justfile +6 -3
- package/examples/full-example/package.json +0 -2
- package/examples/full-example/src/commands/status/__generated__/index.ts +5 -0
- package/examples/full-example/src/commands/status/command.ts +2 -2
- package/examples/full-example/src/commands/status/types.ts +19 -1
- package/examples/full-example/src/config/__generated__/index.ts +5 -0
- package/examples/full-example/src/program.ts +4 -4
- package/index.d.ts +24 -3
- package/package.json +8 -1
- package/src/cli-tool/full-example-capabilities.test.ts +2 -1
- package/src/cli-tool/post-create.ts +3 -3
- package/src/cli-tool/program.ts +36 -0
- package/src/cli-tool/run-schemagen.ts +23 -0
- package/src/cli-tool/schemagen/cleanup.ts +60 -0
- package/src/cli-tool/schemagen/discover-schema-roots.ts +173 -0
- package/src/cli-tool/schemagen/index.ts +3 -0
- package/src/cli-tool/schemagen/names.ts +22 -0
- package/src/cli-tool/schemagen/run.ts +109 -0
- package/src/cli-tool/schemagen/schemagen.test.ts +143 -0
- package/src/context.ts +20 -2
- package/src/help.ts +2 -0
- package/src/index.ts +1 -0
- package/src/leaf-inputs.test.ts +170 -0
- package/src/leaf-inputs.ts +178 -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/schemas/configSchemas.ts +0 -6
- package/examples/full-example/schemas/outputSchemas.ts +0 -6
- package/examples/full-example/scripts/schemagen/discover-schema-roots.test.ts +0 -25
- package/examples/full-example/scripts/schemagen/discover-schema-roots.ts +0 -148
- package/examples/full-example/scripts/schemagen/naming.ts +0 -99
- package/examples/full-example/scripts/schemagen.ts +0 -91
- package/examples/full-example/src/commands/status/schema-types.ts +0 -14
- /package/examples/full-example/{schemas/generated/status.json → src/commands/status/__generated__/outputSchema.json} +0 -0
- /package/examples/full-example/{schemas/generated/app-config.json → src/config/__generated__/configSchema.json} +0 -0
- /package/examples/full-example/src/config/{schema-types.ts → types.ts} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [6.1.0] - 2026-07-22
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`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`.
|
|
15
|
+
- **`ctx.readLeafInputsAsync()`** — resolves Json options (flag, stdin, or `toolArgs`) and validates against `leaf.inputSchema` when set.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **Schemagen discovery** — role exports (`configType` / `inputType` / `outputType`) live in `src/**/types.ts` instead of thin `schema.ts` manifests.
|
|
20
|
+
|
|
21
|
+
## [6.0.2] - 2026-07-22
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- **`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`).
|
|
26
|
+
- **`argsbarg/schemagen`** export — `runSchemagen`, `discoverSchemaRoots`, and naming helpers (`src/cli-tool/schemagen/`).
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- **Schemagen convention** — replace per-repo `scripts/schemagen*` copies and `schema-types.ts` bindings with `schema.ts` + gitignored `__generated__/`. Run via `just schemagen` (`argsbarg schemagen` with `node_modules/.bin` on `PATH`).
|
|
31
|
+
|
|
10
32
|
## [6.0.1] - 2026-07-22
|
|
11
33
|
|
|
12
34
|
### Added
|
|
@@ -15,7 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
15
37
|
|
|
16
38
|
### Changed
|
|
17
39
|
|
|
18
|
-
- **
|
|
40
|
+
- **Colocated schemagen** — `*.schema.json` beside each `schema-types.ts`; exports `configSchema` / `inputSchema` / `outputSchema` (replaces central `*Schemas.ts` bridges).
|
|
19
41
|
- **HTTP tool errors** — return a plain `{ "error": "..." }` JSON body without ANSI color codes or appended CLI help text.
|
|
20
42
|
- **`cliErrWithHelp`** — on `api` / `mcp` invocations, throws a plain error instead of printing contextual help.
|
|
21
43
|
- **`GET /openapi-browser`** — Scalar config preserves schema property order from the OpenAPI document (`orderSchemaPropertiesBy: "preserve"`).
|
|
@@ -728,7 +750,9 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
|
|
|
728
750
|
- 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`).
|
|
729
751
|
- Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
|
|
730
752
|
|
|
731
|
-
[Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v6.0
|
|
753
|
+
[Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v6.1.0...HEAD
|
|
754
|
+
[6.1.0]: https://github.com/bdombro/bun-argsbarg/releases/tag/v6.1.0
|
|
755
|
+
[6.0.2]: https://github.com/bdombro/bun-argsbarg/releases/tag/v6.0.2
|
|
732
756
|
[6.0.1]: https://github.com/bdombro/bun-argsbarg/releases/tag/v6.0.1
|
|
733
757
|
[6.0.0]: https://github.com/bdombro/bun-argsbarg/releases/tag/v6.0.0
|
|
734
758
|
[5.1.16]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.1.16
|
package/README.md
CHANGED
|
@@ -208,6 +208,7 @@ Add `CliPositional` entries to the command’s `positionals` list (separate from
|
|
|
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
210
|
- `ctx.readLeafInputs()` — coerced option and positional values for the current leaf (schema-driven).
|
|
211
|
+
- `ctx.readLeafInputsAsync()` — like `readLeafInputs()`, plus Json options (flag, piped stdin, MCP `toolArgs`) and `inputSchema` validation.
|
|
211
212
|
- `ctx.typedOpt<T>("custom", parseFn)` — custom parsing for type-safe option resolution.
|
|
212
213
|
- `ctx.args` — positional words in order as `string[]`.
|
|
213
214
|
- `ctx.positional("name")` — named positional lookup; varargs slots return `string[]`, single slots return `string | undefined`.
|
|
@@ -267,9 +268,9 @@ To refresh the Cursor rule in an existing consumer: `bun scripts/merge-cli-progr
|
|
|
267
268
|
| Area | Files / wiring |
|
|
268
269
|
| --------------------- | -------------------------------------------------------------------------------------- |
|
|
269
270
|
| All builtins | `completion`, `version`, `configure`, `docs`, `mcp`, `api`, `configure get`/`set` |
|
|
270
|
-
| `program.appConfig` | `src/config/
|
|
271
|
-
| `outputSchema` | `src/commands/status/
|
|
272
|
-
| Schemagen | `
|
|
271
|
+
| `program.appConfig` | `src/config/types.ts` → `configSchema` from `__generated__/` |
|
|
272
|
+
| `outputSchema` | `src/commands/status/types.ts` → `outputSchema` from `__generated__/` |
|
|
273
|
+
| Schemagen | `just schemagen` → `argsbarg schemagen` (justfile exports `node_modules/.bin` on `PATH`) |
|
|
273
274
|
| Command layout | `src/commands/<name>/command.ts`; registration in `src/program.ts` |
|
|
274
275
|
| MCP doc topics | `docs.topics` auto-exposed as `<key>://docs/<topic>` resources when docs + MCP enabled |
|
|
275
276
|
| Package import | `from "argsbarg"` (not relative to argsbarg `src/`) |
|
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 `await ctx.readLeafInputsAsync()` — it merges flag values, piped stdin, and the original flat JSON body from `POST /tools/:name`.
|
package/docs/cli-program.md
CHANGED
|
@@ -223,9 +223,37 @@ const { limit, "skip-readiness": skipReadiness, timeout } = ctx.readLeafInputs()
|
|
|
223
223
|
| `format: date-time` | `string` (normalized UTC ISO) |
|
|
224
224
|
| Single positional | `string` or `undefined` |
|
|
225
225
|
| Varargs positional | `string[]` or `undefined` |
|
|
226
|
+
| `kind: json` | parsed object/array or `undefined` (use `readLeafInputsAsync()` for pipable stdin and MCP merge) |
|
|
226
227
|
|
|
227
228
|
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.
|
|
228
229
|
|
|
230
|
+
### Json options and piped stdin
|
|
231
|
+
|
|
232
|
+
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:
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
{
|
|
236
|
+
name: "invoice",
|
|
237
|
+
description: "Invoice template data. Pass JSON via --invoice or pipe to stdin.",
|
|
238
|
+
kind: CliOptionKind.Json,
|
|
239
|
+
pipable: true,
|
|
240
|
+
required: true,
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
| Surface | How `invoice` is supplied |
|
|
245
|
+
| --- | --- |
|
|
246
|
+
| CLI | `--invoice '<json>'` **or** omit the flag and pipe JSON to stdin |
|
|
247
|
+
| MCP / HTTP | `invoice` object in the tool JSON body (`ctx.toolArgs`) |
|
|
248
|
+
|
|
249
|
+
**Precedence:** if `--invoice` is set, the flag value wins and stdin is not read.
|
|
250
|
+
|
|
251
|
+
Use **`await ctx.readLeafInputsAsync()`** (not sync `readLeafInputs()`) so Json options resolve from flags, piped stdin, or `toolArgs`. When `leaf.inputSchema` is set, argsbarg validates the merged inputs (same JSON Schema subset as `program.appConfig`).
|
|
252
|
+
|
|
253
|
+
At most one `pipable` Json option per leaf. Json option names must appear in `inputSchema.properties` when a custom `inputSchema` is set.
|
|
254
|
+
|
|
255
|
+
See [output-schema.md](output-schema.md) for schemagen `inputType` and [api-server.md](api-server.md) for HTTP tool bodies.
|
|
256
|
+
|
|
229
257
|
`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
258
|
|
|
231
259
|
See [examples/formats.ts](../examples/formats.ts) for a runnable demo.
|
package/docs/config-schema.md
CHANGED
|
@@ -136,42 +136,42 @@ Interactive `configure` does not persist values supplied only by env or `resolve
|
|
|
136
136
|
| **Omit `jsonSchema`** | Simple apps; all values stored as strings; use `entry.default` |
|
|
137
137
|
| **Codegen from TypeScript** | Typed config, nested objects, shared with JSON Schema CI |
|
|
138
138
|
|
|
139
|
-
## Recommended pipeline (
|
|
139
|
+
## Recommended pipeline (argsbarg schemagen)
|
|
140
140
|
|
|
141
141
|
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
|
-
subgraph gen [
|
|
149
|
-
Script["
|
|
148
|
+
subgraph gen [argsbarg schemagen]
|
|
149
|
+
Script["argsbarg schemagen"]
|
|
150
150
|
Gen["ts-json-schema-generator"]
|
|
151
151
|
end
|
|
152
|
-
subgraph artifacts [
|
|
153
|
-
Json["
|
|
154
|
-
|
|
152
|
+
subgraph artifacts [Gitignored __generated__]
|
|
153
|
+
Json["configSchema.json"]
|
|
154
|
+
Index["index.ts"]
|
|
155
155
|
end
|
|
156
156
|
subgraph runtime [Runtime]
|
|
157
157
|
Program["program.appConfig.jsonSchema"]
|
|
158
158
|
Validate["argsbarg runtime subset validator"]
|
|
159
159
|
end
|
|
160
160
|
types --> Script --> Gen --> Json
|
|
161
|
-
|
|
161
|
+
Gen --> Index --> Program --> Validate
|
|
162
162
|
```
|
|
163
163
|
|
|
164
164
|
| Piece | Convention |
|
|
165
165
|
| --- | --- |
|
|
166
|
-
| Generator | [`ts-json-schema-generator`](https://github.com/vega/ts-json-schema-generator) |
|
|
167
|
-
| Discovery | `export type configType = …` in `src/config/
|
|
168
|
-
| Artifacts |
|
|
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/types.ts` |
|
|
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
|
}
|
|
@@ -179,6 +179,8 @@ export interface AppConfig {
|
|
|
179
179
|
export type configType = AppConfig;
|
|
180
180
|
```
|
|
181
181
|
|
|
182
|
+
Wire on the program root: `import { configSchema } from "./config/__generated__/index.ts"`.
|
|
183
|
+
|
|
182
184
|
### Supported AppConfig shapes (argsbarg runtime validator)
|
|
183
185
|
|
|
184
186
|
| Supported (v1) | Deferred |
|
|
@@ -220,7 +222,7 @@ Object/array/`$ref` properties require `--json` on `configure set` when comma-se
|
|
|
220
222
|
|
|
221
223
|
| Example | Role |
|
|
222
224
|
| --- | --- |
|
|
223
|
-
| [`examples/full-example/`](../examples/full-example/) | **Copy template** —
|
|
225
|
+
| [`examples/full-example/`](../examples/full-example/) | **Copy template** — `argsbarg schemagen`, `program.appConfig`, built-in `configure get`/`set` |
|
|
224
226
|
|
|
225
227
|
```bash
|
|
226
228
|
cd examples/full-example && just setup && just schemagen
|
package/docs/output-schema.md
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
# Output schemas (`outputSchema`)
|
|
2
2
|
|
|
3
|
-
How to describe JSON stdout on leaf commands — and
|
|
3
|
+
How to describe JSON stdout on leaf commands — and the **argsbarg schemagen** pipeline used in production apps.
|
|
4
4
|
|
|
5
5
|
## Argsbarg contract
|
|
6
6
|
|
|
7
7
|
On **leaf commands**, set `outputSchema` to a JSON Schema object when the handler emits JSON (typically with `--json`, always for JSON-only commands, or on the MCP headless path).
|
|
8
8
|
|
|
9
9
|
```typescript
|
|
10
|
-
import {
|
|
10
|
+
import { outputSchema } from "./__generated__/index.ts";
|
|
11
11
|
|
|
12
12
|
export const status = {
|
|
13
13
|
key: "status",
|
|
14
14
|
description: "Show environment status.",
|
|
15
|
-
outputSchema
|
|
15
|
+
outputSchema,
|
|
16
16
|
handler: async (ctx) => { /* writes JSON to stdout */ },
|
|
17
17
|
} satisfies CliLeaf;
|
|
18
18
|
```
|
|
@@ -42,58 +42,52 @@ See [cli-program.md — Structured stdout](cli-program.md#structured-stdout) for
|
|
|
42
42
|
|
|
43
43
|
Production CLIs with several JSON commands tend to use **codegen** so types, handlers, and schemas stay aligned.
|
|
44
44
|
|
|
45
|
-
##
|
|
45
|
+
## Schemagen pipeline (built into argsbarg)
|
|
46
46
|
|
|
47
|
-
No
|
|
47
|
+
No per-repo scripts to copy — run **`argsbarg schemagen`** (or `import { runSchemagen } from "argsbarg/schemagen"`).
|
|
48
|
+
|
|
49
|
+
Reference implementations: **sqsp-qa-manager-poc**, **sqsp-workspaces**, **sqsp-i18n-tools-poc**, **pdf-gen** (see each repo’s `docs/architecture.md` for which commands use which schema root).
|
|
48
50
|
|
|
49
51
|
```mermaid
|
|
50
52
|
flowchart LR
|
|
51
|
-
subgraph types [
|
|
53
|
+
subgraph types [types.ts]
|
|
52
54
|
Config["export type configType = AppConfig"]
|
|
53
55
|
Output["export type outputType = StatusJsonOutput"]
|
|
54
56
|
Input["export type inputType = ToolInput (optional)"]
|
|
55
57
|
end
|
|
56
|
-
subgraph gen [
|
|
57
|
-
Discover["discover
|
|
58
|
-
Script["schemagen.ts / generate-output-schemas.ts"]
|
|
58
|
+
subgraph gen [argsbarg schemagen]
|
|
59
|
+
Discover["discover types.ts roots"]
|
|
59
60
|
Gen["ts-json-schema-generator"]
|
|
60
61
|
end
|
|
61
|
-
subgraph artifacts [
|
|
62
|
-
Json["
|
|
63
|
-
|
|
62
|
+
subgraph artifacts [Gitignored __generated__]
|
|
63
|
+
Json["configSchema.json / inputSchema.json / outputSchema.json"]
|
|
64
|
+
Index["index.ts re-exports"]
|
|
64
65
|
end
|
|
65
66
|
subgraph runtime [Runtime]
|
|
66
|
-
Leaves["
|
|
67
|
+
Leaves["import { outputSchema } from ./__generated__/index.ts"]
|
|
67
68
|
Docgen["just docgen"]
|
|
68
69
|
end
|
|
69
|
-
types --> Discover -->
|
|
70
|
-
|
|
70
|
+
types --> Discover --> Gen --> Json
|
|
71
|
+
Gen --> Index --> Leaves --> Docgen
|
|
71
72
|
```
|
|
72
73
|
|
|
73
74
|
| Piece | Convention |
|
|
74
75
|
| --- | --- |
|
|
75
|
-
| Generator | [`ts-json-schema-generator`](https://github.com/vega/ts-json-schema-generator) (
|
|
76
|
-
|
|
|
77
|
-
|
|
|
78
|
-
|
|
|
76
|
+
| Generator | [`ts-json-schema-generator`](https://github.com/vega/ts-json-schema-generator) (bundled as an argsbarg dependency) |
|
|
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
|
+
| Invocation | `just schemagen` — justfile exports `node_modules/.bin` on `PATH` for local `argsbarg` |
|
|
79
80
|
| tsconfig | `"resolveJsonModule": true` |
|
|
80
|
-
| CI | `just check`: `schemagen` →
|
|
81
|
+
| CI | `just check`: `schemagen` → typecheck (no git diff on generated files) |
|
|
82
|
+
| Cleanup | Schemagen removes orphan `__generated__/` dirs and stale JSON when roots or kinds are removed |
|
|
81
83
|
| Docgen | `docgen` depends on `schemagen` so saved `./docs/api.md` and `./docs/cli-schema.json` are fresh |
|
|
82
84
|
|
|
83
|
-
Copy these scripts into each consumer repo (they are intentionally duplicated, not published):
|
|
84
|
-
|
|
85
|
-
- `scripts/schemagen.ts` or `scripts/generate-output-schemas.ts` — generate JSON + rewrite bridges
|
|
86
|
-
- `scripts/schemagen/discover-schema-roots.ts` — find roots and map names → filenames / export constants
|
|
87
|
-
- `scripts/schemagen/discover-schema-roots.test.ts` — lock discovery and naming per app
|
|
88
|
-
|
|
89
85
|
### Declaring a schema root
|
|
90
86
|
|
|
91
|
-
Put schema-facing interfaces in **`
|
|
87
|
+
Put schema-facing interfaces and role exports in **`types.ts`** (or `core/types.ts` for shared shapes):
|
|
92
88
|
|
|
93
89
|
```typescript
|
|
94
|
-
// src/commands/status/
|
|
95
|
-
import type { WorkspaceStatus } from "./types.ts";
|
|
96
|
-
|
|
90
|
+
// src/commands/status/types.ts
|
|
97
91
|
/** JSON stdout for `myapp status --json`. */
|
|
98
92
|
export interface StatusJsonOutput {
|
|
99
93
|
workspaces: WorkspaceStatus[];
|
|
@@ -103,10 +97,10 @@ export interface StatusJsonOutput {
|
|
|
103
97
|
export type outputType = StatusJsonOutput;
|
|
104
98
|
```
|
|
105
99
|
|
|
106
|
-
|
|
107
|
-
// src/ui/runHeadless/schema-types.ts — shared by many mutating commands
|
|
108
|
-
import type { HeadlessTaskResult } from "./types.ts";
|
|
100
|
+
Handlers and other modules import from the same **`types.ts`** module.
|
|
109
101
|
|
|
102
|
+
```typescript
|
|
103
|
+
// src/ui/runHeadless/types.ts — shared by many mutating commands
|
|
110
104
|
export interface HeadlessOpResult {
|
|
111
105
|
command: string;
|
|
112
106
|
exitCode: number;
|
|
@@ -117,70 +111,80 @@ export type outputType = HeadlessOpResult;
|
|
|
117
111
|
```
|
|
118
112
|
|
|
119
113
|
```typescript
|
|
120
|
-
// src/commands/render-invoice/
|
|
121
|
-
export interface
|
|
114
|
+
// src/commands/render-invoice/types.ts — custom HTTP/MCP body (pdf-gen)
|
|
115
|
+
export interface RenderInvoiceInput {
|
|
122
116
|
format: "pdf" | "html";
|
|
123
117
|
invoice: InvoiceData;
|
|
124
118
|
}
|
|
125
119
|
|
|
126
|
-
export
|
|
127
|
-
|
|
120
|
+
export interface RenderInvoiceOutput {
|
|
121
|
+
bytes: number;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export type inputType = RenderInvoiceInput;
|
|
125
|
+
export type outputType = RenderInvoiceOutput;
|
|
128
126
|
```
|
|
129
127
|
|
|
130
128
|
| Export | Role |
|
|
131
129
|
| --- | --- |
|
|
132
|
-
| `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`) |
|
|
133
131
|
| `export type outputType = …` | `leaf.outputSchema` |
|
|
134
132
|
| `export type inputType = …` | `leaf.inputSchema` (only when the tool body is not flat CLI flags) |
|
|
135
133
|
|
|
136
|
-
**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`.
|
|
137
135
|
|
|
138
|
-
|
|
136
|
+
For nested MCP/HTTP bodies, add a `kind: Json` option (same name as the `inputType` property) and use `await ctx.readLeafInputsAsync()` — see [cli-program.md](cli-program.md#json-options-and-piped-stdin).
|
|
139
137
|
|
|
140
138
|
When you do **not** set `inputSchema`, argsbarg builds tool input from CLI `options` + `positionals`.
|
|
141
139
|
|
|
142
|
-
###
|
|
140
|
+
### Generated artifacts
|
|
143
141
|
|
|
144
|
-
|
|
142
|
+
Schemagen writes under `__generated__/` beside each `types.ts` with role exports:
|
|
145
143
|
|
|
146
|
-
|
|
|
147
|
-
| --- | --- | --- |
|
|
148
|
-
|
|
|
149
|
-
|
|
|
150
|
-
|
|
|
151
|
-
| `Result` | `PrResult` | `pr.json` | `PR_RESULT_OUTPUT_SCHEMA` |
|
|
152
|
-
| `ToolInput` | `RenderInvoiceToolInput` | `render-invoice-tool-input.json` | `RENDER_INVOICE_TOOL_INPUT_SCHEMA` |
|
|
144
|
+
| Kind | Generated file | Exported const (from `__generated__/index.ts`) |
|
|
145
|
+
| --- | --- | --- |
|
|
146
|
+
| config | `configSchema.json` | `configSchema` |
|
|
147
|
+
| output | `outputSchema.json` | `outputSchema` |
|
|
148
|
+
| input | `inputSchema.json` | `inputSchema` |
|
|
153
149
|
|
|
154
|
-
|
|
150
|
+
Wire on the leaf:
|
|
155
151
|
|
|
156
|
-
|
|
152
|
+
```typescript
|
|
153
|
+
import { outputSchema } from "./__generated__/index.ts";
|
|
157
154
|
|
|
158
|
-
|
|
155
|
+
export const statusCommand = {
|
|
156
|
+
outputSchema,
|
|
157
|
+
// …
|
|
158
|
+
} satisfies CliLeaf;
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Shared mutators:
|
|
159
162
|
|
|
160
163
|
```typescript
|
|
161
|
-
|
|
164
|
+
import { outputSchema } from "../../../ui/runHeadless/__generated__/index.ts";
|
|
165
|
+
```
|
|
162
166
|
|
|
163
|
-
|
|
167
|
+
App config:
|
|
164
168
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
```
|
|
169
|
+
```typescript
|
|
170
|
+
import { configSchema } from "./config/__generated__/index.ts";
|
|
168
171
|
|
|
169
|
-
|
|
172
|
+
appConfig: { jsonSchema: configSchema, entries: { … } },
|
|
173
|
+
```
|
|
170
174
|
|
|
171
175
|
## Schema-facing types
|
|
172
176
|
|
|
173
177
|
**Goal:** generated schemas match what handlers actually print, with descriptions agents can read in `docs api`.
|
|
174
178
|
|
|
175
|
-
1. **Schema roots** — `export interface` in `
|
|
179
|
+
1. **Schema roots** — `export interface` in `types.ts`, with `export type outputType = …` (or `inputType` / `configType`).
|
|
176
180
|
2. **Per property** — `/** … */` on every field that should appear in JSON Schema `properties` (including nested named types).
|
|
177
181
|
3. **Unions / enums** — document the alias; generator emits `enum` / `anyOf` with type-level description.
|
|
178
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"`.
|
|
179
|
-
5. **Do not hand-edit** `
|
|
183
|
+
5. **Do not hand-edit** `__generated__/` — change types/JSDoc in `types.ts`, run `just schemagen`.
|
|
180
184
|
|
|
181
185
|
### Narrowing when runtime ≠ stdout
|
|
182
186
|
|
|
183
|
-
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`:
|
|
184
188
|
|
|
185
189
|
```typescript
|
|
186
190
|
/** JSON stdout for `myapp pr` and `myapp file`. */
|
|
@@ -201,27 +205,26 @@ Handlers keep using runtime types; only discovered roots (and their type graph)
|
|
|
201
205
|
|
|
202
206
|
## Tests
|
|
203
207
|
|
|
204
|
-
|
|
208
|
+
In argsbarg: `src/cli-tool/schemagen/schemagen.test.ts` locks discovery and generation against `examples/full-example/`.
|
|
209
|
+
|
|
210
|
+
Per consumer repo (optional):
|
|
205
211
|
|
|
206
|
-
- **`
|
|
207
|
-
- **`schemas/outputSchemas.test.ts`** (optional) — schema shape smoke tests: object root, key `description` fields, enums, `@format date-time`.
|
|
212
|
+
- **`src/generated-schemas.test.ts`** — smoke-test that key `outputSchema` objects have expected shape.
|
|
208
213
|
|
|
209
214
|
## Contributor workflow
|
|
210
215
|
|
|
211
|
-
1. Add or edit schema roots in `src/**/
|
|
212
|
-
2. `just schemagen` — refresh `
|
|
213
|
-
3. Import
|
|
214
|
-
4.
|
|
215
|
-
5.
|
|
216
|
-
6. Document which commands use which roots in **your** `docs/architecture.md` (argsbarg does not maintain per-app tables).
|
|
216
|
+
1. Add or edit schema roots in `src/**/types.ts` with `outputType` / `inputType` / `configType` and per-field JSDoc.
|
|
217
|
+
2. `just schemagen` — refresh `src/**/__generated__/`.
|
|
218
|
+
3. Import `{ outputSchema }` / `{ inputSchema }` / `{ configSchema }` from the relevant `__generated__/index.ts`.
|
|
219
|
+
4. `just docgen` / `myapp docs api --save` — refresh consumer docs.
|
|
220
|
+
5. Document which commands use which roots in **your** `docs/architecture.md` (argsbarg does not maintain per-app tables).
|
|
217
221
|
|
|
218
|
-
Add a bullet under your app’s `**… conventions:**` block in `.cursor/rules/cli-program.mdc` pointing at `node_modules/argsbarg/docs/output-schema.md
|
|
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`.
|
|
219
223
|
|
|
220
|
-
**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`.
|
|
221
225
|
|
|
222
226
|
## Out of scope
|
|
223
227
|
|
|
224
|
-
- Shared codegen package or monorepo tooling
|
|
225
228
|
- Runtime Zod / `.parse()` on stdout in argsbarg
|
|
226
229
|
- `outputSchema` for plain-text, streaming, or Ink-only commands
|
|
227
230
|
|
|
@@ -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 `
|
|
66
|
-
| --- | --- |
|
|
67
|
-
| `export type configType = …` | `
|
|
68
|
-
| `export type outputType = …` | `
|
|
69
|
-
| `export type inputType = …` | `
|
|
65
|
+
| Export in `types.ts` | Generated artifact | Import on leaf / program |
|
|
66
|
+
| --- | --- | --- |
|
|
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
|
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
"devDependencies": {
|
|
11
11
|
"@biomejs/biome": "^2.5.0",
|
|
12
12
|
"@types/bun": "^1.3.12",
|
|
13
|
-
"ts-json-schema-generator": "^2.3.0",
|
|
14
13
|
"typescript": "^5.9.3",
|
|
15
14
|
},
|
|
16
15
|
},
|
|
@@ -36,40 +35,12 @@
|
|
|
36
35
|
|
|
37
36
|
"@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="],
|
|
38
37
|
|
|
39
|
-
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
|
40
|
-
|
|
41
38
|
"@types/node": ["@types/node@26.0.0", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA=="],
|
|
42
39
|
|
|
43
40
|
"argsbarg": ["argsbarg@file:../..", { "devDependencies": { "@biomejs/biome": "^2.5.0", "@types/bun": "^1.3.12", "typescript": "^5.9.3" }, "bin": { "argsbarg": "src/index.ts" } }],
|
|
44
41
|
|
|
45
|
-
"balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
|
|
46
|
-
|
|
47
|
-
"brace-expansion": ["brace-expansion@5.0.6", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g=="],
|
|
48
|
-
|
|
49
42
|
"bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="],
|
|
50
43
|
|
|
51
|
-
"commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="],
|
|
52
|
-
|
|
53
|
-
"glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="],
|
|
54
|
-
|
|
55
|
-
"json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
|
|
56
|
-
|
|
57
|
-
"lru-cache": ["lru-cache@11.5.1", "", {}, "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A=="],
|
|
58
|
-
|
|
59
|
-
"minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
|
|
60
|
-
|
|
61
|
-
"minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="],
|
|
62
|
-
|
|
63
|
-
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
|
|
64
|
-
|
|
65
|
-
"path-scurry": ["path-scurry@2.0.2", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg=="],
|
|
66
|
-
|
|
67
|
-
"safe-stable-stringify": ["safe-stable-stringify@2.5.0", "", {}, "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA=="],
|
|
68
|
-
|
|
69
|
-
"ts-json-schema-generator": ["ts-json-schema-generator@2.9.0", "", { "dependencies": { "@types/json-schema": "^7.0.15", "commander": "^14.0.3", "glob": "^13.0.6", "json5": "^2.2.3", "normalize-path": "^3.0.0", "safe-stable-stringify": "^2.5.0", "tslib": "^2.8.1", "typescript": "^5.9.3" }, "bin": { "ts-json-schema-generator": "bin/ts-json-schema-generator.js" } }, "sha512-NR5ZE108uiPtBHBJNGnhwoUaUx5vWTDJzDFG9YlRoqxPU76n+5FClRh92dcGgysbe1smRmYalM9Saj97GW1J4Q=="],
|
|
70
|
-
|
|
71
|
-
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
|
72
|
-
|
|
73
44
|
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
|
74
45
|
|
|
75
46
|
"undici-types": ["undici-types@8.3.0", "", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="],
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
set shell := ["bash", "-eu", "-o", "pipefail", "-c"]
|
|
2
2
|
|
|
3
|
+
export PATH := justfile_directory() + "/node_modules/.bin:" + env_var("PATH")
|
|
4
|
+
|
|
3
5
|
cli_key := `bun scripts/print-identity.ts key`
|
|
4
6
|
tap_org := `bun scripts/print-identity.ts tapOrg`
|
|
5
7
|
tap_repo := `bun scripts/print-identity.ts tapRepo`
|
|
@@ -19,9 +21,8 @@ build:
|
|
|
19
21
|
bun build ./src/index.ts --compile --outfile=dist/{{cli_key}}
|
|
20
22
|
@rm -f .*.bun-build
|
|
21
23
|
|
|
22
|
-
# Run schemagen,
|
|
24
|
+
# Run schemagen, typecheck, and format
|
|
23
25
|
check: schemagen format typecheck
|
|
24
|
-
git diff --exit-code schemas/
|
|
25
26
|
|
|
26
27
|
# Run the CLI from source with optional args; restarts on file changes
|
|
27
28
|
dev *ARGS:
|
|
@@ -84,11 +85,13 @@ run *ARGS:
|
|
|
84
85
|
|
|
85
86
|
# Generate JSON Schema artifacts from TypeScript types
|
|
86
87
|
schemagen:
|
|
87
|
-
|
|
88
|
+
argsbarg schemagen
|
|
88
89
|
|
|
89
90
|
# Install bun/npm dependencies
|
|
91
|
+
# Install bun/npm dependencies and generate schemas
|
|
90
92
|
setup:
|
|
91
93
|
bun install
|
|
94
|
+
just schemagen
|
|
92
95
|
|
|
93
96
|
# Run unit tests (after check)
|
|
94
97
|
test: check
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"biome": "biome",
|
|
12
|
-
"schemagen": "bun run scripts/schemagen.ts",
|
|
13
12
|
"start": "bun run src/index.ts"
|
|
14
13
|
},
|
|
15
14
|
"dependencies": {
|
|
@@ -18,7 +17,6 @@
|
|
|
18
17
|
"devDependencies": {
|
|
19
18
|
"@biomejs/biome": "^2.5.0",
|
|
20
19
|
"@types/bun": "^1.3.12",
|
|
21
|
-
"ts-json-schema-generator": "^2.3.0",
|
|
22
20
|
"typescript": "^5.9.3"
|
|
23
21
|
}
|
|
24
22
|
}
|
|
@@ -3,7 +3,7 @@ Status leaf — demonstrates outputSchema and ctx.appConfig.
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { type CliLeaf, CliOptionKind } from "argsbarg";
|
|
6
|
-
import {
|
|
6
|
+
import { outputSchema } from "./__generated__/index.ts";
|
|
7
7
|
import type { StatusJsonOutput } from "./types.ts";
|
|
8
8
|
|
|
9
9
|
export const statusCommand = {
|
|
@@ -16,7 +16,7 @@ export const statusCommand = {
|
|
|
16
16
|
kind: CliOptionKind.Presence,
|
|
17
17
|
},
|
|
18
18
|
],
|
|
19
|
-
outputSchema
|
|
19
|
+
outputSchema,
|
|
20
20
|
handler: (ctx) => {
|
|
21
21
|
const out: StatusJsonOutput = {
|
|
22
22
|
defaultRegion: ctx.appConfig.get("defaultRegion") as string | undefined,
|