argsbarg 6.0.1 → 6.0.2

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.
Files changed (33) hide show
  1. package/CHANGELOG.md +14 -2
  2. package/README.md +3 -3
  3. package/docs/config-schema.md +15 -13
  4. package/docs/output-schema.md +67 -66
  5. package/examples/full-example/README.md +7 -7
  6. package/examples/full-example/bun.lock +0 -29
  7. package/examples/full-example/justfile +6 -3
  8. package/examples/full-example/package.json +0 -2
  9. package/examples/full-example/src/commands/status/__generated__/index.ts +5 -0
  10. package/examples/full-example/src/commands/status/command.ts +2 -2
  11. package/examples/full-example/src/commands/status/types.ts +1 -1
  12. package/examples/full-example/src/config/__generated__/index.ts +5 -0
  13. package/examples/full-example/src/program.ts +4 -4
  14. package/package.json +8 -1
  15. package/src/cli-tool/full-example-capabilities.test.ts +2 -1
  16. package/src/cli-tool/post-create.ts +3 -3
  17. package/src/cli-tool/program.ts +35 -0
  18. package/src/cli-tool/run-schemagen.ts +23 -0
  19. package/src/cli-tool/schemagen/cleanup.ts +60 -0
  20. package/{examples/full-example/scripts → src/cli-tool}/schemagen/discover-schema-roots.ts +13 -58
  21. package/src/cli-tool/schemagen/index.ts +3 -0
  22. package/src/cli-tool/schemagen/names.ts +22 -0
  23. package/src/cli-tool/schemagen/run.ts +108 -0
  24. package/src/cli-tool/schemagen/schemagen.test.ts +125 -0
  25. package/examples/full-example/schemas/configSchemas.ts +0 -6
  26. package/examples/full-example/schemas/outputSchemas.ts +0 -6
  27. package/examples/full-example/scripts/schemagen/discover-schema-roots.test.ts +0 -25
  28. package/examples/full-example/scripts/schemagen/naming.ts +0 -99
  29. package/examples/full-example/scripts/schemagen.ts +0 -91
  30. /package/examples/full-example/{schemas/generated/status.json → src/commands/status/__generated__/outputSchema.json} +0 -0
  31. /package/examples/full-example/src/commands/status/{schema-types.ts → schema.ts} +0 -0
  32. /package/examples/full-example/{schemas/generated/app-config.json → src/config/__generated__/configSchema.json} +0 -0
  33. /package/examples/full-example/src/config/{schema-types.ts → schema.ts} +0 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [6.0.2] - 2026-07-22
11
+
12
+ ### Added
13
+
14
+ - **`argsbarg schemagen`** — centralized JSON Schema generation from `src/**/schema.ts` into colocated `__generated__/` directories (`configSchema.json`, `inputSchema.json`, `outputSchema.json`, plus `index.ts` re-exports). Removes orphan `__generated__/` trees and stale JSON when schema roots or kinds are removed.
15
+ - **`argsbarg/schemagen`** export — `runSchemagen`, `discoverSchemaRoots`, and naming helpers (`src/cli-tool/schemagen/`).
16
+
17
+ ### Changed
18
+
19
+ - **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`).
20
+
10
21
  ## [6.0.1] - 2026-07-22
11
22
 
12
23
  ### Added
@@ -15,7 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
15
26
 
16
27
  ### Changed
17
28
 
18
- - **Schemagen discovery contract** — `examples/full-example` and docs now use `src/**/schema-types.ts` with `export type configType` / `outputType` / `inputType` instead of JSDoc markers (`Config schema`, `JSON payload`, `Tool input`) on `types.ts`.
29
+ - **Colocated schemagen** — `*.schema.json` beside each `schema-types.ts`; exports `configSchema` / `inputSchema` / `outputSchema` (replaces central `*Schemas.ts` bridges).
19
30
  - **HTTP tool errors** — return a plain `{ "error": "..." }` JSON body without ANSI color codes or appended CLI help text.
20
31
  - **`cliErrWithHelp`** — on `api` / `mcp` invocations, throws a plain error instead of printing contextual help.
21
32
  - **`GET /openapi-browser`** — Scalar config preserves schema property order from the OpenAPI document (`orderSchemaPropertiesBy: "preserve"`).
@@ -728,7 +739,8 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
728
739
  - 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
740
  - Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
730
741
 
731
- [Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v6.0.1...HEAD
742
+ [Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v6.0.2...HEAD
743
+ [6.0.2]: https://github.com/bdombro/bun-argsbarg/releases/tag/v6.0.2
732
744
  [6.0.1]: https://github.com/bdombro/bun-argsbarg/releases/tag/v6.0.1
733
745
  [6.0.0]: https://github.com/bdombro/bun-argsbarg/releases/tag/v6.0.0
734
746
  [5.1.16]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.1.16
package/README.md CHANGED
@@ -267,9 +267,9 @@ To refresh the Cursor rule in an existing consumer: `bun scripts/merge-cli-progr
267
267
  | Area | Files / wiring |
