argsbarg 3.6.4 → 4.0.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.
Files changed (98) hide show
  1. package/CHANGELOG.md +28 -2
  2. package/README.md +21 -9
  3. package/docs/README.md +12 -8
  4. package/docs/bundled-docs.md +1 -1
  5. package/docs/cli-program.md +62 -2
  6. package/docs/config-schema.md +192 -0
  7. package/docs/developing.md +13 -0
  8. package/docs/install.md +38 -1
  9. package/docs/mcp.md +43 -19
  10. package/docs/output-schema.md +74 -52
  11. package/docs/templates/cursor/rules/cli-program.mdc +10 -5
  12. package/examples/config-app/main.ts +20 -0
  13. package/examples/config-app/program.ts +81 -0
  14. package/examples/config-app/schema.ts +37 -0
  15. package/examples/config-app/types.ts +19 -0
  16. package/examples/consumer-app/README.md +56 -0
  17. package/examples/consumer-app/bun.lock +75 -0
  18. package/examples/consumer-app/capabilities.test.ts +69 -0
  19. package/examples/consumer-app/package.json +17 -0
  20. package/examples/consumer-app/schemas/configSchemas.ts +6 -0
  21. package/examples/consumer-app/schemas/generated/app-config.json +40 -0
  22. package/examples/consumer-app/schemas/generated/status.json +28 -0
  23. package/examples/consumer-app/schemas/outputSchemas.ts +6 -0
  24. package/examples/consumer-app/scripts/schemagen/discover-schema-roots.test.ts +25 -0
  25. package/examples/consumer-app/scripts/schemagen/discover-schema-roots.ts +93 -0
  26. package/examples/consumer-app/scripts/schemagen/naming.ts +82 -0
  27. package/examples/consumer-app/scripts/schemagen.ts +76 -0
  28. package/examples/consumer-app/src/commands/status/types.ts +11 -0
  29. package/examples/consumer-app/src/main.ts +15 -0
  30. package/examples/consumer-app/src/program.ts +116 -0
  31. package/examples/consumer-app/src/types.ts +23 -0
  32. package/examples/consumer-app/tsconfig.json +14 -0
  33. package/examples/formats.ts +10 -3
  34. package/examples/mcp-test.ts +27 -8
  35. package/examples/minimal.ts +4 -3
  36. package/examples/nested.ts +5 -4
  37. package/examples/option-required.ts +8 -4
  38. package/index.d.ts +152 -75
  39. package/package.json +1 -1
  40. package/src/builtins/builtins.test.ts +13 -0
  41. package/src/builtins/config.test.ts +82 -0
  42. package/src/builtins/config.ts +220 -0
  43. package/src/builtins/dispatch.ts +18 -9
  44. package/src/builtins/export.ts +8 -33
  45. package/src/builtins/index.ts +1 -0
  46. package/src/builtins/install.ts +13 -0
  47. package/src/builtins/mcp.ts +1 -1
  48. package/src/builtins/presentation.ts +2 -17
  49. package/src/builtins/registry.ts +40 -0
  50. package/src/capabilities.ts +46 -0
  51. package/src/cli-errors.ts +15 -0
  52. package/src/cli.ts +389 -0
  53. package/src/config/bootstrap.ts +265 -0
  54. package/src/config/context.test.ts +61 -0
  55. package/src/config/context.ts +98 -0
  56. package/src/config/entry.ts +81 -0
  57. package/src/config/file.test.ts +112 -0
  58. package/src/config/file.ts +120 -0
  59. package/src/config/manifest.ts +62 -0
  60. package/src/config/resolve.test.ts +88 -0
  61. package/src/config/resolve.ts +167 -0
  62. package/src/config/schema.ts +101 -0
  63. package/src/config/validate.test.ts +63 -0
  64. package/src/config/validate.ts +292 -0
  65. package/src/config.integration.test.ts +100 -0
  66. package/src/context.ts +5 -0
  67. package/src/docs/docs.test.ts +16 -16
  68. package/src/docs/mcp-guide.ts +35 -11
  69. package/src/hidden-mcpb.test.ts +40 -2
  70. package/src/index.ts +4 -3
  71. package/src/install/index.ts +46 -3
  72. package/src/install/paths.ts +5 -20
  73. package/src/install/plan.ts +6 -0
  74. package/src/install/status.ts +12 -0
  75. package/src/install/uninstall.ts +11 -0
  76. package/src/install/update.test.ts +5 -5
  77. package/src/invoke.test.ts +207 -0
  78. package/src/mcp/bundle.ts +9 -116
  79. package/src/mcp/claude.test.ts +73 -0
  80. package/src/mcp/claude.ts +168 -0
  81. package/src/mcp/env.ts +3 -37
  82. package/src/mcp/server.ts +18 -10
  83. package/src/mcp/tools.ts +3 -7
  84. package/src/mcp/zip.ts +82 -0
  85. package/src/mcp.integration.test.ts +502 -0
  86. package/src/{index.test.ts → parse.test.ts} +24 -935
  87. package/src/paths/host.ts +40 -0
  88. package/src/schema.ts +1 -1
  89. package/src/skill/generate.ts +18 -5
  90. package/src/skill/hint.ts +18 -0
  91. package/src/skill/install.ts +1 -5
  92. package/src/test-fixtures.ts +192 -0
  93. package/src/types.ts +39 -13
  94. package/src/validate.ts +70 -0
  95. package/src/completion.ts +0 -13
  96. package/src/invoke.ts +0 -217
  97. package/src/mcp.ts +0 -28
  98. package/src/runtime.ts +0 -134
