argsbarg 3.6.2 → 3.6.4
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 +28 -1
- package/README.md +6 -2
- package/docs/README.md +33 -0
- package/docs/bundled-docs.md +15 -0
- package/docs/cli-program.md +66 -4
- package/docs/developing.md +54 -0
- package/docs/mcp.md +1 -1
- package/docs/output-schema.md +181 -0
- package/docs/templates/cursor/rules/cli-program.mdc +5 -3
- package/examples/formats.ts +66 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [3.6.4] - 2026-06-23
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`docs/output-schema.md`** — recommended TypeScript → JSON Schema codegen pipeline for leaf `outputSchema` (manifest, bridge, JSDoc, narrowing, CI).
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- **`docs/README.md`**, **`cli-program.md`**, **`bundled-docs.md`**, Cursor rule template — cross-links to output-schema guide.
|
|
19
|
+
|
|
20
|
+
## [3.6.3] - 2026-06-23
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- **`docs/README.md`** — documentation map (framework vs consumer docgen).
|
|
25
|
+
- **`docs/developing.md`** — maintainer workflow (`consumer-dev`, `consumers-sync`, npm `files`).
|
|
26
|
+
- **`examples/formats.ts`** — `CliValueFormat`, `default`, and `readLeafInputs()` demo.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- **`cli-program.md`** — `CliLeafInputs` / `readLeafInputs()` semantics, upgrading to 3.6+, read-once-resolve-once cross-links.
|
|
31
|
+
- **`bundled-docs.md`** — framework docs vs consumer docgen.
|
|
32
|
+
- **`docs/mcp.md`** — varargs JSON array only (fixes stale comma-string guidance).
|
|
33
|
+
- **`consumers-dev` / `consumers-sync`** — refresh consumer `.cursor/rules/cli-program.mdc` from template via `scripts/merge-cli-program-rule.ts`.
|
|
34
|
+
|
|
10
35
|
## [3.6.2] - 2026-06-23
|
|
11
36
|
|
|
12
37
|
### Added
|
|
@@ -399,7 +424,9 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
|
|
|
399
424
|
- 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`).
|
|
400
425
|
- Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
|
|
401
426
|
|
|
402
|
-
[Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v3.6.
|
|
427
|
+
[Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v3.6.4...HEAD
|
|
428
|
+
[3.6.4]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.6.4
|
|
429
|
+
[3.6.3]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.6.3
|
|
403
430
|
[3.6.2]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.6.2
|
|
404
431
|
[3.6.1]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.6.1
|
|
405
432
|
[3.6.0]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.6.0
|
package/README.md
CHANGED
|
@@ -155,7 +155,7 @@ mkdir -p .cursor/rules
|
|
|
155
155
|
cp node_modules/argsbarg/docs/templates/cursor/rules/cli-program.mdc .cursor/rules/cli-program.mdc
|
|
156
156
|
```
|
|
157
157
|
|
|
158
|
-
Add app-specific conventions in a second rule if needed.
|
|
158
|
+
Add app-specific conventions in a second rule if needed. Copy the rule from the template, then add a `**<your-app> conventions:**` block at the bottom (see **Cursor rule** in [docs/cli-program.md](docs/cli-program.md)). Documentation map: **[docs/README.md](docs/README.md)**.
|
|
159
159
|
|
|
160
160
|
|
|
161
161
|
## How it works
|
|
@@ -214,6 +214,7 @@ Check the `examples/` directory for full working scripts:
|
|
|
214
214
|
| --- | --- | --- |
|
|
215
215
|
| `ArgsBargMinimal` | `examples/minimal.ts` | String + presence flags, `MissingOrUnknown` fallback. |
|
|
216
216
|
| `ArgsBargNested` | `examples/nested.ts` | Nested command tree, positional tails, async handlers. |
|
|
217
|
+
| `ArgsBargFormats` | `examples/formats.ts` | `CliValueFormat`, `default`, `readLeafInputs()`. |
|
|
217
218
|
|
|
218
219
|
```bash
|
|
219
220
|
export PATH="$PATH:$(pwd)/examples"
|
|
@@ -225,6 +226,8 @@ minimal.ts hello --name world
|
|
|
225
226
|
eval "$(nested.ts completion zsh)"
|
|
226
227
|
nested.ts stat owner lookup -u alice ./README.md
|
|
227
228
|
nested.ts read ./README.md
|
|
229
|
+
|
|
230
|
+
bun ./examples/formats.ts run --tags demo,docs --on 2026-06-22
|
|
228
231
|
```
|
|
229
232
|
|
|
230
233
|
|
|
@@ -238,7 +241,8 @@ The package root (`argsbarg` / `src/index.ts`) exports the types and runtime you
|
|
|
238
241
|
| `CliProgram`, `CliOption`, `CliPositional`, `CliHandler` | Schema and handler types. |
|
|
239
242
|
| `CliOptionKind`, `CliValueFormat`, `CliFallbackMode` | Option kinds, value formats (`duration`, `comma-list`, `date`, `date-time`), and root fallback behavior. |
|
|
240
243
|
| `CliSchemaValidationError` | Thrown when the static command tree violates schema rules. |
|
|
241
|
-
| `CliContext` | Handler context (`ctx.
|
|
244
|
+
| `CliContext` | Handler context (`ctx.hasFlag`, `ctx.stringOpt`, `ctx.durationOpt`, `ctx.readLeafInputs`, `ctx.invocation`, …). |
|
|
245
|
+
| `CliLeafInputs` | Record type returned by `readLeafInputs()` — coerced option/positional values keyed by schema name. |
|
|
242
246
|
| `cliRun(root, [argv])` | Validate, parse argv, dispatch, exit. |
|
|
243
247
|
| `cliInvoke(root, argv)` | Parse and dispatch without exiting; returns captured stdout/stderr. |
|
|
244
248
|
| `cliErrWithHelp(ctx, msg)` | Print error + scoped help on stderr, exit 1. |
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Argsbarg documentation
|
|
2
|
+
|
|
3
|
+
Start here to pick the right guide.
|
|
4
|
+
|
|
5
|
+
| If you are… | Read |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| **New to argsbarg** | [../README.md](../README.md) — install, minimal usage, public API |
|
|
8
|
+
| **Authoring a `CliProgram`** (humans or agents) | [cli-program.md](cli-program.md) — schema, formats, headless, `read*Flags` |
|
|
9
|
+
| **JSON stdout / `outputSchema`** | [output-schema.md](output-schema.md) — codegen pipeline, JSDoc, narrowing |
|
|
10
|
+
| **Exposing MCP tools** | [mcp.md](mcp.md) — stdio server, `inputSchema`, varargs, `install --mcp` |
|
|
11
|
+
| **Shipping install / completions / skills** | [install.md](install.md) — `myapp install`, shell completions |
|
|
12
|
+
| **Bundling `myapp docs` topics** | [bundled-docs.md](bundled-docs.md) — consumer docgen vs framework docs |
|
|
13
|
+
| **Agent skills** | [ai-skills.md](ai-skills.md) — `install --skill`, `docs skill` |
|
|
14
|
+
| **Maintaining the argsbarg repo** | [developing.md](developing.md) — release, consumers, npm `files` |
|
|
15
|
+
| **Cursor / IDE agents in a consumer app** | Copy [templates/cursor/rules/cli-program.mdc](templates/cursor/rules/cli-program.mdc) to `.cursor/rules/` |
|
|
16
|
+
|
|
17
|
+
## Framework docs vs consumer docgen
|
|
18
|
+
|
|
19
|
+
| Source | What it is | Where it lives |
|
|
20
|
+
| --- | --- | --- |
|
|
21
|
+
| **Framework docs** | How argsbarg works; authoring conventions | This directory — shipped in `node_modules/argsbarg/docs/` after `bun add argsbarg` |
|
|
22
|
+
| **Consumer docgen** | *Your* command tree, API, MCP guide for *your* app | `myapp docs api`, `docs schema`, `docs mcp` — written to `./docs/` with `--save` |
|
|
23
|
+
| **Cursor rule** | Thin tripwire telling agents to read framework docs | `node_modules/argsbarg/docs/templates/cursor/rules/cli-program.mdc` — copy into your repo and append app conventions |
|
|
24
|
+
|
|
25
|
+
Agents do **not** load `node_modules/argsbarg/docs/` unless your repo references them (Cursor rule, `AGENTS.md`, or an `alwaysApply` project rule). Generated `./docs/api.md` in a consumer repo describes **your** CLI, not argsbarg itself.
|
|
26
|
+
|
|
27
|
+
## Examples
|
|
28
|
+
|
|
29
|
+
| Example | Shows |
|
|
30
|
+
| --- | --- |
|
|
31
|
+
| [../examples/minimal.ts](../examples/minimal.ts) | Presence + string flags, fallback routing |
|
|
32
|
+
| [../examples/nested.ts](../examples/nested.ts) | Nested commands, varargs, MCP + `docs` |
|
|
33
|
+
| [../examples/formats.ts](../examples/formats.ts) | `CliValueFormat`, `default`, `readLeafInputs()` |
|
package/docs/bundled-docs.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
ArgsBarg can expose bundled markdown topics as the built-in `docs` command group. Opt in on the program root with `docs: { enabled: true, topics: { ... } }`.
|
|
4
4
|
|
|
5
|
+
## Framework docs vs your app's docgen
|
|
6
|
+
|
|
7
|
+
Two documentation layers often coexist in a consumer repo:
|
|
8
|
+
|
|
9
|
+
| Layer | Contents | How agents/humans get it |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| **Argsbarg framework** | How to author `CliProgram`, MCP varargs policy, headless patterns | `node_modules/argsbarg/docs/` — wire via [Cursor rule](templates/cursor/rules/cli-program.mdc) or `AGENTS.md` |
|
|
12
|
+
| **Your CLI (docgen)** | Your command tree, options, MCP tool list, install notes | `myapp docs api`, `docs schema`, `docs mcp` — save with `--save` to `./docs/` |
|
|
13
|
+
|
|
14
|
+
`docs api` and `docs schema` embed each leaf’s `outputSchema` when set — see [output-schema.md](output-schema.md) for how to generate and wire schemas.
|
|
15
|
+
|
|
16
|
+
Do not confuse them: editing `./docs/api.md` after docgen updates **your** app reference; it does not change argsbarg's framework guides. When MCP behavior changes (e.g. varargs arrays in 3.6+), update consumer `docs/mcp.md` via **`myapp docs mcp --save`** and bump the `argsbarg` dependency.
|
|
17
|
+
|
|
18
|
+
See [docs/README.md](README.md) for the full documentation map.
|
|
19
|
+
|
|
5
20
|
## Quick start
|
|
6
21
|
|
|
7
22
|
```typescript
|
package/docs/cli-program.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
ArgsBarg turns your schema into help, shell completions, MCP tools, and agent skills. **The same `description` fields you write for humans are the agent contract** for basic apps.
|
|
4
4
|
|
|
5
|
+
**Documentation map:** [docs/README.md](README.md) — which guide to read for MCP, install, consumer docgen, and Cursor setup.
|
|
6
|
+
|
|
5
7
|
## Minimal app (MCP is free)
|
|
6
8
|
|
|
7
9
|
```typescript
|
|
@@ -132,6 +134,8 @@ On **leaf commands**, set `outputSchema` to a JSON Schema describing stdout when
|
|
|
132
134
|
|
|
133
135
|
Exported in `docs schema`, `docs api`, skill `reference.md`, and MCP `tools/list`. Not validated at runtime yet. Pair with `notes` for prose examples; do not duplicate the full schema in `notes`.
|
|
134
136
|
|
|
137
|
+
For a **outputSchema codegen guidelines** (TypeScript types → JSON Schema → `outputSchema` constants), see [output-schema.md](output-schema.md).
|
|
138
|
+
|
|
135
139
|
Do **not** use `mcpTool.description` to paper over missing `--yes`, non-standard flag names, or handlers that only work interactively — fix those instead.
|
|
136
140
|
|
|
137
141
|
If help text and MCP behavior match after your fixes, **omit `mcpTool` entirely**.
|
|
@@ -193,6 +197,26 @@ const { limit, "skip-readiness": skipReadiness, timeout } = ctx.readLeafInputs()
|
|
|
193
197
|
// duration → number (ms); comma-list → string[]; presence → boolean; number → number
|
|
194
198
|
```
|
|
195
199
|
|
|
200
|
+
**`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:
|
|
201
|
+
|
|
202
|
+
| Schema | Value in `CliLeafInputs` |
|
|
203
|
+
| --- | --- |
|
|
204
|
+
| Presence | `boolean` |
|
|
205
|
+
| Number | `number` or `undefined` if omitted |
|
|
206
|
+
| String (plain) | `string` or `undefined` |
|
|
207
|
+
| `format: duration` | `number` (milliseconds) |
|
|
208
|
+
| `format: comma-list` | `string[]` |
|
|
209
|
+
| `format: date` | `string` (`YYYY-MM-DD`) |
|
|
210
|
+
| `format: date-time` | `string` (normalized UTC ISO) |
|
|
211
|
+
| Single positional | `string` or `undefined` |
|
|
212
|
+
| Varargs positional | `string[]` or `undefined` |
|
|
213
|
+
|
|
214
|
+
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.
|
|
215
|
+
|
|
216
|
+
`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`.
|
|
217
|
+
|
|
218
|
+
See [examples/formats.ts](../examples/formats.ts) for a runnable demo.
|
|
219
|
+
|
|
196
220
|
Cross-field rules (e.g. `--match-remote` requires `--branch`) stay in consumer `resolve*` layers — argsbarg does not validate those.
|
|
197
221
|
|
|
198
222
|
## Read flags once, resolve once
|
|
@@ -248,6 +272,30 @@ handler: async (ctx) => {
|
|
|
248
272
|
|
|
249
273
|
**JSON-only CLIs** — a single `readCommandOptions(ctx)` wrapping `readLeafInputs()` per shared option set is usually enough; full `resolve*` layering is optional.
|
|
250
274
|
|
|
275
|
+
## Upgrading to 3.6+
|
|
276
|
+
|
|
277
|
+
### MCP varargs (breaking)
|
|
278
|
+
|
|
279
|
+
Varargs positionals (`argMax: 0`) must be a **JSON array** in `tools/call` — comma-separated strings are no longer accepted.
|
|
280
|
+
|
|
281
|
+
```json
|
|
282
|
+
// before (removed)
|
|
283
|
+
{ "uids": "a,b,c" }
|
|
284
|
+
|
|
285
|
+
// after
|
|
286
|
+
{ "uids": ["a", "b", "c"] }
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
CLI argv is unchanged: space-separated words. Use `format: comma-list` on an **option** when a single flag should accept `a,b` or `["a","b"]` over MCP.
|
|
290
|
+
|
|
291
|
+
### Value formats (optional)
|
|
292
|
+
|
|
293
|
+
Add `format`, `default`, or `pattern` on string **options**; read with `ctx.durationOpt`, `ctx.commaListOpt`, `ctx.readLeafInputs()`, etc. Replace hand-rolled `split(",")` / `parseDurationMs` try/catch where the schema can declare the shape.
|
|
294
|
+
|
|
295
|
+
### Handler layering (optional)
|
|
296
|
+
|
|
297
|
+
Ink + headless + MCP apps benefit from `read*Flags(ctx)` + `resolve*Input(flags)` — see above.
|
|
298
|
+
|
|
251
299
|
## Headless-capable handlers
|
|
252
300
|
|
|
253
301
|
Simple leaves (read args, print stdout) are already headless — no extra work. **Any handler that might mount Ink, prompt, or open a browser should also implement a scriptable fast path** for:
|
|
@@ -342,14 +390,28 @@ mkdir -p .cursor/rules
|
|
|
342
390
|
cp node_modules/argsbarg/docs/templates/cursor/rules/cli-program.mdc .cursor/rules/cli-program.mdc
|
|
343
391
|
```
|
|
344
392
|
|
|
345
|
-
The template is ~25 lines: when to read which doc, plus hard rules agents often get wrong. It does **not** duplicate this guide
|
|
393
|
+
The template is ~25 lines: when to read which doc, plus hard rules agents often get wrong. It does **not** duplicate this guide.
|
|
394
|
+
|
|
395
|
+
2. **Add an app-specific block at the bottom** (recommended). Replace the template placeholder with a heading like `**myapp conventions:**` and short bullets — shared flag modules, `read*Flags` / `resolve*` paths, Ink vs JSON-only, etc. Example:
|
|
396
|
+
|
|
397
|
+
```markdown
|
|
398
|
+
**sqsp-qa conventions:**
|
|
399
|
+
|
|
400
|
+
- Shared mutator flags: `readQaMutatingFlags(ctx)` in `src/cli/shared.ts`.
|
|
401
|
+
- Per command: `read*Flags` + `resolve*Input` in `commands/<name>/resolve.ts`.
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
If you maintain argsbarg from a sibling checkout, `just consumer-dev` / `just consumers-sync` refresh the shared template and **keep** this footer (matched by the `**… conventions:**` heading). Commit `.cursor/rules/cli-program.mdc` in your repo.
|
|
346
405
|
|
|
347
|
-
|
|
406
|
+
3. **Optional:** a separate rule (e.g. `.cursor/argsbarg.mdc` or `AGENTS.md`) for broader package API notes.
|
|
348
407
|
|
|
349
|
-
|
|
408
|
+
**Not this file:** `myapp install --skill` writes the **app** skill (`SKILL.md` under `~/.cursor/skills/`) from your command schema — how to *invoke* the CLI. The rule above is for *authoring* argsbarg schema.
|
|
350
409
|
|
|
351
410
|
## See also
|
|
352
411
|
|
|
412
|
+
- [Documentation map](README.md) — which doc to read when
|
|
413
|
+
- [Output schemas](output-schema.md) — codegen pipeline for leaf `outputSchema`
|
|
414
|
+
- [Developing argsbarg](developing.md) — release, consumer sync, npm `files`
|
|
353
415
|
- [MCP server](mcp.md) — tools, schema resource, env bootstrapping
|
|
354
416
|
- [Agent skills](ai-skills.md) — `install --skill`
|
|
355
|
-
- [Bundled docs](bundled-docs.md) — `docs` topics
|
|
417
|
+
- [Bundled docs](bundled-docs.md) — `docs` topics, consumer docgen vs framework docs
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Developing argsbarg
|
|
2
|
+
|
|
3
|
+
Notes for maintainers of this repository. Also shipped under `node_modules/argsbarg/docs/` for fork maintainers.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- [Bun](https://bun.sh) ≥ 1.3
|
|
8
|
+
- [just](https://github.com/casey/just) — `just` lists recipes
|
|
9
|
+
- `gh` and `npm` logged in for release
|
|
10
|
+
|
|
11
|
+
## Day-to-day
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
just check # typecheck + format
|
|
15
|
+
just test # check + unit tests
|
|
16
|
+
just typegen # regenerate index.d.ts
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Release
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
just release patch # or minor | major
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The release script bumps `package.json`, promotes `[Unreleased]` in `CHANGELOG.md`, commits, tags, pushes, creates a GitHub release, and publishes to npm. Run `just test` first (the `just release` recipe does).
|
|
26
|
+
|
|
27
|
+
Update `CHANGELOG.md` under `[Unreleased]` before releasing.
|
|
28
|
+
|
|
29
|
+
## Local consumer apps
|
|
30
|
+
|
|
31
|
+
Sibling repos under `../../ss/` (paths are machine-specific; adjust in `justfile` if needed):
|
|
32
|
+
|
|
33
|
+
| Recipe | When | Effect |
|
|
34
|
+
| --- | --- | --- |
|
|
35
|
+
| `just consumer-dev` | Before publish; hacking on argsbarg locally | `bun add argsbarg@file:<relative>`; refresh `.cursor/rules/cli-program.mdc` from template (keeps app-specific suffix) |
|
|
36
|
+
| `just consumers-sync` | After release | Sets `"argsbarg": "^<this package.json version>"`, `bun install`, merge **argsbarg Cursor rule**, `just build`, `just docgen`, `just install` (consumer app binary, completions, and **app** skill) |
|
|
37
|
+
|
|
38
|
+
`consumers-sync` reads the version from **this repo’s** `package.json` — not npm. Run it **after** `just release` so consumers pin a version that exists on the registry.
|
|
39
|
+
|
|
40
|
+
**Argsbarg authoring rule** — `scripts/merge-cli-program-rule.ts` copies `docs/templates/cursor/rules/cli-program.mdc` into each consumer’s `.cursor/rules/cli-program.mdc`, preserving any existing `**… conventions:**` footer block.
|
|
41
|
+
|
|
42
|
+
**Recommended in each consumer:** replace the template placeholder with `**<app> conventions:**` bullets (paths to `read*Flags`, shared flags, Ink vs JSON-only). Commit that file; merges refresh the shared top, not your footer.
|
|
43
|
+
|
|
44
|
+
**Consumer app skill** — `just install` in each consumer (part of `consumers-sync`) runs `myapp install --skill`, which updates `~/.cursor/skills/<app>/` from that app’s schema — not the argsbarg framework rule.
|
|
45
|
+
|
|
46
|
+
## npm package contents
|
|
47
|
+
|
|
48
|
+
`npm publish` does **not** honor `.gitignore`. Only paths listed in `package.json` `files` are included in the tarball (plus always-excluded defaults like `node_modules`).
|
|
49
|
+
|
|
50
|
+
When adding docs or examples intended for consumers, ensure they live under whitelisted paths (`docs/`, `examples/`, `src/`, etc.).
|
|
51
|
+
|
|
52
|
+
## Docs
|
|
53
|
+
|
|
54
|
+
See [README.md](README.md) for the documentation map. Framework authoring guide: [cli-program.md](cli-program.md).
|
package/docs/mcp.md
CHANGED
|
@@ -208,7 +208,7 @@ Set **`outputSchema` on the leaf** (not under `mcpTool`) — see [cli-program.md
|
|
|
208
208
|
Each tool’s `inputSchema` is a JSON Schema object built from your CLI definition:
|
|
209
209
|
|
|
210
210
|
- **Options** — parent-scoped flags are included (e.g. `stat`’s `--json` appears on `stat_owner_lookup`). Presence options are `boolean`; string, number, and **enum** options match their `CliOptionKind` (`Enum` uses JSON Schema `enum`). Required options are listed in `required`.
|
|
211
|
-
- **Positionals** — one property per `CliPositional` on the leaf. Single-slot positionals are `string`; varargs tails (`argMax: 0`) are `string[]`. Required positionals are listed in `required`.
|
|
211
|
+
- **Positionals** — one property per `CliPositional` on the leaf. Single-slot positionals are `string`; varargs tails (`argMax: 0`) are `string[]`. Required positionals are listed in `required`. **Varargs must be a JSON array** — comma-separated strings are not accepted (use `format: comma-list` on an option when a single flag should accept `"a,b"` or `["a","b"]`).
|
|
212
212
|
|
|
213
213
|
Arguments are a **flat JSON object** keyed by option and positional names (same names as in your schema, including hyphenated option names like `"user-name"`).
|
|
214
214
|
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Output schemas (`outputSchema`)
|
|
2
|
+
|
|
3
|
+
How to describe JSON stdout on leaf commands — and a **recommended codegen pipeline** used in production argsbarg apps.
|
|
4
|
+
|
|
5
|
+
## Argsbarg contract
|
|
6
|
+
|
|
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
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { STATUS_OUTPUT_SCHEMA } from "../schemas/outputSchemas.js";
|
|
11
|
+
|
|
12
|
+
export const status = {
|
|
13
|
+
key: "status",
|
|
14
|
+
description: "Show environment status.",
|
|
15
|
+
outputSchema: STATUS_OUTPUT_SCHEMA,
|
|
16
|
+
handler: async (ctx) => { /* writes JSON to stdout */ },
|
|
17
|
+
} satisfies CliLeaf;
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
| Where argsbarg uses it | Purpose |
|
|
21
|
+
| --- | --- |
|
|
22
|
+
| `myapp docs schema` | Full command tree JSON export |
|
|
23
|
+
| `myapp docs api` | Markdown per-command **Output** section |
|
|
24
|
+
| `myapp docs skill` | `reference.md` for agent skills |
|
|
25
|
+
| MCP `tools/list` | Optional `outputSchema` on each tool |
|
|
26
|
+
|
|
27
|
+
**Not validated at runtime** — argsbarg does not parse or reject handler stdout against the schema today. The schema is documentation and MCP metadata.
|
|
28
|
+
|
|
29
|
+
**Set on the leaf only** — not under `mcpTool` (legacy `mcpTool.outputSchema` still resolves but is deprecated).
|
|
30
|
+
|
|
31
|
+
**Draft version** — argsbarg accepts any JSON Schema object (`type`, `properties`, `definitions`, etc.). Generators may emit draft-07 or draft 2020-12; docgen embeds the object as-is.
|
|
32
|
+
|
|
33
|
+
See [cli-program.md — Structured stdout](cli-program.md#structured-stdout) for when to use `outputSchema` vs `notes`, and [mcp.md](mcp.md) for how MCP returns parsed JSON as `structuredContent`.
|
|
34
|
+
|
|
35
|
+
## Hand-written vs generated
|
|
36
|
+
|
|
37
|
+
| Approach | When |
|
|
38
|
+
| --- | --- |
|
|
39
|
+
| **Inline object** on the leaf | One-off commands, spikes, very small shapes |
|
|
40
|
+
| **Codegen from TypeScript** | Multiple commands share a shape, nested objects, or you want rich `description` fields in `docs api` / skills |
|
|
41
|
+
|
|
42
|
+
Production CLIs with several JSON commands tend to use **codegen** so types, handlers, and schemas stay aligned.
|
|
43
|
+
|
|
44
|
+
## Recommended pipeline (copy per repo)
|
|
45
|
+
|
|
46
|
+
No shared npm package — each app copies the same **contract**. Reference implementations: **sqsp-qa-tools**, **idp-trees**, **sqsp-i18n-tools** (see each repo’s `docs/architecture.md` for manifest tables).
|
|
47
|
+
|
|
48
|
+
```mermaid
|
|
49
|
+
flowchart LR
|
|
50
|
+
subgraph types [Schema-facing TS + JSDoc]
|
|
51
|
+
Roots["Manifest root interfaces"]
|
|
52
|
+
Narrow["Narrowed types where needed"]
|
|
53
|
+
end
|
|
54
|
+
subgraph gen [just schemagen]
|
|
55
|
+
Script["scripts/generate-output-schemas.ts"]
|
|
56
|
+
Gen["ts-json-schema-generator"]
|
|
57
|
+
end
|
|
58
|
+
subgraph artifacts [Committed]
|
|
59
|
+
Json["src/schemas/generated/*.json"]
|
|
60
|
+
end
|
|
61
|
+
subgraph runtime [Runtime]
|
|
62
|
+
Bridge["src/schemas/outputSchemas.ts"]
|
|
63
|
+
Leaves["leaf outputSchema"]
|
|
64
|
+
Docgen["just docgen"]
|
|
65
|
+
end
|
|
66
|
+
Roots --> Script
|
|
67
|
+
Narrow --> Script
|
|
68
|
+
Script --> Gen --> Json --> Bridge --> Leaves --> Docgen
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
| Piece | Convention |
|
|
72
|
+
| --- | --- |
|
|
73
|
+
| Generator | [`ts-json-schema-generator`](https://github.com/vega/ts-json-schema-generator) (e.g. `createGenerator` with `jsDoc: "extended"`) |
|
|
74
|
+
| Config | `tsconfig: "tsconfig.json"`, `topRef: false`, `skipTypeCheck: false` |
|
|
75
|
+
| Manifest | Explicit `{ typeName, path, outfile }[]` — only **roots** become schema files |
|
|
76
|
+
| Artifacts | Commit `src/schemas/generated/*.json` |
|
|
77
|
+
| Bridge | `src/schemas/outputSchemas.ts` imports JSON → `*_OUTPUT_SCHEMA` constants |
|
|
78
|
+
| tsconfig | `"resolveJsonModule": true` |
|
|
79
|
+
| CI | `just check`: run `schemagen` → `git diff --exit-code src/schemas/generated/` → typecheck |
|
|
80
|
+
| Docgen | `docgen` depends on `schemagen` so saved `./docs/api.md` and `./docs/schema.json` are fresh |
|
|
81
|
+
|
|
82
|
+
Type files can live anywhere the manifest points (`commands/<cmd>/types.ts`, `resolve.ts`, `core/types.ts`, `ui/runHeadless.ts`).
|
|
83
|
+
|
|
84
|
+
### Example manifest + generator
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
const MANIFEST = [
|
|
88
|
+
{ typeName: "StatusJsonOutput", path: "src/commands/status/resolve.ts", outfile: "status.json" },
|
|
89
|
+
{ typeName: "HeadlessOpResult", path: "src/ui/runHeadless.ts", outfile: "headless-op-result.json" },
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
for (const entry of MANIFEST) {
|
|
93
|
+
const generator = createGenerator({
|
|
94
|
+
path: path.join(ROOT, entry.path),
|
|
95
|
+
type: entry.typeName,
|
|
96
|
+
tsconfig: path.join(ROOT, "tsconfig.json"),
|
|
97
|
+
topRef: false,
|
|
98
|
+
skipTypeCheck: false,
|
|
99
|
+
jsDoc: "extended",
|
|
100
|
+
});
|
|
101
|
+
fs.writeFileSync(
|
|
102
|
+
path.join(OUT_DIR, entry.outfile),
|
|
103
|
+
`${JSON.stringify(generator.createSchema(entry.typeName), null, 2)}\n`,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Example bridge
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
import statusJson from "./generated/status.json";
|
|
112
|
+
|
|
113
|
+
export const STATUS_JSON_OUTPUT_SCHEMA = statusJson as Record<string, unknown>;
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Wire the constant on each leaf that emits that shape (several commands may share one schema, e.g. mutating ops sharing `HeadlessOpResult`).
|
|
117
|
+
|
|
118
|
+
## Schema-facing types
|
|
119
|
+
|
|
120
|
+
**Goal:** generated schemas match what handlers actually print, with descriptions agents can read in `docs api`.
|
|
121
|
+
|
|
122
|
+
1. **Manifest roots** — `export interface` (preferred) or documented type alias; top-level JSDoc names which command(s) emit it.
|
|
123
|
+
2. **Per property** — `/** … */` on every field that should appear in JSON Schema `properties` (including nested named types).
|
|
124
|
+
3. **Unions / enums** — document the alias; generator emits `enum` / `anyOf` with type-level description.
|
|
125
|
+
4. **Formats** — property JSDoc can include `@format date-time` for ISO timestamps; add a smoke test that the generated property has `format: "date-time"`.
|
|
126
|
+
5. **Do not hand-edit** `src/schemas/generated/` — change types/JSDoc, run `just schemagen`, commit JSON.
|
|
127
|
+
|
|
128
|
+
### Narrowing when runtime ≠ stdout
|
|
129
|
+
|
|
130
|
+
When a shared runtime type is **wider** than one command’s JSON, add a **schema-facing** type:
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
/** Runtime union across commands. */
|
|
134
|
+
export type ResultSource = TranslationReadinessSource | { kind: "uids"; uids: string[] };
|
|
135
|
+
|
|
136
|
+
/** JSON for `pr` and `file` only — no `uids` variant. */
|
|
137
|
+
export interface TranslationReadinessResult {
|
|
138
|
+
source: TranslationReadinessSource;
|
|
139
|
+
evaluatedAt: string;
|
|
140
|
+
// ...
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Patterns:
|
|
145
|
+
|
|
146
|
+
- **Shallow dashboard types** — separate interfaces from fat API types so generated schema stays readable.
|
|
147
|
+
- **Assignability tests** — `expectTypeOf<RuntimeRow>().toMatchTypeOf<SchemaFacingRow>()` or equivalent so refactors cannot drift.
|
|
148
|
+
|
|
149
|
+
Handlers keep using runtime types; only the manifest root (and its graph) feed codegen.
|
|
150
|
+
|
|
151
|
+
## Tests
|
|
152
|
+
|
|
153
|
+
After wiring, add `src/schemas/outputSchemas.test.ts` (or similar):
|
|
154
|
+
|
|
155
|
+
- Each exported schema has `type: "object"` (or expected root).
|
|
156
|
+
- Key `properties` / `definitions` have non-empty `description` from JSDoc.
|
|
157
|
+
- Spot-check enums, required fields, and `@format date-time` where it matters.
|
|
158
|
+
|
|
159
|
+
## Contributor workflow
|
|
160
|
+
|
|
161
|
+
1. Edit schema-facing interfaces and JSDoc.
|
|
162
|
+
2. `just schemagen` — refresh `src/schemas/generated/`.
|
|
163
|
+
3. Commit generated JSON with the type changes.
|
|
164
|
+
4. `just docgen` / `myapp docs api --save` — refresh consumer docs.
|
|
165
|
+
5. Document app-specific manifest roots in **your** `docs/architecture.md` (not in argsbarg).
|
|
166
|
+
|
|
167
|
+
Add a bullet under your app’s `**… conventions:**` block in `.cursor/rules/cli-program.mdc` pointing at `node_modules/argsbarg/docs/output-schema.md` and your `src/schemas/` layout.
|
|
168
|
+
|
|
169
|
+
## Out of scope
|
|
170
|
+
|
|
171
|
+
- Shared codegen package or monorepo tooling
|
|
172
|
+
- Runtime Zod / `.parse()` on stdout in argsbarg
|
|
173
|
+
- `outputSchema` for plain-text, streaming, or Ink-only commands
|
|
174
|
+
- Forcing identical file layout across repos — the **manifest** is authoritative
|
|
175
|
+
|
|
176
|
+
## See also
|
|
177
|
+
|
|
178
|
+
- [cli-program.md](cli-program.md) — structured stdout, headless JSON, `read*Flags`
|
|
179
|
+
- [mcp.md](mcp.md) — `tools/list`, `structuredContent`
|
|
180
|
+
- [bundled-docs.md](bundled-docs.md) — `docs api` / `docs schema` docgen
|
|
181
|
+
- [docs/README.md](README.md) — documentation map
|
|
@@ -8,8 +8,9 @@ When adding or changing argsbarg schema, leaf handlers, or MCP exposure:
|
|
|
8
8
|
|
|
9
9
|
1. **Read** `node_modules/argsbarg/docs/cli-program.md` (required — authoritative guide).
|
|
10
10
|
2. MCP tools, varargs, `inputSchema` → also `node_modules/argsbarg/docs/mcp.md`.
|
|
11
|
-
3. `
|
|
12
|
-
4.
|
|
11
|
+
3. JSON stdout / `outputSchema` guide and codegen → `node_modules/argsbarg/docs/output-schema.md`.
|
|
12
|
+
4. `install`, completions, skills → `node_modules/argsbarg/docs/install.md`.
|
|
13
|
+
5. Bundled `docs` built-in → `node_modules/argsbarg/docs/bundled-docs.md`.
|
|
13
14
|
|
|
14
15
|
**Hard rules** (details and examples are in the docs above — do not contradict them):
|
|
15
16
|
|
|
@@ -20,5 +21,6 @@ When adding or changing argsbarg schema, leaf handlers, or MCP exposure:
|
|
|
20
21
|
- String options: `format` / `default` / `pattern` per `cli-program.md`; `readLeafInputs()` for multi-flag leaves.
|
|
21
22
|
- Varargs (`argMax: 0`): CLI space-separated; MCP JSON array only — no comma-splitting positionals.
|
|
22
23
|
- Multi-surface leaves (Ink + headless + MCP): one **`read*Flags(ctx)`** per command (or shared family helper + extensions); one **`resolve*Input(flags)`** for cross-field rules — handler reads ctx once, all paths share the struct.
|
|
24
|
+
- JSON stdout: `outputSchema` on the leaf from generated constants — see `output-schema.md`; do not hand-edit `src/schemas/generated/`.
|
|
23
25
|
|
|
24
|
-
**App-specific conventions:**
|
|
26
|
+
**App-specific conventions:** replace this line with a `**<your-app> conventions:**` section (bullets only). Keep it at the bottom of this file — `just consumer-dev` / `just consumers-sync` in the argsbarg repo refresh the template above and preserve this block. Do not duplicate `cli-program.md` here; link paths and patterns only. For a second rule file (e.g. `.cursor/argsbarg.mdc`), that is fine too.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/*
|
|
3
|
+
* Value formats demo: duration, comma-list, date, default, and readLeafInputs().
|
|
4
|
+
* Run: bun ./examples/formats.ts run --tags alpha,beta --on 2026-06-22
|
|
5
|
+
* MCP: pass comma-list as string or array; varargs N/A on this leaf.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import pkg from "../package.json" with { type: "json" };
|
|
9
|
+
import { cliRun, CliFallbackMode, CliOptionKind, CliValueFormat, type CliProgram } from "../src/index.ts";
|
|
10
|
+
|
|
11
|
+
const cli = {
|
|
12
|
+
key: "formats.ts",
|
|
13
|
+
version: pkg.version,
|
|
14
|
+
description: "Value formats and readLeafInputs demo.",
|
|
15
|
+
fallbackCommand: "run",
|
|
16
|
+
fallbackMode: CliFallbackMode.MissingOnly,
|
|
17
|
+
mcpServer: { enabled: true },
|
|
18
|
+
commands: [
|
|
19
|
+
{
|
|
20
|
+
key: "run",
|
|
21
|
+
description: "Print coerced option values from readLeafInputs().",
|
|
22
|
+
options: [
|
|
23
|
+
{
|
|
24
|
+
name: "timeout",
|
|
25
|
+
description: "Wait budget (default 30s).",
|
|
26
|
+
kind: CliOptionKind.String,
|
|
27
|
+
format: CliValueFormat.Duration,
|
|
28
|
+
default: "30s",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: "tags",
|
|
32
|
+
description: "Comma-separated labels.",
|
|
33
|
+
kind: CliOptionKind.String,
|
|
34
|
+
format: CliValueFormat.CommaList,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "on",
|
|
38
|
+
description: "Calendar day (YYYY-MM-DD).",
|
|
39
|
+
kind: CliOptionKind.String,
|
|
40
|
+
format: CliValueFormat.Date,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: "verbose",
|
|
44
|
+
description: "Also print raw ctx.opts strings.",
|
|
45
|
+
kind: CliOptionKind.Presence,
|
|
46
|
+
shortName: "v",
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
handler: (ctx) => {
|
|
50
|
+
const inputs = ctx.readLeafInputs();
|
|
51
|
+
const out = {
|
|
52
|
+
readLeafInputs: inputs,
|
|
53
|
+
durationMs: ctx.durationOpt("timeout"),
|
|
54
|
+
tags: ctx.commaListOpt("tags"),
|
|
55
|
+
on: ctx.dateOpt("on"),
|
|
56
|
+
};
|
|
57
|
+
if (ctx.hasFlag("verbose")) {
|
|
58
|
+
Object.assign(out, { rawOpts: ctx.opts });
|
|
59
|
+
}
|
|
60
|
+
process.stdout.write(`${JSON.stringify(out, null, 2)}\n`);
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
} satisfies CliProgram;
|
|
65
|
+
|
|
66
|
+
await cliRun(cli);
|