argsbarg 3.6.3 → 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 +39 -1
  2. package/README.md +22 -10
  3. package/docs/README.md +13 -8
  4. package/docs/bundled-docs.md +3 -1
  5. package/docs/cli-program.md +79 -5
  6. package/docs/config-schema.md +192 -0
  7. package/docs/developing.md +20 -3
  8. package/docs/install.md +38 -1
  9. package/docs/mcp.md +43 -19
  10. package/docs/output-schema.md +203 -0
  11. package/docs/templates/cursor/rules/cli-program.mdc +12 -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,6 +7,41 @@ 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
+
35
+ ## [3.6.4] - 2026-06-23
36
+
37
+ ### Added
38
+
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.
40
+
41
+ ### Changed
42
+
43
+ - **`docs/README.md`**, **`cli-program.md`**, **`bundled-docs.md`**, Cursor rule template — cross-links to output-schema guide.
44
+
10
45
  ## [3.6.3] - 2026-06-23
11
46
 
12
47
  ### Added
@@ -20,6 +55,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
20
55
  - **`cli-program.md`** — `CliLeafInputs` / `readLeafInputs()` semantics, upgrading to 3.6+, read-once-resolve-once cross-links.
21
56
  - **`bundled-docs.md`** — framework docs vs consumer docgen.
22
57
  - **`docs/mcp.md`** — varargs JSON array only (fixes stale comma-string guidance).
58
+ - **`consumers-dev` / `consumers-sync`** — refresh consumer `.cursor/rules/cli-program.mdc` from template via `scripts/merge-cli-program-rule.ts`.
23
59
 
24
60
  ## [3.6.2] - 2026-06-23
25
61
 
@@ -413,7 +449,9 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
413
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`).
414
450
  - Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
415
451
 
416
- [Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v3.6.3...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
454
+ [3.6.4]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.6.4
417
455
  [3.6.3]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.6.3
418
456
  [3.6.2]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.6.2
419
457
  [3.6.1]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.6.1
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
 
@@ -155,13 +156,13 @@ mkdir -p .cursor/rules
155
156
  cp node_modules/argsbarg/docs/templates/cursor/rules/cli-program.mdc .cursor/rules/cli-program.mdc
156
157
  ```
157
158
 
158
- Add app-specific conventions in a second rule if needed. Documentation map: **[docs/README.md](docs/README.md)**. Authoring guide: **[docs/cli-program.md](docs/cli-program.md)**.
159
+ 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
160
 
160
161
 
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
@@ -6,12 +6,25 @@ Start here to pick the right guide.
6
6
  | --- | --- |
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
+ | **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 |
9
11
  | **Exposing MCP tools** | [mcp.md](mcp.md) — stdio server, `inputSchema`, varargs, `install --mcp` |
10
12
  | **Shipping install / completions / skills** | [install.md](install.md) — `myapp install`, shell completions |
11
13
  | **Bundling `myapp docs` topics** | [bundled-docs.md](bundled-docs.md) — consumer docgen vs framework docs |
12
14
  | **Agent skills** | [ai-skills.md](ai-skills.md) — `install --skill`, `docs skill` |
13
15
  | **Maintaining the argsbarg repo** | [developing.md](developing.md) — release, consumers, npm `files` |
14
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) |
15
28
 
16
29
  ## Framework docs vs consumer docgen
17
30
 
@@ -22,11 +35,3 @@ Start here to pick the right guide.
22
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 |
23
36
 
24
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.
25
-
26
- ## Examples
27
-
28
- | Example | Shows |
29
- | --- | --- |
30
- | [../examples/minimal.ts](../examples/minimal.ts) | Presence + string flags, fallback routing |
31
- | [../examples/nested.ts](../examples/nested.ts) | Nested commands, varargs, MCP + `docs` |
32
- | [../examples/formats.ts](../examples/formats.ts) | `CliValueFormat`, `default`, `readLeafInputs()` |
@@ -11,6 +11,8 @@ Two documentation layers often coexist in a consumer repo:
11
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
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
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
+
14
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.
15
17
 
16
18
  See [docs/README.md](README.md) for the full documentation map.
@@ -85,7 +87,7 @@ When `docs.enabled` is `true`:
85
87
 