package/CHANGELOG.md CHANGED
@@ -7,11 +7,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [4.0.0] - 2026-06-24
11
+
12
+ ### Added
13
+
14
+ - **`Cli` class** — single runtime entry: eager `cliValidateProgram` + `Object.freeze(program)` in constructor; `run()`, `invoke()`, `serveMcp()`; lazy `cli.appConfig` getter (refreshed on dispatch); `exportCommandSchema()` and `exportAppConfigSchema()`.
15
+ - **`program.appConfig` + `CliAppConfig` / `CliAppConfigEntry`** — config-first model: flat JSON file, block `jsonSchema` (or all-string fallback), metadata overlay per key (`entries`), strict load (reject unknown keys), `ctx.appConfig` (`get`, `require`, `set`, `read`), built-in `config get`/`set`, zero-deps draft-07 subset validation.
16
+ - **`docs/config-schema.md`** — recommended TypeScript → JSON Schema codegen for `program.appConfig.jsonSchema` (parallel to output-schema guide).
17
+ - **`examples/consumer-app/`** — kitchen-sink copy template: all builtins, schemagen discovery, `outputSchema`, `from "argsbarg"`.
18
+ - **`mcp bundle` Claude Code plugin** — writes `dist/<key>-plugin/` (`.claude-plugin/plugin.json`, `.mcp.json`, `bin/<key>`, skills) alongside `dist/<key>.mcpb`.
19
+
20
+ ### Changed
21
+
22
+ - **Breaking:** **`cliRun`, `cliInvoke`, `cliMcpServeStdio` removed** — use `new Cli(program).run()`, `.invoke(argv)`, `.serveMcp()` instead.
23
+ - **Breaking:** **`program.config` → `program.appConfig`**, **`schema` → `entries`**, **`CliConfig` → `CliAppConfig`**, **`ctx.config` → `ctx.appConfig`**.
24
+ - **Breaking:** **`program.env` + `CliEnvVarConfig` removed** — use `program.appConfig.entries`; root `configFile` → `appConfig.path`; nested env bag → flat schema keys; no extra file keys / `raw()`.
25
+ - **Breaking:** Handler config access — prefer `ctx.appConfig.get/require` over `process.env` for app config (env export remains for subprocesses).
26
+ - **`mcp bundle`** — stdout prints both `.mcpb` and plugin directory paths (one line each).
27
+ - **MCP config enforcement** — required keys from `program.appConfig` checked at `tools/call` (MCP) or before leaf dispatch (CLI).
28
+
29
+ ### Removed
30
+
31
+ - **`mcpTool.requiresEnv`** — use `program.appConfig` schema entries with `env` instead.
32
+ - **`mcpServer.envFile`** — use `program.appConfig` + JSON config file instead.
33
+ - **`loadAppConfigEnv`, `ensureProgramEnv`** — replaced internally by `ensureAppConfig`, `exportConfigToEnv`.
34
+
10
35
  ## [3.6.4] - 2026-06-23
11
36
 
12
37
  ### Added
13
38
 
14
- - **`docs/output-schema.md`** — recommended TypeScript → JSON Schema codegen pipeline for leaf `outputSchema` (manifest, bridge, JSDoc, narrowing, CI).
39
+ - **`docs/output-schema.md`** — recommended TypeScript → JSON Schema codegen for leaf `outputSchema`: `JSON payload` JSDoc discovery in `src/**/types.ts`, auto-generated `outputSchemas.ts` bridge, naming suffixes, JSDoc quality bar, narrowing, CI.
15
40
 
16
41
  ### Changed
17
42
 
@@ -424,7 +449,8 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
424
449
  - 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`).
425
450
  - Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
426
451
 
427
- [Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v3.6.4...HEAD
452
+ [Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v4.0.0...HEAD
453
+ [4.0.0]: https://github.com/bdombro/bun-argsbarg/releases/tag/v4.0.0
428
454
  [3.6.4]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.6.4
429
455
  [3.6.3]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.6.3
430
456
  [3.6.2]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.6.2
package/README.md CHANGED
@@ -35,9 +35,9 @@ Shell completions! -->
35
35
  ## Usage
36
36
 
37
37
  ```typescript