268
268
  | --------------------- | -------------------------------------------------------------------------------------- |
269
269
  | All builtins | `completion`, `version`, `configure`, `docs`, `mcp`, `api`, `configure get`/`set` |
270
- | `program.appConfig` | `src/config/schema-types.ts` (`AppConfig`) → `schemas/configSchemas.ts` |
271
- | `outputSchema` | `src/commands/status/schema-types.ts` → `schemas/outputSchemas.ts` |
272
- | Schemagen | `scripts/schemagen.ts` + `scripts/schemagen/discover-schema-roots.ts` |
270
+ | `program.appConfig` | `src/config/schema.ts` → `configSchema` from `__generated__/` |
271
+ | `outputSchema` | `src/commands/status/schema.ts` → `outputSchema` from `__generated__/` |
272
+ | Schemagen | `just schemagen` → `argsbarg schemagen` (justfile exports `node_modules/.bin` on `PATH`) |
273
273
  | Command layout | `src/commands/<name>/command.ts`; registration in `src/program.ts` |
274
274
  | MCP doc topics | `docs.topics` auto-exposed as `<key>://docs/<topic>` resources when docs + MCP enabled |
275
275
  | Package import | `from "argsbarg"` (not relative to argsbarg `src/`) |
@@ -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 (copy per repo)
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 [schema-types.ts]
145
+ subgraph types [schema.ts]
146
146
  Marker["export type configType = AppConfig"]
147
147
  end
148
- subgraph gen [just schemagen]
149
- Script["scripts/schemagen.ts"]
148
+ subgraph gen [argsbarg schemagen]
149
+ Script["argsbarg schemagen"]
150
150
  Gen["ts-json-schema-generator"]
151
151
  end
152
- subgraph artifacts [Committed]
153
- Json["schemas/generated/app-config.json"]
154
- Bridge["schemas/configSchemas.ts"]
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
- Script --> Bridge --> Program --> Validate
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/schema-types.ts` (type defined in same file) |
168
- | Artifacts | Commit `app-config.json` and `configSchemas.ts` bridge exporting `APP_CONFIG_JSON_SCHEMA` |
166
+ | Generator | [`ts-json-schema-generator`](https://github.com/vega/ts-json-schema-generator) (bundled with argsbarg) |
167
+ | Discovery | `export type configType = …` in `src/config/schema.ts` (type defined in same file) |
168
+ | Artifacts | `src/config/__generated__/` — gitignored; run `just schemagen` after clone |
169
169
  | Consumer CI | Optional: `ajv` + `ajv-formats` against the same committed JSON (not an argsbarg runtime dep) |
170
170
 
171
171
  Example:
172
172
 
173
173
  ```typescript
174
- // src/config/schema-types.ts
174
+ // src/config/schema.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** — schemagen discovery, `APP_CONFIG_JSON_SCHEMA` bridge, `program.appConfig`, built-in `configure get`/`set` |
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
@@ -1,18 +1,18 @@
1
1
  # Output schemas (`outputSchema`)
