argsbarg 1.5.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/plans/cliprogram_capabilities_refactor_081e1737.plan.md +224 -0
- package/CHANGELOG.md +31 -1
- package/README.md +12 -8
- package/docs/install.md +2 -2
- package/docs/mcp.md +3 -3
- package/examples/mcp-test.ts +3 -3
- package/examples/minimal.ts +3 -3
- package/examples/nested.ts +3 -3
- package/examples/option-required.ts +3 -3
- package/index.d.ts +38 -37
- package/package.json +1 -1
- package/src/builtins/builtins.test.ts +3 -3
- package/src/builtins/completion-bash.ts +3 -3
- package/src/builtins/completion-fish.ts +2 -2
- package/src/builtins/completion-group.ts +2 -2
- package/src/builtins/completion-zsh.ts +3 -3
- package/src/builtins/dispatch.ts +41 -26
- package/src/builtins/export.ts +15 -8
- package/src/builtins/install.ts +3 -3
- package/src/builtins/mcp.ts +2 -2
- package/src/builtins/presentation.ts +34 -23
- package/src/builtins/scopes.ts +9 -8
- package/src/capabilities.ts +32 -0
- package/src/context.ts +17 -7
- package/src/help.ts +21 -9
- package/src/index.test.ts +128 -121
- package/src/index.ts +1 -1
- package/src/install/binary.ts +3 -3
- package/src/install/completions.ts +2 -2
- package/src/install/detect-installed.ts +1 -1
- package/src/install/index.ts +4 -4
- package/src/install/install.test.ts +2 -2
- package/src/install/mcp-config.ts +2 -2
- package/src/install/paths.ts +3 -3
- package/src/install/plan.ts +4 -4
- package/src/install/status.ts +2 -2
- package/src/install/uninstall.ts +2 -2
- package/src/invoke.ts +14 -5
- package/src/mcp/server.ts +3 -3
- package/src/mcp/tools.ts +16 -16
- package/src/mcp.ts +2 -2
- package/src/parse.ts +55 -27
- package/src/runtime.ts +34 -25
- package/src/schema.ts +6 -6
- package/src/skill/generate.ts +6 -6
- package/src/skill/install.ts +2 -2
- package/src/types.test.ts +40 -0
- package/src/types.ts +54 -44
- package/src/validate.ts +87 -72
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: CliProgram capabilities refactor
|
|
3
|
+
overview: Introduce `CliNode` / `CliProgram` types and an internal capabilities resolver, replacing `CliCommand` in the public API as a **2.0.0 breaking change**. Keep capability machinery private to avoid locking in unstable internals.
|
|
4
|
+
todos:
|
|
5
|
+
- id: types-split
|
|
6
|
+
content: "Refactor src/types.ts: CliNodeBase, CliLeaf, CliRouter, CliNode, CliProgram; remove CliCommand"
|
|
7
|
+
status: completed
|
|
8
|
+
- id: capabilities-module
|
|
9
|
+
content: Add internal src/capabilities.ts (resolveCapabilities, reservedCommandNames)
|
|
10
|
+
status: completed
|
|
11
|
+
- id: wire-validate-presentation
|
|
12
|
+
content: Update validate.ts, presentation.ts, export.ts, dispatch.ts, runtime.ts, invoke.ts to use CliProgram + caps
|
|
13
|
+
status: completed
|
|
14
|
+
- id: internal-walkers
|
|
15
|
+
content: Update parse, context, mcp/tools, completion scopes, install paths to CliNode/CliProgram as appropriate
|
|
16
|
+
status: completed
|
|
17
|
+
- id: public-api-2
|
|
18
|
+
content: Update index.ts exports (CliProgram only), run typegen, bump 2.0.0 CHANGELOG; verify qa-cli + idp-trees compile against local checkout
|
|
19
|
+
status: completed
|
|
20
|
+
- id: examples-docs
|
|
21
|
+
content: Migrate examples + README to CliProgram / satisfies pattern
|
|
22
|
+
status: completed
|
|
23
|
+
- id: tests-migrate
|
|
24
|
+
content: Migrate tests; keep runtime negative tests with cast helpers
|
|
25
|
+
status: completed
|
|
26
|
+
- id: nice-to-haves
|
|
27
|
+
content: "Optional: ctx.program getter, cliValidateProgram rename, satisfies examples, @ts-expect-error type tests"
|
|
28
|
+
status: completed
|
|
29
|
+
isProject: false
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
# CliProgram + internal capabilities (2.0)
|
|
33
|
+
|
|
34
|
+
## Goals
|
|
35
|
+
|
|
36
|
+
- **Types**: `CliNode` (user tree) + `CliProgram` (what `cliRun` accepts) with root-only `mcpServer` / `install` only on `CliProgram`.
|
|
37
|
+
- **Capabilities**: One internal resolver drives reserved names, help/schema/completion visibility, and dispatch guards.
|
|
38
|
+
- **Exports**: Minimal public surface — no `resolveCapabilities`, no presentation helpers, no builtin command builders.
|
|
39
|
+
- **Breaking**: Drop `CliCommand` from the public API — ship **2.0.0 directly** (Option B; no deprecated alias release).
|
|
40
|
+
|
|
41
|
+
## Type model
|
|
42
|
+
|
|
43
|
+
Add to [`src/types.ts`](src/types.ts):
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
interface CliNodeBase { key; description; notes?; options? }
|
|
47
|
+
|
|
48
|
+
type CliLeaf = CliNodeBase & { handler; positionals?; mcpTool? }
|
|
49
|
+
type CliRouter = CliNodeBase & { commands: CliNode[]; fallbackCommand?; fallbackMode? }
|
|
50
|
+
type CliNode = CliLeaf | CliRouter
|
|
51
|
+
|
|
52
|
+
type CliProgram = CliNode & { mcpServer?; install? }
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Remove `mcpServer`, `install`, `mcpTool` from a shared `CliCommandBase`. Remove the old `CliCommand` union entirely **inside the repo**.
|
|
56
|
+
|
|
57
|
+
**Leaf program roots** (`examples/minimal.ts`) remain valid: `CliProgram` = leaf + root config.
|
|
58
|
+
|
|
59
|
+
```mermaid
|
|
60
|
+
flowchart TB
|
|
61
|
+
subgraph public [Public API]
|
|
62
|
+
CliProgram
|
|
63
|
+
cliRun["cliRun(program)"]
|
|
64
|
+
cliInvoke["cliInvoke(program, argv)"]
|
|
65
|
+
end
|
|
66
|
+
subgraph internal [Internal only]
|
|
67
|
+
resolveCaps["resolveCapabilities(program)"]
|
|
68
|
+
presentation["presentationRoot(program)"]
|
|
69
|
+
dispatch["dispatchBuiltin(...)"]
|
|
70
|
+
end
|
|
71
|
+
CliProgram --> cliRun
|
|
72
|
+
CliProgram --> resolveCaps
|
|
73
|
+
resolveCaps --> presentation
|
|
74
|
+
resolveCaps --> dispatch
|
|
75
|
+
presentation --> help["help / schema / completions"]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Export policy (intentionally narrow)
|
|
79
|
+
|
|
80
|
+
### Export from [`src/index.ts`](src/index.ts) only
|
|
81
|
+
|
|
82
|
+
| Symbol | Action |
|
|
83
|
+
|--------|--------|
|
|
84
|
+
| `CliProgram` | **Add** — primary schema type |
|
|
85
|
+
| `CliCommand` | **Remove** — breaking |
|
|
86
|
+
| `CliNode`, `CliLeaf`, `CliRouter` | **Do not export** — authors use `satisfies CliProgram` or `typeof program.commands[n]` |
|
|
87
|
+
| `CliOption`, `CliPositional`, config types | unchanged |
|
|
88
|
+
| `cliRun`, `cliInvoke`, `CliContext`, enums | unchanged signatures, `CliProgram` param |
|
|
89
|
+
|
|
90
|
+
Run `just typegen` so [`index.d.ts`](index.d.ts) reflects only the barrel.
|
|
91
|
+
|
|
92
|
+
### Keep internal (not in barrel, no deep `package.json` exports)
|
|
93
|
+
|
|
94
|
+
- [`src/capabilities.ts`](src/capabilities.ts) (new): `resolveCapabilities`, `reservedCommandNames`
|
|
95
|
+
- [`src/builtins/presentation.ts`](src/builtins/presentation.ts), [`export.ts`](src/builtins/export.ts), [`dispatch.ts`](src/builtins/dispatch.ts)
|
|
96
|
+
- Completion emitters, install module, `setCompiledExecutableOverride`
|
|
97
|
+
- [`src/completion.ts`](src/completion.ts) shim — trim re-exports if any leak toward public paths; tests import from `builtins/` or `completion.ts` directly in-repo only
|
|
98
|
+
|
|
99
|
+
**Rule**: If it decides *when* a builtin appears, it stays internal. Consumers only see the resulting CLI behavior.
|
|
100
|
+
|
|
101
|
+
## Internal capabilities module
|
|
102
|
+
|
|
103
|
+
New [`src/capabilities.ts`](src/capabilities.ts):
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
interface CliCapabilities {
|
|
107
|
+
completion: true;
|
|
108
|
+
mcp: boolean; // !!program.mcpServer
|
|
109
|
+
install: boolean; // isCompiledExecutable() && program.install?.enabled !== false
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function resolveCapabilities(program: CliProgram): CliCapabilities
|
|
113
|
+
function reservedCommandNames(caps: CliCapabilities): string[]
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Wire callers to use this instead of re-deriving:
|
|
117
|
+
|
|
118
|
+
| File | Change |
|
|
119
|
+
|------|--------|
|
|
120
|
+
| [`src/validate.ts`](src/validate.ts) | `cliValidateProgram(program)`; reserved names from `caps`; **keep** runtime root-only checks for untyped/JS abuse |
|
|
121
|
+
| [`src/builtins/presentation.ts`](src/builtins/presentation.ts) | `presentationBuiltins(program, caps)` |
|
|
122
|
+
| [`src/builtins/export.ts`](src/builtins/export.ts) | same |
|
|
123
|
+
| [`src/builtins/dispatch.ts`](src/builtins/dispatch.ts) | derive `caps` once from `program` |
|
|
124
|
+
| [`src/runtime.ts`](src/runtime.ts) | `cliRun(program: CliProgram)` |
|
|
125
|
+
|
|
126
|
+
Walkers (`parse`, `mcp/tools`, completion scopes) take `CliNode` where they recurse; entrypoints take `CliProgram`.
|
|
127
|
+
|
|
128
|
+
**Presentation vs user tree**: Help, `--schema`, and completion emitters consume `cliPresentationRoot(program)` (synthetic router with builtin stubs), not raw `CliProgram`. Capability logic decides what gets injected; emitters keep walking the same presentation shape.
|
|
129
|
+
|
|
130
|
+
**Validate edge cases** (keep at runtime even when TS catches most mistakes):
|
|
131
|
+
|
|
132
|
+
- `mcpServer` / `install` on inner nodes — reject (untyped/JS abuse)
|
|
133
|
+
- `mcpTool` on program root (leaf-shaped `CliProgram`) — reject (same as today)
|
|
134
|
+
- Reserved command names from `reservedCommandNames(caps)` — drop the old `cliPresentationRoot` escape hatch that skips injection when user already declared `completion`
|
|
135
|
+
|
|
136
|
+
## Context typing
|
|
137
|
+
|
|
138
|
+
[`src/context.ts`](src/context.ts):
|
|
139
|
+
|
|
140
|
+
- Change `ctx.schema` type to `CliProgram` (field name unchanged — avoids extra public surface).
|
|
141
|
+
- **Nice-to-have**: add `get program(): CliProgram` alias returning `this.schema` with JSDoc pointing to `schema` for familiarity. Do **not** export a new type for this.
|
|
142
|
+
|
|
143
|
+
## Consumer migration (2.0)
|
|
144
|
+
|
|
145
|
+
### Before/after (representative of qa-cli, idp-trees, and all `: CliCommand`-typed consumers)
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
// Before (1.x)
|
|
149
|
+
import { cliRun, type CliCommand, CliOptionKind, CliFallbackMode } from "argsbarg";
|
|
150
|
+
const cli: CliCommand = { key: 'myapp', commands: [...], mcpServer: {...} };
|
|
151
|
+
await cliRun(cli);
|
|
152
|
+
|
|
153
|
+
// After (2.0)
|
|
154
|
+
import { cliRun, type CliProgram, CliOptionKind, CliFallbackMode } from "argsbarg";
|
|
155
|
+
const cli = { key: 'myapp', commands: [...], mcpServer: {...} } satisfies CliProgram;
|
|
156
|
+
await cliRun(cli);
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
No structural change for well-formed apps — only import + type annotation. `: CliProgram` also works if the consumer prefers explicit annotation over `satisfies`. Both patterns are supported and type-check identically.
|
|
160
|
+
|
|
161
|
+
### Consumer impact (verified)
|
|
162
|
+
|
|
163
|
+
| Consumer | Files importing argsbarg | Uses `CliCommand`? | Uses `mcpServer`/`install`? | Migration cost |
|
|
164
|
+
|---|---|---|---|---|
|
|
165
|
+
| qa-cli | 3 (`index.tsx`, `mcpConfig.ts`, `mcp.ts`) | `const cli: CliCommand` | `mcpServer` on root | Replace `CliCommand` → `CliProgram` in 1 import, 1 annotation |
|
|
166
|
+
| idp-trees | 3 (`index.tsx`, `mcp/config.ts`, `headless/mode.ts`) | `const cli: CliCommand` | `mcpServer` on root | Replace `CliCommand` → `CliProgram` in 1 import, 1 annotation |
|
|
167
|
+
|
|
168
|
+
Both consumers only use `CliCommand` for the root schema annotation. Neither imports `CliNode`/`CliLeaf`/`CliRouter` (they can't — they don't exist yet). Neither has deeply nested command trees (max depth 2). All other imported types (`CliOptionKind`, `CliFallbackMode`, `CliMcpServerConfig`, `CliMcpToolConfig`, `CliInvocation`, `CliContext`, `isInteractiveTty`) remain unchanged — those files need zero changes.
|
|
169
|
+
|
|
170
|
+
### `install` builtin default behavior
|
|
171
|
+
|
|
172
|
+
Both qa-cli and idp-trees rely on the `install` builtin being **enabled by default** (they do not set `install` on the root). The capabilities resolver must preserve this: `install: isCompiledExecutable() && program.install?.enabled !== false` — which defaults to enabled when `install` is absent.
|
|
173
|
+
|
|
174
|
+
## Tests and negative cases
|
|
175
|
+
|
|
176
|
+
- Update fixtures in [`src/index.test.ts`](src/index.test.ts), [`src/builtins/builtins.test.ts`](src/builtins/builtins.test.ts), [`src/install/install.test.ts`](src/install/install.test.ts): `CliProgram` / `CliNode` internally.
|
|
177
|
+
- Runtime rejection tests (`mcpServer` on nested node) use `as unknown as CliProgram` or a small `invalidProgram()` helper — proves validate still catches misuse without TS.
|
|
178
|
+
|
|
179
|
+
## Docs and changelog
|
|
180
|
+
|
|
181
|
+
- [`README.md`](README.md): `CliProgram`, capabilities mental model (1 short paragraph), reserved names derived from config.
|
|
182
|
+
- [`CHANGELOG.md`](CHANGELOG.md): **2.0.0** — `CliCommand` removed; `CliProgram` added; show before/after migration snippet. Include a note that structural schema shape is unchanged — only the type name and annotation pattern differ.
|
|
183
|
+
- Optional short **Architecture** note in README (not a new doc file unless you want one).
|
|
184
|
+
|
|
185
|
+
### 2.0.0 migration snippet (for CHANGELOG)
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
// 1.x
|
|
189
|
+
import { type CliCommand } from "argsbarg";
|
|
190
|
+
const cli: CliCommand = { ... };
|
|
191
|
+
// 2.0
|
|
192
|
+
import { type CliProgram } from "argsbarg";
|
|
193
|
+
const cli = { ... } satisfies CliProgram; // or : CliProgram
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Version and release
|
|
197
|
+
|
|
198
|
+
**2.0.0** — rename-only breaking change for typed consumers; runtime behavior unchanged.
|
|
199
|
+
|
|
200
|
+
**Release path (chosen): Option B** — jump straight to 2.0.0. No `CliCommand` deprecated alias in 1.6. Breaking changes are acceptable; known consumers (qa-cli, idp-trees) migrate in one import + one annotation each.
|
|
201
|
+
|
|
202
|
+
### Pre-release checklist (required before tag)
|
|
203
|
+
|
|
204
|
+
1. `just test` green in argsbarg
|
|
205
|
+
2. `just typegen` — `index.d.ts` exports `CliProgram` only (no `CliCommand`)
|
|
206
|
+
3. Point qa-cli and idp-trees `package.json` at local argsbarg checkout; confirm `tsc` / build passes with `CliProgram` migration applied in those repos
|
|
207
|
+
4. CHANGELOG 2.0.0 entry with migration snippet
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## Nice-to-haves (defer if time-boxed)
|
|
212
|
+
|
|
213
|
+
1. **`satisfies CliProgram`** in all examples (better DX than `: CliProgram` annotation). Verify that `satisfies` still infers precise sub-command types (not widening to `object`), particularly in nested structures like `examples/nested.ts`.
|
|
214
|
+
2. **`ctx.program` getter** — alias for `ctx.schema`; document `schema` as legacy name in JSDoc only (no removal in 2.0).
|
|
215
|
+
3. **Internal rename** `cliValidateRoot` → `cliValidateProgram` (not exported today; safe).
|
|
216
|
+
4. **Type tests** in `src/types.test.ts` — compile-only assertions that invalid shapes fail (e.g. `mcpServer` on `CliNode`) using `@ts-expect-error` snippets.
|
|
217
|
+
5. **README diagram** — small mermaid of user tree vs injected capabilities (documentation only).
|
|
218
|
+
|
|
219
|
+
## Explicitly out of scope (avoid future breaks)
|
|
220
|
+
|
|
221
|
+
- Exporting `CliCapabilities`, `resolveCapabilities`, `presentationRoot`, builtin command builders
|
|
222
|
+
- Exporting `CliNode` / `CliLeaf` / `CliRouter` (can add in 2.x if demand appears; not needed for `nested.ts`-style apps)
|
|
223
|
+
- `package.json` subpath exports (`argsbarg/builtins`, etc.)
|
|
224
|
+
- Renaming `ctx.schema` in 2.0 (would break handlers that read `.schema`)
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.0.1] - 2026-06-20
|
|
11
|
+
|
|
12
|
+
### Removed
|
|
13
|
+
|
|
14
|
+
- **`CliContext.schema`** — use `ctx.program` (removed alias; `program` is the only field).
|
|
15
|
+
|
|
16
|
+
## [2.0.0] - 2026-06-20
|
|
17
|
+
|
|
18
|
+
### Changed (breaking)
|
|
19
|
+
|
|
20
|
+
- **`CliCommand` removed** — use `CliProgram` as the schema type passed to `cliRun` / `cliInvoke`. The runtime object shape is unchanged; only the type name and how you annotate it differ.
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
// 1.x
|
|
24
|
+
import { type CliCommand } from "argsbarg";
|
|
25
|
+
const cli: CliCommand = { ... };
|
|
26
|
+
// 2.0
|
|
27
|
+
import { type CliProgram } from "argsbarg";
|
|
28
|
+
const cli = { ... } satisfies CliProgram; // or : CliProgram
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- **Internal type split** — `CliNode` / `CliLeaf` / `CliRouter` model the user command tree; `CliProgram` adds root-only `mcpServer` and `install`. These are not exported from the public API.
|
|
32
|
+
- **Capabilities resolver** — reserved built-in names (`completion`, `install`, `mcp`) are derived from program config and runtime (compiled binary), not from user-declared commands.
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- **`ctx.program`** — alias for `ctx.schema` on `CliContext` (same `CliProgram` value).
|
|
37
|
+
|
|
10
38
|
## [1.5.0] - 2026-06-20
|
|
11
39
|
|
|
12
40
|
### Added
|
|
@@ -149,7 +177,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
149
177
|
- 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`).
|
|
150
178
|
- Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
|
|
151
179
|
|
|
152
|
-
[Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/
|
|
180
|
+
[Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v2.0.1...HEAD
|
|
181
|
+
[2.0.1]: https://github.com/bdombro/bun-argsbarg/releases/tag/v2.0.1
|
|
182
|
+
[2.0.0]: https://github.com/bdombro/bun-argsbarg/releases/tag/v2.0.0
|
|
153
183
|
[1.5.0]: https://github.com/bdombro/bun-argsbarg/releases/tag/v1.5.0
|
|
154
184
|
[1.4.3]: https://github.com/bdombro/bun-argsbarg/releases/tag/v1.4.3
|
|
155
185
|
[1.4.2]: https://github.com/bdombro/bun-argsbarg/releases/tag/v1.4.2
|
package/README.md
CHANGED
|
@@ -35,9 +35,9 @@ Shell completions! -->
|
|
|
35
35
|
## Usage
|
|
36
36
|
|
|
37
37
|
```typescript
|
|
38
|
-
import { cliRun,
|
|
38
|
+
import { cliRun, type CliProgram, CliOptionKind } from "argsbarg";
|
|
39
39
|
|
|
40
|
-
const cli
|
|
40
|
+
const cli = {
|
|
41
41
|
key: "helloapp",
|
|
42
42
|
description: "Tiny demo.",
|
|
43
43
|
positionals: [
|
|
@@ -64,7 +64,7 @@ const cli: CliCommand = {
|
|
|
64
64
|
}
|
|
65
65
|
console.log(`hello ${name}`);
|
|
66
66
|
},
|
|
67
|
-
};
|
|
67
|
+
} satisfies CliProgram;
|
|
68
68
|
|
|
69
69
|
await cliRun(cli);
|
|
70
70
|
```
|
|
@@ -77,7 +77,7 @@ await cliRun(cli);
|
|
|
77
77
|
|
|
78
78
|
Everything you need for a first-class CLI:
|
|
79
79
|
|
|
80
|
-
- **Nested subcommands** (
|
|
80
|
+
- **Nested subcommands** (router nodes with `commands`, leaf nodes with `handler`)
|
|
81
81
|
- **POSIX-style options** (`-x`, `--long`, `--long=value`) — kinds: presence, string, number, **enum** (`choices` array)
|
|
82
82
|
- **Bundled presence flags** (`-abc`)
|
|
83
83
|
- **Positional arguments and varargs tails** (`CliPositional` objects on `positionals`)
|
|
@@ -147,7 +147,7 @@ bun add bun-argsbarg
|
|
|
147
147
|
|
|
148
148
|
## How it works
|
|
149
149
|
|
|
150
|
-
1. Build a **program root** `
|
|
150
|
+
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).
|
|
151
151
|
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**).
|
|
152
152
|
3. From a handler, `cliErrWithHelp(ctx, "message")` prints a red error line plus contextual help on stderr and exits **1**.
|
|
153
153
|
|
|
@@ -181,7 +181,11 @@ Add `CliPositional` entries to the command’s `positionals` list (separate from
|
|
|
181
181
|
- `ctx.typedOpt<T>("custom", parseFn)` — pass a custom parsing function for type-safe option resolution.
|
|
182
182
|
- `ctx.args` — positional words in order as `string[]`.
|
|
183
183
|
- `ctx.positional("name")` — named positional lookup; varargs slots return `string[]`, single slots return `string | undefined`.
|
|
184
|
-
- `ctx.
|
|
184
|
+
- `ctx.program` — program root (`CliProgram`) for contextual help.
|
|
185
|
+
|
|
186
|
+
### Capabilities (built-ins)
|
|
187
|
+
|
|
188
|
+
`completion`, `install`, and `mcp` are not part of your schema — they are injected at runtime from program-level config (`mcpServer`, compiled binary + `install`). Reserved command names follow from that config: `completion` and `install` are always reserved; `mcp` is reserved when `mcpServer` is set.
|
|
185
189
|
|
|
186
190
|
|
|
187
191
|
|
|
@@ -192,7 +196,7 @@ Check the `examples/` directory for full working scripts:
|
|
|
192
196
|
| Example | File | Shows |
|
|
193
197
|
| --- | --- | --- |
|
|
194
198
|
| `ArgsBargMinimal` | `examples/minimal.ts` | String + presence flags, `MissingOrUnknown` fallback. |
|
|
195
|
-
| `ArgsBargNested` | `examples/nested.ts` | Nested
|
|
199
|
+
| `ArgsBargNested` | `examples/nested.ts` | Nested command tree, positional tails, async handlers. |
|
|
196
200
|
|
|
197
201
|
```bash
|
|
198
202
|
export PATH="$PATH:$(pwd)/examples"
|
|
@@ -214,7 +218,7 @@ The package root (`argsbarg` / `src/index.ts`) exports the types and runtime you
|
|
|
214
218
|
|
|
215
219
|
| Symbol | Role |
|
|
216
220
|
| --- | --- |
|
|
217
|
-
| `
|
|
221
|
+
| `CliProgram`, `CliOption`, `CliPositional`, `CliHandler` | Schema and handler types. |
|
|
218
222
|
| `CliOptionKind`, `CliFallbackMode` | Option kinds (`Presence`, `String`, `Number`, `Enum`) and root fallback behavior. |
|
|
219
223
|
| `CliSchemaValidationError` | Thrown when the static command tree violates schema rules. |
|
|
220
224
|
| `CliContext` | Handler context (`ctx.flag`, `ctx.stringOpt`, `ctx.args`, `ctx.invocation`, …). |
|
package/docs/install.md
CHANGED
|
@@ -75,10 +75,10 @@ If an existing entry differs, the command exits with an error unless `--yes` is
|
|
|
75
75
|
## Opt out
|
|
76
76
|
|
|
77
77
|
```typescript
|
|
78
|
-
const cli
|
|
78
|
+
const cli = {
|
|
79
79
|
key: "myapp",
|
|
80
80
|
description: "...",
|
|
81
81
|
install: { enabled: false },
|
|
82
82
|
// ...
|
|
83
|
-
};
|
|
83
|
+
} satisfies CliProgram;
|
|
84
84
|
```
|
package/docs/mcp.md
CHANGED
|
@@ -9,12 +9,12 @@ MCP is **opt-in**. Apps that do not set `mcpServer` on the program root behave e
|
|
|
9
9
|
1. Add `mcpServer` to your program root:
|
|
10
10
|
|
|
11
11
|
```typescript
|
|
12
|
-
const cli
|
|
12
|
+
const cli = {
|
|
13
13
|
key: "myapp",
|
|
14
14
|
description: "My app.",
|
|
15
15
|
mcpServer: { name: "myapp", version: "1.0.0" },
|
|
16
16
|
commands: [/* ... */],
|
|
17
|
-
};
|
|
17
|
+
} satisfies CliProgram;
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
`mcpServer: {}` is enough to enable the server. Optional fields override defaults (see [Configuration](#configuration)).
|
|
@@ -62,7 +62,7 @@ Any host that spawns a subprocess and wires stdin/stdout works the same way: the
|
|
|
62
62
|
|
|
63
63
|
## Configuration
|
|
64
64
|
|
|
65
|
-
Set `mcpServer` on the **program root only** (the `
|
|
65
|
+
Set `mcpServer` on the **program root only** (the `CliProgram` passed to `cliRun`). Validation rejects `mcpServer` on nested nodes.
|
|
66
66
|
|
|
67
67
|
| Field | Default | Purpose |
|
|
68
68
|
| --- | --- | --- |
|
package/examples/mcp-test.ts
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
MCP test fixture for subprocess integration tests only.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { cliRun,
|
|
6
|
+
import { cliRun, CliProgram, CliOptionKind } from "../src/index.ts";
|
|
7
7
|
|
|
8
8
|
const envFilePath = process.env.ARGS_TEST_ENV_FILE;
|
|
9
9
|
|
|
10
|
-
const cli
|
|
10
|
+
const cli = {
|
|
11
11
|
key: "mcp-test",
|
|
12
12
|
description: "MCP integration test fixture.",
|
|
13
13
|
mcpServer: {
|
|
@@ -61,6 +61,6 @@ const cli: CliCommand = {
|
|
|
61
61
|
},
|
|
62
62
|
},
|
|
63
63
|
],
|
|
64
|
-
};
|
|
64
|
+
} satisfies CliProgram;
|
|
65
65
|
|
|
66
66
|
await cliRun(cli);
|
package/examples/minimal.ts
CHANGED
|
@@ -7,9 +7,9 @@ readers can copy the pattern into their own scripts quickly.
|
|
|
7
7
|
It demonstrates the minimal Bun integration path.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { cliRun,
|
|
10
|
+
import { cliRun, CliProgram, CliOptionKind } from "../src/index.ts";
|
|
11
11
|
|
|
12
|
-
const cli
|
|
12
|
+
const cli = {
|
|
13
13
|
key: "minimal.ts",
|
|
14
14
|
description: "Tiny demo.",
|
|
15
15
|
positionals: [
|
|
@@ -36,6 +36,6 @@ const cli: CliCommand = {
|
|
|
36
36
|
}
|
|
37
37
|
console.log(`hello ${name}`);
|
|
38
38
|
},
|
|
39
|
-
};
|
|
39
|
+
} satisfies CliProgram;
|
|
40
40
|
|
|
41
41
|
await cliRun(cli);
|
package/examples/nested.ts
CHANGED
|
@@ -7,9 +7,9 @@ and fallback commands fit together in one schema.
|
|
|
7
7
|
It demonstrates how the schema scales beyond one command.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { cliRun,
|
|
10
|
+
import { cliRun, CliProgram, CliOptionKind, CliFallbackMode } from "../src/index.ts";
|
|
11
11
|
|
|
12
|
-
const cli
|
|
12
|
+
const cli = {
|
|
13
13
|
key: "nested.ts",
|
|
14
14
|
description: "Nested groups demo.",
|
|
15
15
|
mcpServer: { name: "nested-demo", version: "1.0.0" },
|
|
@@ -97,6 +97,6 @@ const cli: CliCommand = {
|
|
|
97
97
|
],
|
|
98
98
|
fallbackCommand: "read",
|
|
99
99
|
fallbackMode: CliFallbackMode.MissingOrUnknown,
|
|
100
|
-
};
|
|
100
|
+
} satisfies CliProgram;
|
|
101
101
|
|
|
102
102
|
await cliRun(cli);
|
|
@@ -7,9 +7,9 @@ readers can copy the pattern into their own scripts quickly.
|
|
|
7
7
|
It demonstrates the minimal Bun integration path.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { cliRun,
|
|
10
|
+
import { cliRun, CliProgram, CliOptionKind, CliFallbackMode, isInteractiveTty } from "../src/index.ts";
|
|
11
11
|
|
|
12
|
-
const cli
|
|
12
|
+
const cli = {
|
|
13
13
|
key: "option-required.ts",
|
|
14
14
|
description: "Demo of a required option.",
|
|
15
15
|
options: [
|
|
@@ -42,6 +42,6 @@ const cli: CliCommand = {
|
|
|
42
42
|
console.log(`requiredNonTty: ${requiredNonTty}`);
|
|
43
43
|
console.log(`optional: ${optional}`);
|
|
44
44
|
},
|
|
45
|
-
};
|
|
45
|
+
} satisfies CliProgram;
|
|
46
46
|
|
|
47
47
|
await cliRun(cli);
|
package/index.d.ts
CHANGED
|
@@ -7,11 +7,11 @@ export declare class CliContext {
|
|
|
7
7
|
readonly appName: string;
|
|
8
8
|
readonly commandPath: string[];
|
|
9
9
|
readonly args: string[];
|
|
10
|
-
readonly
|
|
10
|
+
readonly program: CliProgram;
|
|
11
11
|
readonly opts: Record<string, string>;
|
|
12
12
|
readonly invocation: CliInvocation;
|
|
13
|
-
/** Captures the
|
|
14
|
-
constructor(appName: string, commandPath: string[], args: string[], opts: Record<string, string>,
|
|
13
|
+
/** Captures the program root, routed path, positional words, and option map for a leaf handler. */
|
|
14
|
+
constructor(appName: string, commandPath: string[], args: string[], opts: Record<string, string>, program: CliProgram, invocation?: CliInvocation);
|
|
15
15
|
/** Returns whether a presence flag was set (including implicit "1" for boolean options). */
|
|
16
16
|
hasFlag(name: string): boolean;
|
|
17
17
|
/** Returns the string value for a string-valued option, if present. */
|
|
@@ -64,7 +64,7 @@ export declare enum CliFallbackMode {
|
|
|
64
64
|
UnknownOnly = "unknownOnly"
|
|
65
65
|
}
|
|
66
66
|
/**
|
|
67
|
-
* A named flag or value option (`--long`, `-short`), listed on `
|
|
67
|
+
* A named flag or value option (`--long`, `-short`), listed on command `options`.
|
|
68
68
|
*/
|
|
69
69
|
export interface CliOption {
|
|
70
70
|
/** Option name (e.g., "name", "verbose"). */
|
|
@@ -84,7 +84,7 @@ export interface CliOption {
|
|
|
84
84
|
choices?: string[];
|
|
85
85
|
}
|
|
86
86
|
/**
|
|
87
|
-
* An ordered positional argument slot, listed on `
|
|
87
|
+
* An ordered positional argument slot, listed on leaf `positionals`.
|
|
88
88
|
*/
|
|
89
89
|
export interface CliPositional {
|
|
90
90
|
/** Positional name (used in help and error messages). */
|
|
@@ -105,7 +105,7 @@ export interface CliPositional {
|
|
|
105
105
|
argMax?: number;
|
|
106
106
|
}
|
|
107
107
|
/**
|
|
108
|
-
*
|
|
108
|
+
* Enables `myapp mcp` and MCP stdio server metadata (program root only).
|
|
109
109
|
*/
|
|
110
110
|
export interface CliMcpServerConfig {
|
|
111
111
|
/** `initialize` serverInfo.name (default: root `key`). */
|
|
@@ -166,7 +166,7 @@ export interface CliMcpToolConfig {
|
|
|
166
166
|
requiresEnv?: string[];
|
|
167
167
|
}
|
|
168
168
|
/**
|
|
169
|
-
*
|
|
169
|
+
* Opt-out and defaults for the `install` built-in (compiled binaries only; program root only).
|
|
170
170
|
*/
|
|
171
171
|
export interface CliInstallConfig {
|
|
172
172
|
/** When `false`, hide/disable `install` (default: enabled). */
|
|
@@ -175,9 +175,9 @@ export interface CliInstallConfig {
|
|
|
175
175
|
prefix?: string;
|
|
176
176
|
}
|
|
177
177
|
/**
|
|
178
|
-
* Base properties shared by all command
|
|
178
|
+
* Base properties shared by all nodes in the user command tree.
|
|
179
179
|
*/
|
|
180
|
-
export interface
|
|
180
|
+
export interface CliNodeBase {
|
|
181
181
|
/** Program or command key (e.g., "myapp", "stat", "owner"). */
|
|
182
182
|
key: string;
|
|
183
183
|
/** Short description shown in help. */
|
|
@@ -186,49 +186,50 @@ export interface CliCommandBase {
|
|
|
186
186
|
notes?: string;
|
|
187
187
|
/** Global or command-level flags/options. */
|
|
188
188
|
options?: CliOption[];
|
|
189
|
-
/** Root-only. When set, enables the `mcp` built-in subcommand. */
|
|
190
|
-
mcpServer?: CliMcpServerConfig;
|
|
191
|
-
/** Root-only. Opt-out and defaults for `install` (compiled binaries only). */
|
|
192
|
-
install?: CliInstallConfig;
|
|
193
|
-
/** Leaf-only. Per-tool MCP exposure and metadata. */
|
|
194
|
-
mcpTool?: CliMcpToolConfig;
|
|
195
189
|
}
|
|
196
190
|
/**
|
|
197
|
-
* A command node
|
|
198
|
-
*
|
|
199
|
-
* The value passed to cliRun is the program root: name is the app/binary name.
|
|
200
|
-
* The root may be a routing group or a leaf command.
|
|
191
|
+
* A leaf command node with a handler and optional positionals.
|
|
201
192
|
*/
|
|
202
|
-
export type
|
|
193
|
+
export type CliLeaf = CliNodeBase & {
|
|
203
194
|
/** Handler function for leaf commands. */
|
|
204
195
|
handler: CliHandler;
|
|
205
196
|
/** Positional argument definitions. */
|
|
206
197
|
positionals?: CliPositional[];
|
|
207
|
-
/**
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
198
|
+
/** Per-tool MCP exposure and metadata. */
|
|
199
|
+
mcpTool?: CliMcpToolConfig;
|
|
200
|
+
};
|
|
201
|
+
/**
|
|
202
|
+
* A routing command node with nested subcommands.
|
|
203
|
+
*/
|
|
204
|
+
export type CliRouter = CliNodeBase & {
|
|
214
205
|
/** Nested subcommands. */
|
|
215
|
-
commands:
|
|
206
|
+
commands: CliNode[];
|
|
216
207
|
/** Default subcommand when argv omits a command or uses an unknown token at this routing node. */
|
|
217
208
|
fallbackCommand?: string;
|
|
218
|
-
/** How fallbackCommand is applied at this routing node
|
|
209
|
+
/** How fallbackCommand is applied at this routing node. */
|
|
219
210
|
fallbackMode?: CliFallbackMode;
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
211
|
+
};
|
|
212
|
+
/**
|
|
213
|
+
* A node in the user-defined command tree (router or leaf).
|
|
214
|
+
*/
|
|
215
|
+
export type CliNode = CliLeaf | CliRouter;
|
|
216
|
+
/**
|
|
217
|
+
* Program root passed to `cliRun` / `cliInvoke`.
|
|
218
|
+
* May be a leaf or router, plus optional program-level MCP and install config.
|
|
219
|
+
*/
|
|
220
|
+
export type CliProgram = CliNode & {
|
|
221
|
+
/** When set, enables the `mcp` built-in subcommand. */
|
|
222
|
+
mcpServer?: CliMcpServerConfig;
|
|
223
|
+
/** Opt-out and defaults for `install` (compiled binaries only). */
|
|
224
|
+
install?: CliInstallConfig;
|
|
225
|
+
};
|
|
225
226
|
/**
|
|
226
227
|
* Handler closure type for leaf commands.
|
|
227
228
|
* Supports both sync and async handlers.
|
|
228
229
|
*/
|
|
229
230
|
export type CliHandler = (ctx: CliContext) => void | Promise<void>;
|
|
230
231
|
/**
|
|
231
|
-
* Error thrown when the static
|
|
232
|
+
* Error thrown when the static CLI tree violates ArgsBarg rules.
|
|
232
233
|
*/
|
|
233
234
|
export declare class CliSchemaValidationError extends Error {
|
|
234
235
|
/** Creates a schema validation error with a human-readable rule violation. */
|
|
@@ -253,8 +254,8 @@ export interface CliInvokeResult {
|
|
|
253
254
|
* Parses argv against the user root, runs the leaf handler, and returns captured output.
|
|
254
255
|
* Never calls process.exit.
|
|
255
256
|
*/
|
|
256
|
-
export declare function cliInvoke(root:
|
|
257
|
-
export declare function cliRun(
|
|
257
|
+
export declare function cliInvoke(root: CliProgram, argv: string[]): Promise<CliInvokeResult>;
|
|
258
|
+
export declare function cliRun(program: CliProgram, argv?: string[]): Promise<never>;
|
|
258
259
|
export declare function cliErrWithHelp(ctx: CliContext, msg: string): never;
|
|
259
260
|
/** True when stdin is a TTY. */
|
|
260
261
|
export declare const isInteractiveTty: boolean;
|
package/package.json
CHANGED
|
@@ -4,10 +4,10 @@ import { cliBuiltinMcpCommand } from "./mcp.ts";
|
|
|
4
4
|
import { cliPresentationRoot } from "./presentation.ts";
|
|
5
5
|
import { completionBashScript, completionFishScript, completionZshScript } from "./index.ts";
|
|
6
6
|
import { exportPresentationBuiltins } from "./export.ts";
|
|
7
|
-
import {
|
|
7
|
+
import { CliProgram } from "../types.ts";
|
|
8
8
|
import { setCompiledExecutableOverride } from "../install/compiled.ts";
|
|
9
9
|
|
|
10
|
-
const fixture:
|
|
10
|
+
const fixture: CliProgram = {
|
|
11
11
|
key: "myapp",
|
|
12
12
|
description: "Demo app.",
|
|
13
13
|
mcpServer: { name: "myapp" },
|
|
@@ -38,7 +38,7 @@ describe("builtins help copy", () => {
|
|
|
38
38
|
|
|
39
39
|
test("install omits --mcp option when mcpServer unset", () => {
|
|
40
40
|
setCompiledExecutableOverride(true);
|
|
41
|
-
const noMcp:
|
|
41
|
+
const noMcp: CliProgram = { key: "x", description: "x", handler: () => {} };
|
|
42
42
|
const names = installBuiltinOptions(noMcp).map((o) => o.name);
|
|
43
43
|
expect(names).not.toContain("mcp");
|
|
44
44
|
});
|