38
- import { cliRun, type CliProgram, CliOptionKind } from "argsbarg";
38
+ import { Cli, type CliProgram, CliOptionKind } from "argsbarg";
39
39
 
40
- const cli = {
40
+ const program = {
41
41
  key: "helloapp",
42
42
  version: "1.0.0",
43
43
  description: "Tiny demo.",
@@ -67,10 +67,11 @@ const cli = {
67
67
  },
68
68
  } satisfies CliProgram;
69
69
 
70
- await cliRun(cli);
70
+ const cli = new Cli(program);
71
+ await cli.run();
71
72
  ```
72
73
 
73
- `cliRun` parses `process.argv`, prints help or errors, dispatches the leaf handler, and **exits the process**.
74
+ `Cli.run()` parses `process.argv`, prints help or errors, dispatches the leaf handler, and **exits the process**.
74
75
 
75
76
 
76
77
 
@@ -95,7 +96,7 @@ Everything you need for a first-class CLI:
95
96
  Every app gets:
96
97
 
97
98
  - `-h` / `--help` at any routing depth (scoped help).
98
- - **`completion bash` / `completion zsh` / `completion fish`** — print shell completion scripts to stdout (injected by `cliRun`).
99
+ - **`completion bash` / `completion zsh` / `completion fish`** — print shell completion scripts to stdout (injected by `Cli.run()`).
99
100
  - **`version`** — print `CliProgram.version` (`myapp version`).
100
101
  - **`mcp`** — when `mcpServer.enabled` is `true`, run as an MCP stdio server (`myapp mcp`).
101
102
  - **`docs`** — when `docs.enabled` is `true`, print bundled markdown topics, schema JSON, API markdown, and generated skill content (`myapp docs`, `myapp docs readme`, `myapp docs schema`, `myapp docs api`, `myapp docs skill`, …). See [docs/bundled-docs.md](docs/bundled-docs.md).
@@ -108,7 +109,7 @@ When **`docs.enabled`** is `true`, do not declare a top-level command named **`d
108
109
 
109
110
  ### MCP (AI agents)
110
111
 
111
- Opt in on the program root with `mcpServer: { enabled: true }`, then run `myapp mcp` for a stdio MCP server. Each leaf command becomes a tool; the CLI tree is available as resource `<sanitized-key>://schema` (same as `myapp docs schema`). Handlers can read `ctx.invocation` and use `cliInvoke` for headless testing.
112
+ Opt in on the program root with `mcpServer: { enabled: true }`, then run `myapp mcp` for a stdio MCP server. Each leaf command becomes a tool; the CLI tree is available as resource `<sanitized-key>://schema` (same as `myapp docs schema`). Handlers can read `ctx.invocation`; use `cli.invoke(argv)` for headless testing.
112
113
 
113
114
  See **[docs/mcp.md](docs/mcp.md)** for configuration, env bootstrapping, custom resources, Cursor setup, and protocol details. See **[docs/cli-program.md](docs/cli-program.md)** for schema authoring (consumer apps: copy **`docs/templates/cursor/rules/cli-program.mdc`** to **`.cursor/rules/cli-program.mdc`**).
114
115
 
@@ -161,7 +162,7 @@ Add app-specific conventions in a second rule if needed. Copy the rule from the
161
162
  ## How it works
162
163
 
163
164
  1. Build a **program root** with `satisfies CliProgram` (or `: CliProgram`): `key` is the app/binary name, `commands` are top-level subcommands, `options` are global flags. A router root must not set `handler` or declare `positionals` (validated at startup). A leaf root may set `handler` and `positionals` directly. Use `fallbackCommand` / `fallbackMode` on any **routing node** for default subcommand routing (not root-only).
164
- 2. Call `await cliRun(root)` with that root — validates, parses argv, renders help or errors, invokes the leaf handler, and `process.exit`s with status **0** on success, **1** on implicit help or error (explicit `--help` → **0**).
165
+ 2. Call `await new Cli(program).run()` — validates, parses argv, renders help or errors, invokes the leaf handler, and `process.exit`s with status **0** on success, **1** on implicit help or error (explicit `--help` → **0**).
165
166
  3. From a handler, `cliErrWithHelp(ctx, "message")` prints a red error line plus contextual help on stderr and exits **1**.
166
167
 
167
168
  ### Fallback modes (`CliFallbackMode`)
@@ -215,6 +216,10 @@ Check the `examples/` directory for full working scripts:
215
216
  | `ArgsBargMinimal` | `examples/minimal.ts` | String + presence flags, `MissingOrUnknown` fallback. |
216
217
  | `ArgsBargNested` | `examples/nested.ts` | Nested command tree, positional tails, async handlers. |
217
218
  | `ArgsBargFormats` | `examples/formats.ts` | `CliValueFormat`, `default`, `readLeafInputs()`. |
219
+ | `ArgsBargConfigApp` | `examples/config-app/` | `program.appConfig`, `ctx.appConfig`, built-in `config get`/`set`, inline JSON Schema. |
220
+ | `ArgsBargConsumerApp` | `examples/consumer-app/` | **Copy template:** all builtins, schemagen discovery, `outputSchema`, `from "argsbarg"`. |
221
+
222
+ Examples ship in the npm package under `node_modules/argsbarg/examples/`. Agents should read **`config-app`** for concepts and **`consumer-app`** when scaffolding a full CLI.
218
223
 
219
224
  ```bash
220
225
  export PATH="$PATH:$(pwd)/examples"
@@ -228,6 +233,12 @@ nested.ts stat owner lookup -u alice ./README.md
228
233
  nested.ts read ./README.md
229
234
 
230
235
  bun ./examples/formats.ts run --tags demo,docs --on 2026-06-22
236
+
237
+ CONFIG_APP_API_TOKEN=dev bun ./examples/config-app/main.ts show --json
238
+ CONFIG_APP_API_TOKEN=dev bun ./examples/config-app/main.ts config get
239
+
240
+ cd examples/consumer-app && bun install && bun run schemagen
241
+ CONSUMER_APP_API_TOKEN=dev bun run start status --json
231
242
  ```
232
243
 
233
244
 
@@ -243,8 +254,9 @@ The package root (`argsbarg` / `src/index.ts`) exports the types and runtime you
243
254
  | `CliSchemaValidationError` | Thrown when the static command tree violates schema rules. |
244
255
  | `CliContext` | Handler context (`ctx.hasFlag`, `ctx.stringOpt`, `ctx.durationOpt`, `ctx.readLeafInputs`, `ctx.invocation`, …). |
245
256
  | `CliLeafInputs` | Record type returned by `readLeafInputs()` — coerced option/positional values keyed by schema name. |
246
- | `cliRun(root, [argv])` | Validate, parse argv, dispatch, exit. |
247
- | `cliInvoke(root, argv)` | Parse and dispatch without exiting; returns captured stdout/stderr. |
257
+ | `Cli` | Runtime: validate + freeze program, `run()`, `invoke()`, `serveMcp()`, `appConfig` getter, `exportCommandSchema()`, `exportAppConfigSchema()`. |
258
+ | `CliInvokeResult`, `CliInvokeKind` | Result types from `cli.invoke()`. |
259
+ | `CliAppConfig`, `CliAppConfigEntry` | App config block on the program root (`entries` metadata overlay + optional `jsonSchema`). |
248
260
  | `cliErrWithHelp(ctx, msg)` | Print error + scoped help on stderr, exit 1. |
249
261
  | `parseDurationMs`, `parseCommaList`, `parseDate`, `parseDateTime` | Optional format parsers for use outside handlers. |
250
262
 
package/docs/README.md CHANGED
@@ -7,12 +7,24 @@ Start here to pick the right guide.
7
7
  | **New to argsbarg** | [../README.md](../README.md) — install, minimal usage, public API |
8
8
  | **Authoring a `CliProgram`** (humans or agents) | [cli-program.md](cli-program.md) — schema, formats, headless, `read*Flags` |
9
9
  | **JSON stdout / `outputSchema`** | [output-schema.md](output-schema.md) — codegen pipeline, JSDoc, narrowing |
10
+ | **App config / `program.appConfig`** | [config-schema.md](config-schema.md) — flat JSON file, `ctx.appConfig`, codegen |
10
11
  | **Exposing MCP tools** | [mcp.md](mcp.md) — stdio server, `inputSchema`, varargs, `install --mcp` |
11
12
  | **Shipping install / completions / skills** | [install.md](install.md) — `myapp install`, shell completions |
12
13
  | **Bundling `myapp docs` topics** | [bundled-docs.md](bundled-docs.md) — consumer docgen vs framework docs |
13
14
  | **Agent skills** | [ai-skills.md](ai-skills.md) — `install --skill`, `docs skill` |
14
15
  | **Maintaining the argsbarg repo** | [developing.md](developing.md) — release, consumers, npm `files` |
15
16
  | **Cursor / IDE agents in a consumer app** | Copy [templates/cursor/rules/cli-program.mdc](templates/cursor/rules/cli-program.mdc) to `.cursor/rules/` |
17
+ | **Runnable examples** (shipped in npm) | [examples/](examples/) — see table below |
18
+
19
+ ## Examples (agents: read these)
20
+
21
+ Examples are included in the npm tarball (`package.json` `files`). After `bun add argsbarg`, open `node_modules/argsbarg/examples/`.
22
+
23
+ | Tier | Path | Use when |
24
+ | --- | --- | --- |
25
+ | Learn | [examples/minimal.ts](../examples/minimal.ts), [config-app/](../examples/config-app/) | One feature at a time |
26
+ | Reference | [examples/nested.ts](../examples/nested.ts), [formats.ts](../examples/formats.ts) | Routing, formats, MCP snippet |
27
+ | **Copy** | [examples/consumer-app/](../examples/consumer-app/) | Bootstrapping a production CLI (all builtins + schemagen) |
16
28
 
17
29
  ## Framework docs vs consumer docgen
18
30
 
@@ -23,11 +35,3 @@ Start here to pick the right guide.
23
35
  | **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
36
 
25
37
  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()` |
@@ -87,7 +87,7 @@ When `docs.enabled` is `true`:
87
87
 
88
88
  ## MCP guide (`docs mcp`)
89
89
 
90
- When both `docs.enabled` and `mcpServer.enabled` are `true`, ArgsBarg injects a **`docs mcp`** topic with an auto-generated guide: tool list, `requiresEnv`, schema resource URI, `install --mcp`, and protocol notes.
90
+ When both `docs.enabled` and `mcpServer.enabled` are `true`, ArgsBarg injects a **`docs mcp`** topic with an auto-generated guide: tool list, `program.appConfig`, schema resource URI, `install --mcp`, and protocol notes.
91
91
 
92
92
  There is no override API in v1 — customize behavior via `mcpTool.description` on leaf commands.
93
93
 
@@ -112,7 +112,6 @@ Many "MCP problems" are schema or handler gaps. Prefer these over escape hatches
112
112
  | Field | Use when |
113
113
  | --- | --- |
114
114
  | `enabled: false` | Command is **genuinely** CLI-only (open browser, Ink-only flow with no scriptable equivalent) |
115
- | `requiresEnv: [...]` | Runtime secrets; appended to MCP description and enforced at `tools/call` |
116
115
  | `description: "..."` | **Irreducible** MCP limitation (e.g. live tail / `--watch` cannot be streamed on the MCP wire yet) |
117
116
 
118
117
  ### Structured stdout
@@ -373,9 +372,70 @@ handler: async (ctx) => {
373
372
 
374
373
  Basic synchronous handlers do not need this structure — only commands with an interactive branch.
375
374
 
375
+ ## Configuration (`program.appConfig`)
376
+
377
+ Declare app configuration on the **program root** (not on leaves). Values persist in a flat JSON file; handlers read resolved values via `ctx.appConfig`.
378
+
379
+ ```typescript
380
+ import { Cli, type CliProgram } from "argsbarg";
381
+ import { APP_CONFIG_JSON_SCHEMA } from "./schemas/configSchemas.js";
382
+
383
+ const program = {
384
+ key: "myapp",
385
+ version: "1.0.0",
386
+ description: "…",
387
+ appConfig: {
388
+ path: "~/.config/myapp/config", // optional override
389
+ jsonSchema: APP_CONFIG_JSON_SCHEMA, // optional; omit for all-string mode
390
+ entries: {
391
+ apiToken: {
392
+ description: "Create at https://example.com/settings/tokens",
393
+ env: "API_TOKEN",
394
+ sensitive: true,
395
+ },
396
+ defaultRegion: {
397
+ title: "Default region",
398
+ description: "AWS region (default us-east-1).",
399
+ required: false,
400
+ },
401
+ maxRetries: { description: "Retry count." },
402
+ },
403
+ },
404
+ handler: (ctx) => {
405
+ const token = ctx.appConfig.require("apiToken");
406
+ const region = ctx.appConfig.get("defaultRegion");
407
+ },
408
+ } satisfies CliProgram;
409
+
410
+ const cli = new Cli(program);
411
+ await cli.run();
412
+ ```
413
+
414
+ | Field | Default | Purpose |
415
+ | --- | --- | --- |
416
+ | `description` | *(required)* | Shown in prompts, `config get`, and bundle manifests |
417
+ | `title` | config key | Short label in `install --configure` |
418
+ | `default` | — | Used when `jsonSchema` omitted (all-string mode) |
419
+ | `required` | `true` | When `false`, optional unless required by `jsonSchema` |
420
+ | `sensitive` | name heuristic (`token`, `secret`, …) | Redact in prompts, `config get`, and status |
421
+ | `env` | — | When set: non-empty host env overrides file; exported to `process.env` after resolve |
422
+
423
+ **Config file** (created on demand):
424
+
425
+ - Default: `$XDG_CONFIG_HOME/<sanitized-key>/config` or `%APPDATA%/<key>/config`.
426
+ - JSON: flat object keyed by schema names — `{ "apiToken": "…", "maxRetries": 5 }`.
427
+ - **Strict:** unknown keys rejected on load.
428
+ - **CLI:** missing required config exits 1 before the leaf handler (TTY prompt when interactive). Built-in `config get`/`set` skip this exit.
429
+ - **MCP:** server stays up; missing config returns `isError: true` at `tools/call`.
430
+ - **Configure:** `myapp install --configure` (not part of `--all`).
431
+
432
+ See [config-schema.md](config-schema.md) for codegen, [install.md](install.md), and [mcp.md](mcp.md).
433
+
434
+ **Handler access (`ctx.appConfig`):** `get`, `require`, `set`, `read` — prefer over `process.env` in handlers; env export remains for subprocess inheritance.
435
+
376
436
  ## Reserved names
377
437
 
378
- Do not declare user commands named `completion`, `install`, `mcp`, `version`, or `docs` at the root — ArgsBarg injects these when configured.
438
+ Do not declare user commands named `completion`, `install`, `mcp`, `version`, `docs`, or `config` at the root — ArgsBarg injects these when configured.
379
439
 
380
440
  ## Cursor rule for consumer repos
381
441
 
@@ -0,0 +1,192 @@
1
+ # Config schema (`program.appConfig`)
2
+
3
+ How to declare app configuration — flat JSON file, env overrides, handler access via `ctx.appConfig`, and a **recommended codegen pipeline** for typed config.
4
+
5
+ ## Argsbarg contract
6
+
7
+ On the **program root**, set `appConfig` with metadata `entries` and optional block `jsonSchema`:
8
+
9
+ ```typescript
10
+ import { Cli, type CliProgram } from "argsbarg";
11
+ import { APP_CONFIG_JSON_SCHEMA } from "./schemas/configSchemas.js";
12
+
13
+ const program = {
14
+ key: "myapp",
15
+ version: "1.0.0",
16
+ description: "…",
17
+ appConfig: {
18
+ jsonSchema: APP_CONFIG_JSON_SCHEMA,
19
+ entries: {
20
+ apiToken: {
21
+ description: "Create at https://example.com/settings/tokens",
22
+ env: "API_TOKEN",
23
+ sensitive: true,
24
+ },
25
+ defaultRegion: { description: "AWS region.", required: false },
26
+ maxRetries: { description: "Retry count." },
27
+ },
28
+ },
29
+ handler: (ctx) => {
30
+ const token = ctx.appConfig.require("apiToken");
31
+ const region = ctx.appConfig.get("defaultRegion"); // default already applied
32
+ },
33
+ } satisfies CliProgram;
34
+
35
+ const cli = new Cli(program);
36
+ await cli.run();
37
+ ```
38
+
39
+ | Where argsbarg uses it | Purpose |
40
+ | --- | --- |
41
+ | Config file | Flat JSON keyed by schema names; strict load (unknown keys rejected) |
42
+ | `install --configure` / `--status` | Interactive setup and status |
43
+ | Built-in `config get` / `config set` | Read/write resolved values (opt-out via `commands: false`) |
44
+ | MCP bundle / Claude plugin | `userConfig` for entries with `env` set |
45
+ | `ctx.appConfig` in handlers | `get`, `require`, `set`, `read` — prefer over `process.env` |
46
+
47
+ **Validation at runtime** — argsbarg validates the config file and `config set` / `ctx.appConfig.set` against the effective JSON Schema (block `jsonSchema` or synthesized all-string schema).
48
+
49
+ **No public config I/O exports** — consumers use `program.appConfig` for authoring and `ctx.appConfig` in handlers.
50
+
51
+ See [cli-program.md — Configuration](cli-program.md#configuration-programappconfig) for resolution order, bootstrap timing, and reserved `config` command.
52
+
53
+ ## `CliAppConfig` and `CliAppConfigEntry`
54
+
55
+ ```typescript
56
+ export interface CliAppConfigEntry {
57
+ description: string;
58
+ title?: string; // default: config key
59
+ default?: string; // all-string mode only
60
+ required?: boolean; // default: true (can override jsonSchema required)
61
+ sensitive?: boolean; // default: name heuristic
62
+ env?: string; // env override + export to process.env after resolve
63
+ }
64
+
65
+ export interface CliAppConfig {
66
+ path?: string; // default: ~/.config/<key>/config (OS rules)
67
+ commands?: boolean | { enabled?: boolean; mcpSet?: boolean };
68
+ jsonSchema?: Record<string, unknown>; // draft-07 block schema
69
+ entries: Record<string, CliAppConfigEntry>;
70
+ }
71
+ ```
72
+
73
+ **Conventions:**
74
+
75
+ - Secrets: `env: "API_TOKEN"` on entry; file key `apiToken`
76
+ - Prefs without env: local-file only; excluded from MCP/plugin manifests
77
+ - MCP manifests: only entries with `env` set (sanitized to snake_case keys)
78
+
79
+ ## Config file shape
80
+
81
+ Flat JSON at `config.path` (default OS path unchanged):
82
+
83
+ ```json
84
+ {
85
+ "apiToken": "xxx",
86
+ "defaultRegion": "eu-west-1",
87
+ "maxRetries": 5,
88
+ "prefs": { "ttl": 3600 }
89
+ }
90
+ ```
91
+
92
+ No nested `env` bag. No extra keys — rejected on load.
93
+
94
+ ## Resolution order (per schema key)
95
+
96
+ | Key has `env`? | Resolved value |
97
+ | --- | --- |
98
+ | **Yes** | non-empty `process.env[env]` → else file[key] → else default |
99
+ | **No** | file[key] → else default |
100
+
101
+ Empty string in env or file counts as **missing** for required entries. After resolution, mapped values are exported to `process.env`.
102
+
103
+ ## Hand-written vs generated
104
+
105
+ | Approach | When |
106
+ | --- | --- |
107
+ | **Omit `jsonSchema`** | Simple apps; all values stored as strings; use `entry.default` |
108
+ | **Codegen from TypeScript** | Typed config, nested objects, shared with JSON Schema CI |
109
+
110
+ ## Recommended pipeline (copy per repo)
111
+
112
+ Mirror the [output-schema.md](output-schema.md) pattern for config:
113
+
114
+ ```mermaid
115
+ flowchart LR
116
+ subgraph types [Schema-facing TS + JSDoc]
117
+ TypesTs["src/**/types.ts"]
118
+ Marker["JSDoc contains Config schema"]
119
+ end
120
+ subgraph gen [just schemagen]
121
+ Script["scripts/generate-config-schemas.ts"]
122
+ Gen["ts-json-schema-generator"]
123
+ end
124
+ subgraph artifacts [Committed]
125
+ Json["src/schemas/generated/app-config.json"]
126
+ Bridge["src/schemas/configSchemas.ts"]
127
+ end
128
+ subgraph runtime [Runtime]
129
+ Program["program.appConfig.jsonSchema"]
130
+ Validate["argsbarg runtime subset validator"]
131
+ end
132
+ TypesTs --> Marker --> Script --> Gen --> Json
133
+ Script --> Bridge --> Program --> Validate
134
+ ```
135
+
136
+ | Piece | Convention |
137
+ | --- | --- |
138
+ | Generator | [`ts-json-schema-generator`](https://github.com/vega/ts-json-schema-generator) |
139
+ | Discovery | JSDoc marker **`Config schema`** on the root config interface |
140
+ | Artifacts | Commit `app-config.json` and `configSchemas.ts` bridge exporting `APP_CONFIG_JSON_SCHEMA` |
141
+ | Consumer CI | Optional: `ajv` + `ajv-formats` against the same committed JSON (not an argsbarg runtime dep) |
142
+
143
+ ### Supported AppConfig shapes (argsbarg runtime validator)
144
+
145
+ | Supported (v1) | Deferred |
146
+ | --- | --- |
147
+ | `type`, `properties`, `required`, `additionalProperties` | remote `$ref` |
148
+ | `enum`, `const`, `default` | complex `if`/`then`/`else` |
149
+ | local `#/definitions` + `$ref` | full draft-2020-12 |
150
+ | `anyOf` / `oneOf` (basic) | |
151
+ | `items`, `minItems`, `maxItems` | |
152
+ | `minimum`, `maximum`, `minLength`, `maxLength`, `pattern` | |
153
+
154
+ ## Minimal example (no schemagen)
155
+
156
+ ```typescript
157
+ appConfig: {
158
+ entries: {
159
+ apiToken: { description: "Token.", env: "API_TOKEN", sensitive: true },
160
+ greeting: { description: "Greeting.", default: "hello", required: false },
161
+ },
162
+ },
163
+ ```
164
+
165
+ All file values are strings. Defaults come from `entry.default`.
166
+
167
+ ## Built-in `config` command
168
+
169
+ When `program.appConfig` is set and `commands !== false`:
170
+
171
+ | Subcommand | Purpose |
172
+ | --- | --- |
173
+ | `config get [key]` | Resolved value(s); `--json`; `--json --pretty` |
174
+ | `config set <key> <value>` | One key; full document re-validated after merge |
175
+
176
+ `config get`/`set` skip required-config exit and TTY prompts. Sensitive values redact on `get` (`REDACTED` / `{ "set": true }` with `--json`).
177
+
178
+ Object/array/`$ref` properties require `--json` on `config set`.
179
+
180
+ ## Example in this repo
181
+
182
+ | Example | Role |
183
+ | --- | --- |
184
+ | [`examples/config-app/`](../examples/config-app/) | **Learn** — hand-written schema, minimal setup |
185
+ | [`examples/consumer-app/`](../examples/consumer-app/) | **Copy** — schemagen discovery, `APP_CONFIG_JSON_SCHEMA` bridge |
186
+
187
+ ```bash
188
+ cd examples/consumer-app && bun install && bun run schemagen
189
+ CONSUMER_APP_API_TOKEN=dev bun run start config get apiToken --json
190
+ ```
191
+
192
+ Set `CONSUMER_APP_CONFIG_FILE` to override the config file path.
@@ -49,6 +49,19 @@ Sibling repos under `../../ss/` (paths are machine-specific; adjust in `justfile
49
49
 
50
50
  When adding docs or examples intended for consumers, ensure they live under whitelisted paths (`docs/`, `examples/`, `src/`, etc.).
51
51
 
52
+ Exclude `examples/consumer-app/node_modules/` from the npm tarball via [`.npmignore`](../.npmignore).
53
+
54
+ ## Kitchen-sink example
55
+
56
+ [`examples/consumer-app/`](../examples/consumer-app/) must enable every builtin (`capabilities.test.ts`). After builtin or schemagen doc changes:
57
+
58
+ ```bash
59
+ just consumer-app-schemagen
60
+ just test
61
+ ```
62
+
63
+ See [`.cursor/rules/examples.mdc`](../.cursor/rules/examples.mdc) for maintainer guidance.
64
+
52
65
  ## Docs
53
66
 
54
67
  See [README.md](README.md) for the documentation map. Framework authoring guide: [cli-program.md](cli-program.md).
package/docs/install.md CHANGED
@@ -32,6 +32,7 @@ myapp install --uninstall --all --yes
32
32
  | Cursor skill | `--skill` | `~/.cursor/skills/<dir>/` when `~/.cursor` exists |
33
33
  | Claude skill | `--skill` | `~/.claude/skills/<dir>/` when `~/.claude` exists |
34
34
  | MCP config | `--mcp` | Cursor, Claude Code/Desktop, OpenCode (`~/.config/opencode`), Codex (`codex` on PATH), ChatGPT desktop (when app data exists). ChatGPT web uses Connectors — see `docs mcp` |
35
+ | App config | `--config` | JSON config file for `program.appConfig` (with `--uninstall`; included in `--uninstall --all`) |
35
36
 
36
37
  `--all` expands to `--bin`, `--completions`, `--skill`, and `--mcp` (when `mcpServer.enabled` is `true`) for both install and uninstall. Missing targets are skipped silently (no error if nothing is on disk or a shell/agent directory does not exist).
37
38
 
@@ -95,6 +96,42 @@ Environment:
95
96
 
96
97
  - `INSTALL_PREFIX` — same as `install.prefix` / `--prefix`
97
98
 
99
+ ## App config (`program.appConfig`)
100
+
101
+ When `program.appConfig` is set on the program root, ArgsBarg manages a flat JSON config file:
102
+
103
+ ```typescript
104
+ appConfig: {
105
+ entries: {
106
+ apiToken: {
107
+ description: "Create at https://example.com/settings/tokens",
108
+ env: "API_TOKEN",
109
+ sensitive: true,
110
+ },
111
+ },
112
+ path: "~/.config/myapp/config", // optional; default is OS-specific
113
+ },
114
+ ```
115
+
116
+ Default path: `$XDG_CONFIG_HOME/<sanitized-key>/config` (Linux/macOS) or `%APPDATA%/<key>/config` (Windows).
117
+
118
+ | Flag | Description |
119
+ | --- | --- |
120
+ | `--configure` | Interactive prompt for each schema entry; writes or updates the config file (standalone — not part of `--all`) |
121
+ | `--uninstall --config` | Remove the config file (included in `--uninstall --all`) |
122
+ | `--status` | Shows config path and which required keys are set or missing |
123
+
124
+ **Configure UX** (TTY):
125
+
126
+ ```
127
+ API token
128
+ Create at https://example.com/settings/tokens
129
+ Current: REDACTED
130
+ Value (Enter to keep):
131
+ ```
132
+
133
+ Non-sensitive vars show the current value; first-time setup omits the `Current:` line.
134
+
98
135
  ## Flags
99
136
 
100
137
  | Flag | Description |
@@ -108,7 +145,7 @@ Environment:
108
145
  | `--update` | Download latest release and reinstall installed artifacts (requires `install.updateGetLatest`; implies `--yes`) |
109
146
  | `--from <path>` | Binary to copy with `--reinstall` (default: running executable) |
110
147
  | `--status` | Read-only inventory |
111
- | `--uninstall` | Remove artifacts in scope (`--all`, `--bin`, `--completions`, `--skill`, `--mcp`); skips targets not installed |
148
+ | `--uninstall` | Remove artifacts in scope (`--all`, `--bin`, `--completions`, `--skill`, `--mcp`, `--config`); skips targets not installed |
112
149
 
113
150
  ## MCP merge behavior
114
151