2
2
 
3
- How to describe JSON stdout on leaf commands — and a **recommended codegen pipeline** used in production argsbarg apps.
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 { STATUS_JSON_OUTPUT_SCHEMA } from "../schemas/outputSchemas.js";
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: STATUS_JSON_OUTPUT_SCHEMA,
15
+ outputSchema,
16
16
  handler: async (ctx) => { /* writes JSON to stdout */ },
17
17
  } satisfies CliLeaf;
18
18
  ```
@@ -42,56 +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
- ## Recommended pipeline (copy per repo)
45
+ ## Schemagen pipeline (built into argsbarg)
46
46
 
47
- No shared npm package — each app copies the same **contract**. 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).
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 [schema-types.ts]
53
+ subgraph types [schema.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 [just schemagen]
57
- Discover["discover-schema-roots.ts"]
58
- Script["schemagen.ts / generate-output-schemas.ts"]
58
+ subgraph gen [argsbarg schemagen]
59
+ Discover["discover schema.ts roots"]
59
60
  Gen["ts-json-schema-generator"]
60
61
  end
61
- subgraph artifacts [Committed]
62
- Json["schemas/generated/*.json"]
63
- Bridge["outputSchemas.ts / inputSchemas.ts"]
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["leaf outputSchema / inputSchema"]
67
+ Leaves["import { outputSchema } from ./__generated__/index.ts"]
67
68
  Docgen["just docgen"]
68
69
  end
69
- types --> Discover --> Script --> Gen --> Json
70
- Script --> Bridge --> Leaves --> Docgen
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) (`createGenerator` with `jsDoc: "extended"`) |
76
- | Config | `tsconfig: "tsconfig.json"`, `topRef: false`, `skipTypeCheck: false` |
77
- | Discovery | Walk `src/**/schema-types.ts`; generate from `export type outputType = …` / `inputType` / `configType` when the target type is **defined in that file** |
78
- | Artifacts | Commit `schemas/generated/*.json` **and** auto-generated bridge modules |
76
+ | Generator | [`ts-json-schema-generator`](https://github.com/vega/ts-json-schema-generator) (bundled as an argsbarg dependency) |
77
+ | Discovery | Walk `src/**/schema.ts`; generate from `export type outputType = …` / `inputType` / `configType` when the target type is **defined in that file** |
78
+ | Artifacts | `src/**/__generated__/` — gitignored; run schemagen after clone or when `schema.ts` changes |
79
+ | Invocation | `just schemagen` — justfile exports `node_modules/.bin` on `PATH` for local `argsbarg` |
79
80
  | tsconfig | `"resolveJsonModule": true` |
80
- | CI | `just check`: `schemagen` → `git diff --exit-code schemas/` → typecheck |
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 **`schema-types.ts`** next to the command (or in a shared module when several commands reuse one shape). Export a role alias:
87
+ Put schema-facing interfaces in **`schema.ts`** next to the command (or in a shared module when several commands reuse one shape). Export a role alias:
92
88
 
93
89
  ```typescript
94
- // src/commands/status/schema-types.ts
90
+ // src/commands/status/schema.ts
95
91
  import type { WorkspaceStatus } from "./types.ts";
96
92
 
97
93
  /** JSON stdout for `myapp status --json`. */
@@ -104,7 +100,7 @@ export type outputType = StatusJsonOutput;
104
100
  ```
105
101
 
106
102
  ```typescript
107
- // src/ui/runHeadless/schema-types.ts — shared by many mutating commands
103
+ // src/ui/runHeadless/schema.ts — shared by many mutating commands
108
104
  import type { HeadlessTaskResult } from "./types.ts";
109
105
 