86
88
  ## MCP guide (`docs mcp`)
87
89
 
88
- 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.
89
91
 
90
92
  There is no override API in v1 — customize behavior via `mcpTool.description` on leaf commands.
91
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
@@ -134,6 +133,8 @@ On **leaf commands**, set `outputSchema` to a JSON Schema describing stdout when
134
133
 
135
134
  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`.
136
135
 
136
+ For a **outputSchema codegen guidelines** (TypeScript types → JSON Schema → `outputSchema` constants), see [output-schema.md](output-schema.md).
137
+
137
138
  Do **not** use `mcpTool.description` to paper over missing `--yes`, non-standard flag names, or handlers that only work interactively — fix those instead.
138
139
 
139
140
  If help text and MCP behavior match after your fixes, **omit `mcpTool` entirely**.
@@ -371,9 +372,70 @@ handler: async (ctx) => {
371
372
 
372
373
  Basic synchronous handlers do not need this structure — only commands with an interactive branch.
373
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
+
374
436
  ## Reserved names
375
437
 
376
- 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.
377
439
 
378
440
  ## Cursor rule for consumer repos
379
441
 
@@ -388,15 +450,27 @@ mkdir -p .cursor/rules
388
450
  cp node_modules/argsbarg/docs/templates/cursor/rules/cli-program.mdc .cursor/rules/cli-program.mdc
389
451
  ```
390
452
 
391
- The template is ~25 lines: when to read which doc, plus hard rules agents often get wrong. It does **not** duplicate this guide — re-copy after upgrading argsbarg when the template changes.
453
+ The template is ~25 lines: when to read which doc, plus hard rules agents often get wrong. It does **not** duplicate this guide.
454
+
455
+ 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:
456
+
457
+ ```markdown
458
+ **sqsp-qa conventions:**
459
+
460
+ - Shared mutator flags: `readQaMutatingFlags(ctx)` in `src/cli/shared.ts`.
461
+ - Per command: `read*Flags` + `resolve*Input` in `commands/<name>/resolve.ts`.
462
+ ```
463
+
464
+ 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.
392
465
 
393
- 2. **Optional:** a second rule for app-only conventions (e.g. `src/cli/shared.ts` flag names, JSON-only handlers, Ink patterns).
466
+ 3. **Optional:** a separate rule (e.g. `.cursor/argsbarg.mdc` or `AGENTS.md`) for broader package API notes.
394
467
 
395
- 3. **Optional:** `.cursor/argsbarg.mdc` or `AGENTS.md` pointing at `node_modules/argsbarg/docs/cli-program.md` for broader context.
468
+ **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.
396
469
 
397
470
  ## See also
398
471
 
399
472
  - [Documentation map](README.md) — which doc to read when
473
+ - [Output schemas](output-schema.md) — codegen pipeline for leaf `outputSchema`
400
474
  - [Developing argsbarg](developing.md) — release, consumer sync, npm `files`
401
475
  - [MCP server](mcp.md) — tools, schema resource, env bootstrapping
402
476
  - [Agent skills](ai-skills.md) — `install --skill`
@@ -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.
@@ -32,12 +32,16 @@ Sibling repos under `../../ss/` (paths are machine-specific; adjust in `justfile
32
32
 
33
33
  | Recipe | When | Effect |
34
34
  | --- | --- | --- |
35
- | `just consumer-dev` | Before publish; hacking on argsbarg locally | `bun add argsbarg@file:<relative>` in each consumer |
36
- | `just consumers-sync` | After release | Sets `"argsbarg": "^<this package.json version>"`, `bun install`, `just build`, `just docgen`, `just install` |
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
37
 
38
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
39
 
40
- Re-copy `docs/templates/cursor/rules/cli-program.mdc` into consumer repos when the template changes (append app-specific conventions; do not fork the whole guide).
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.
41
45
 
42
46
  ## npm package contents
43
47
 
@@ -45,6 +49,19 @@ Re-copy `docs/templates/cursor/rules/cli-program.mdc` into consumer repos when t
45
49
 
46
50
  When adding docs or examples intended for consumers, ensure they live under whitelisted paths (`docs/`, `examples/`, `src/`, etc.).
47
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
+
48
65
  ## Docs
49
66
 
50
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