110
106
  export interface HeadlessOpResult {
@@ -117,7 +113,7 @@ export type outputType = HeadlessOpResult;
117
113
  ```
118
114
 
119
115
  ```typescript
120
- // src/commands/render-invoice/schema-types.ts — custom HTTP/MCP body (pdf-gen)
116
+ // src/commands/render-invoice/schema.ts — custom HTTP/MCP body (pdf-gen)
121
117
  export interface RenderInvoiceToolInput {
122
118
  format: "pdf" | "html";
123
119
  invoice: InvoiceData;
@@ -129,58 +125,64 @@ export type outputType = RenderInvoiceWrittenOutput;
129
125
 
130
126
  | Export | Role |
131
127
  | --- | --- |
132
- | `export type configType = AppConfig` | `program.appConfig.jsonSchema` (one per repo, typically `src/config/schema-types.ts`) |
128
+ | `export type configType = AppConfig` | `program.appConfig.jsonSchema` (one per repo, typically `src/config/schema.ts`) |
133
129
  | `export type outputType = …` | `leaf.outputSchema` |
134
130
  | `export type inputType = …` | `leaf.inputSchema` (only when the tool body is not flat CLI flags) |
135
131
 
136
- **Domain helpers** stay in `types.ts` (or `core/types.ts`). Discovery scans only `schema-types.ts`. Re-export-only files (`export type outputType = HeadlessOpResult` pointing at another module) are **not** generation roots — generate once from the canonical definition file.
132
+ **Domain helpers** stay in `types.ts` (or `core/types.ts`). Discovery scans only `schema.ts`. Re-export-only files (`export type outputType = HeadlessOpResult` pointing at another module) are **not** generation roots — generate once from the canonical definition file.
137
133
 
138
- Commands without structured JSON omit `schema-types.ts` and import a shared bridge constant (e.g. `HEADLESS_OP_RESULT_OUTPUT_SCHEMA`).
134
+ Commands without structured JSON omit `schema.ts`. Shared shapes (e.g. `HeadlessOpResult`) live in one canonical `schema.ts`; other commands import `{ outputSchema }` from that module’s `__generated__/index.ts`.
139
135
 
140
136
  When you do **not** set `inputSchema`, argsbarg builds tool input from CLI `options` + `positionals`.
141
137
 
142
- ### Stable naming (outfile + bridge export)
138
+ ### Generated artifacts
143
139
 
144
- Discovery maps each root type name to a generated filename and bridge constant. Suffix conventions (implemented in `outfileForOutputType` / `outputSchemaExportName`):
140
+ Schemagen writes under `__generated__/` beside each `schema.ts`:
145
141
 
146
- | Type suffix | Example type | Generated file | Bridge export |
147
- | --- | --- | --- | --- |
148
- | `JsonOutput` | `StatusJsonOutput` | `status.json` | `STATUS_JSON_OUTPUT_SCHEMA` |
149
- | `OpResult` | `HeadlessOpResult` | `headless-op-result.json` | `HEADLESS_OP_RESULT_OUTPUT_SCHEMA` |
150
- | `Output` | `OpenUrlOutput` | `open-url.json` | `OPEN_URL_OUTPUT_SCHEMA` |
151
- | `Result` | `PrResult` | `pr.json` | `PR_RESULT_OUTPUT_SCHEMA` |
152
- | `ToolInput` | `RenderInvoiceToolInput` | `render-invoice-tool-input.json` | `RENDER_INVOICE_TOOL_INPUT_SCHEMA` |
142
+ | Kind | Generated file | Exported const (from `__generated__/index.ts`) |
143
+ | --- | --- | --- |
144
+ | config | `configSchema.json` | `configSchema` |
145
+ | output | `outputSchema.json` | `outputSchema` |
146
+ | input | `inputSchema.json` | `inputSchema` |
153
147
 
154
- Prefer these suffixes for new roots so filenames and import constants stay predictable across repos.
148
+ Wire on the leaf:
155
149
 
156
- ### Generated bridge
150
+ ```typescript
151
+ import { outputSchema } from "./__generated__/index.ts";
152
+
153
+ export const statusCommand = {
154
+ outputSchema,
155
+ // …
156
+ } satisfies CliLeaf;
157
+ ```
157
158
 
158
- `scripts/schemagen.ts` rewrites `schemas/outputSchemas.ts` on every run:
159
+ Shared mutators:
159
160
 
160
161
  ```typescript
161
- // Auto-generated by scripts/schemagen.ts — do not edit by hand.
162
+ import { outputSchema } from "../../../ui/runHeadless/__generated__/index.ts";
163
+ ```
162
164
 
163
- import status from "./generated/status.json";
165
+ App config:
164
166
 
165
- /** JSON Schema for leaf outputSchema from `StatusJsonOutput`. */
166
- export const STATUS_JSON_OUTPUT_SCHEMA = status as Record<string, unknown>;
167
- ```
167
+ ```typescript
168
+ import { configSchema } from "./config/__generated__/index.ts";
168
169
 
169
- Wire the constant on each leaf that emits that shape (several commands may share one schema, e.g. mutating ops sharing `HeadlessOpResult`).
170
+ appConfig: { jsonSchema: configSchema, entries: { … } },
171
+ ```
170
172
 
171
173
  ## Schema-facing types
172
174
 
173
175
  **Goal:** generated schemas match what handlers actually print, with descriptions agents can read in `docs api`.
174
176
 
175
- 1. **Schema roots** — `export interface` in `schema-types.ts`, with `export type outputType = …` (or `inputType` / `configType`).
177
+ 1. **Schema roots** — `export interface` in `schema.ts`, with `export type outputType = …` (or `inputType` / `configType`).
176
178
  2. **Per property** — `/** … */` on every field that should appear in JSON Schema `properties` (including nested named types).
177
179
  3. **Unions / enums** — document the alias; generator emits `enum` / `anyOf` with type-level description.
178
180
  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** `schemas/generated/` or bridge modules — change types/JSDoc, run `just schemagen`, commit both.
181
+ 5. **Do not hand-edit** `__generated__/` — change types/JSDoc in `schema.ts`, run `just schemagen`.
180
182
 
181
183
  ### Narrowing when runtime ≠ stdout
182
184
 
183
- When a shared runtime type is **wider** than one command’s JSON, add a **schema-facing** root in `schema-types.ts`:
185
+ When a shared runtime type is **wider** than one command’s JSON, add a **schema-facing** root in `schema.ts`:
184
186
 
185
187
  ```typescript
186
188
  /** JSON stdout for `myapp pr` and `myapp file`. */
@@ -201,27 +203,26 @@ Handlers keep using runtime types; only discovered roots (and their type graph)
201
203
 
202
204
  ## Tests
203
205
 
204
- Per repo:
206
+ In argsbarg: `src/cli-tool/schemagen/schemagen.test.ts` locks discovery and generation against `examples/full-example/`.
207
+
208
+ Per consumer repo (optional):
205
209
 
206
- - **`scripts/schemagen/discover-schema-roots.test.ts`** — asserts which roots are discovered and stable outfile / export-name mapping.
207
- - **`schemas/outputSchemas.test.ts`** (optional) — schema shape smoke tests: object root, key `description` fields, enums, `@format date-time`.
210
+ - **`src/generated-schemas.test.ts`** — smoke-test that key `outputSchema` objects have expected shape.
208
211
 
209
212
  ## Contributor workflow
210
213
 
211
- 1. Add or edit schema roots in `src/**/schema-types.ts` with `outputType` / `inputType` / `configType` and per-field JSDoc.
212
- 2. `just schemagen` — refresh `schemas/generated/` and bridge modules.
213
- 3. Import the bridge constant on the relevant leaf `outputSchema` / `inputSchema` fields.
214
- 4. Commit generated JSON and bridges with the type changes.
215
- 5. `just docgen` / `myapp docs api --save` — refresh consumer docs.
216
- 6. Document which commands use which roots in **your** `docs/architecture.md` (argsbarg does not maintain per-app tables).
214
+ 1. Add or edit schema roots in `src/**/schema.ts` with `outputType` / `inputType` / `configType` and per-field JSDoc.
215
+ 2. `just schemagen` — refresh `src/**/__generated__/`.
216
+ 3. Import `{ outputSchema }` / `{ inputSchema }` / `{ configSchema }` from the relevant `__generated__/index.ts`.
217
+ 4. `just docgen` / `myapp docs api --save` — refresh consumer docs.
218
+ 5. Document which commands use which roots in **your** `docs/architecture.md` (argsbarg does not maintain per-app tables).
217
219
 
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` and your `schemas/` layout.
220
+ 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
221
 
220
- **Reference implementation:** [`examples/full-example/`](../examples/full-example/) in this repo (shipped in npm as `node_modules/argsbarg/examples/full-example/`) — discovery script, bridges, and `status` leaf with `outputSchema`.
222
+ **Reference implementation:** [`examples/full-example/`](../examples/full-example/) in this repo — `schema.ts` roots, `__generated__/`, and `status` leaf with `outputSchema`.
221
223
 
222
224
  ## Out of scope
223
225
 
224
- - Shared codegen package or monorepo tooling
225
226
  - Runtime Zod / `.parse()` on stdout in argsbarg
226
227
  - `outputSchema` for plain-text, streaming, or Ink-only commands
227
228
 
@@ -9,7 +9,7 @@ From a git checkout at this directory (requires [Homebrew](https://brew.sh), [ju
9
9
  ```bash
10
10
  brew install just bun
11
11
  just setup
12
- just schemagen # after changing src/**/schema-types.ts
12
+ just schemagen # after changing src/**/schema.ts
13
13
  just run status --json
14
14
  just run docs readme
15
15
  ```
@@ -62,13 +62,13 @@ Undo a local dev install: `just uninstall` (formula + agent artifacts; app confi
62
62
 
63
63
  ## Schemagen roots
64
64
 
65
- | Export in `schema-types.ts` | Artifact |
66
- | --- | --- |
67
- | `export type configType = …` | `schemas/configSchemas.ts` + `schemas/generated/*-config.json` |
68
- | `export type outputType = …` | `schemas/outputSchemas.ts` + `schemas/generated/*.json` |
69
- | `export type inputType = …` | `schemas/inputSchemas.ts` + `schemas/generated/*-tool-input.json` |
65
+ | Export in `schema.ts` | Generated artifact | Import on leaf / program |
66
+ | --- | --- | --- |
67
+ | `export type configType = …` | `__generated__/configSchema.json` | `{ configSchema }` from `config/__generated__/index.ts` → `program.appConfig.jsonSchema` |
68
+ | `export type outputType = …` | `__generated__/outputSchema.json` | `{ outputSchema }` from `__generated__/index.ts` → `leaf.outputSchema` |
69
+ | `export type inputType = …` | `__generated__/inputSchema.json` | `{ inputSchema }` from `__generated__/index.ts` → `leaf.inputSchema` |
70
70
 
71
- Discovery walks `src/**/schema-types.ts` only. Domain helpers stay in sibling `types.ts`.
71
+ Discovery walks `src/**/schema.ts` only. Domain helpers stay in sibling `types.ts`. `__generated__/` is gitignored — run `argsbarg schemagen` (via `just schemagen` or `just setup`).
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, git diff check, typecheck, and format
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
- bun run schemagen
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
  }
@@ -0,0 +1,5 @@
1
+ // Auto-generated by argsbarg schemagen — do not edit by hand.
2
+
3
+ import outputSchemaJson from "./outputSchema.json";
4
+
5
+ export const outputSchema = outputSchemaJson as Record<string, unknown>;
@@ -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 { STATUS_JSON_OUTPUT_SCHEMA } from "../../../schemas/outputSchemas.ts";
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: STATUS_JSON_OUTPUT_SCHEMA,
19
+ outputSchema,
20
20
  handler: (ctx) => {
21
21
  const out: StatusJsonOutput = {
22
22
  defaultRegion: ctx.appConfig.get("defaultRegion") as string | undefined,
@@ -1 +1 @@
1
- export type { StatusJsonOutput } from "./schema-types.ts";
1
+ export type { StatusJsonOutput } from "./schema.ts";
@@ -0,0 +1,5 @@
1
+ // Auto-generated by argsbarg schemagen — do not edit by hand.
2
+
3
+ import configSchemaJson from "./configSchema.json";
4
+
5
+ export const configSchema = configSchemaJson as Record<string, unknown>;
@@ -4,12 +4,12 @@ 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 { APP_CONFIG_JSON_SCHEMA } from "../schemas/configSchemas.ts";
7
+ import { configSchema } from "./config/__generated__/index.ts";
8
8
  import { createIdentity } from "../scripts/create-identity.ts";
9
9
  import { echoCommand } from "./commands/echo/command.ts";
10
10
  import { statusCommand } from "./commands/status/command.ts";
11
11
 
12
- const configSchema = {
12
+ const configEntries = {
13
13
  apiToken: {
14
14
  description: "Create at https://example.com/settings/tokens",
15
15
  env: `${createIdentity.envPrefix}_API_TOKEN`,
@@ -34,8 +34,8 @@ export const program = {
34
34
  version: "1.0.0",
35
35
  description: createIdentity.desc,
36
36
  appConfig: {
37
- jsonSchema: APP_CONFIG_JSON_SCHEMA,
38
- entries: configSchema,
37
+ jsonSchema: configSchema,
38
+ entries: configEntries,
39
39
  } satisfies CliAppConfig,
40
40
  docs: {
41
41
  enabled: true,
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "argsbarg",
3
- "version": "6.0.1",
3
+ "version": "6.0.2",
4
4
  "main": "./src/index.ts",
5
5
  "module": "./src/index.ts",
6
+ "dependencies": {
7
+ "ts-json-schema-generator": "^2.3.0"
8
+ },
6
9
  "devDependencies": {
7
10
  "@biomejs/biome": "^2.5.0",
8
11
  "@types/bun": "^1.3.12",
@@ -13,6 +16,10 @@
13
16
  ".": {
14
17
  "types": "./index.d.ts",
15
18
  "default": "./src/index.ts"
19
+ },
20
+ "./schemagen": {
21
+ "types": "./src/cli-tool/schemagen/index.ts",
22
+ "default": "./src/cli-tool/schemagen/index.ts"
16
23
  }
17
24
  },
18
25
  "bin": {
@@ -63,7 +63,8 @@ describe("full-example template", () => {
63
63
 
64
64
  test("status command defines outputSchema", () => {
65
65
  const statusSource = readFileSync(join(exampleRoot, "src/commands/status/command.ts"), "utf8");
66
- expect(statusSource).toContain("outputSchema:");
66
+ expect(statusSource).toMatch(/outputSchema[,:]/);
67
+ expect(statusSource).toContain('from "./__generated__/index.ts"');
67
68
  });
68
69
 
69
70
  test("resolveCapabilities matches full sink shape", () => {
@@ -41,10 +41,10 @@ export async function runPostCreate(targetDir: string, dryRun: boolean): Promise
41
41
  },
42
42
  },
43
43
  {
44
- label: "bun scripts/schemagen.ts",
44
+ label: "argsbarg schemagen",
45
45
  run: () => {
46
46
  if (dryRun) return;
47
- const proc = Bun.spawnSync(["bun", "scripts/schemagen.ts"], {
47
+ const proc = Bun.spawnSync(["argsbarg", "schemagen"], {
48
48
  cwd: abs,
49
49
  stdout: "inherit",
50
50
  stderr: "inherit",
@@ -103,7 +103,7 @@ export async function runPostCreate(targetDir: string, dryRun: boolean): Promise
103
103
  export function printPostCreatePlan(): void {
104
104
  process.stderr.write("Post-create steps:\n");
105
105
  process.stderr.write(" 1. bun install\n");
106
- process.stderr.write(" 2. bun scripts/schemagen.ts\n");
106
+ process.stderr.write(" 2. just schemagen\n");
107
107
  process.stderr.write(" 3. bun test\n");
108
108
  process.stderr.write(" 4. git init + Initial commit (skipped inside existing git work tree)\n");
109
109
  }