argsbarg 3.6.1 → 3.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [3.6.2] - 2026-06-23
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`cli-program.md`** — “Read flags once, resolve once” pattern for multi-surface leaves (`read*Flags` + `resolve*Input`).
|
|
15
|
+
|
|
10
16
|
## [3.6.1] - 2026-06-23
|
|
11
17
|
|
|
12
18
|
### Fixed
|
|
@@ -393,7 +399,8 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
|
|
|
393
399
|
- 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`).
|
|
394
400
|
- Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
|
|
395
401
|
|
|
396
|
-
[Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v3.6.
|
|
402
|
+
[Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v3.6.2...HEAD
|
|
403
|
+
[3.6.2]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.6.2
|
|
397
404
|
[3.6.1]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.6.1
|
|
398
405
|
[3.6.0]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.6.0
|
|
399
406
|
[3.5.0]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.5.0
|
package/docs/cli-program.md
CHANGED
|
@@ -195,6 +195,59 @@ const { limit, "skip-readiness": skipReadiness, timeout } = ctx.readLeafInputs()
|
|
|
195
195
|
|
|
196
196
|
Cross-field rules (e.g. `--match-remote` requires `--branch`) stay in consumer `resolve*` layers — argsbarg does not validate those.
|
|
197
197
|
|
|
198
|
+
## Read flags once, resolve once
|
|
199
|
+
|
|
200
|
+
For apps with **Ink + headless + MCP** (multiple surfaces per leaf), avoid scattering `ctx.hasFlag` / `ctx.stringOpt` through the handler. Use two layers:
|
|
201
|
+
|
|
202
|
+
| Layer | Responsibility |
|
|
203
|
+
| --- | --- |
|
|
204
|
+
| **`read*Flags(ctx)`** | Read coerced values from `ctx` (`readLeafInputs()`, `durationOpt`, `commaListOpt`, shared mutator flags) into one typed struct |
|
|
205
|
+
| **`resolve*Input(flags)`** | Cross-field validation and defaults; returns `{ ok, input }` or `{ ok: false, error }` |
|
|
206
|
+
|
|
207
|
+
The handler calls **`read*Flags` once**, passes the struct to **`resolve*Input`**, then branches to Ink, headless, or MCP with the same resolved input.
|
|
208
|
+
|
|
209
|
+
**Shared reads** — when many leaves share options (`yes`, `dry-run`, `json`), one app-level helper (e.g. `readMutatingFlags(ctx)`) plus per-command extensions:
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
// cli/flags.ts
|
|
213
|
+
export function readMutatingFlags(ctx: CliContext) {
|
|
214
|
+
const dryRun = ctx.hasFlag("dry-run");
|
|
215
|
+
return {
|
|
216
|
+
dryRun,
|
|
217
|
+
yes: ctx.hasFlag("yes"),
|
|
218
|
+
explicitJson: wantsExplicitJson(ctx, ctx.hasFlag("json")),
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// commands/reset/resolve.ts
|
|
223
|
+
export function readResetFlags(ctx: CliContext) {
|
|
224
|
+
return {
|
|
225
|
+
...readMutatingFlags(ctx),
|
|
226
|
+
env: ctx.args[0],
|
|
227
|
+
force: ctx.hasFlag("force"),
|
|
228
|
+
services: ctx.commaListOpt("services"),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function resolveResetInput(flags: ReturnType<typeof readResetFlags>) {
|
|
233
|
+
if (!flags.env) return { ok: false, error: "…" };
|
|
234
|
+
return { ok: true, input: { env: flags.env, force: flags.force, services: flags.services } };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// command handler
|
|
238
|
+
handler: async (ctx) => {
|
|
239
|
+
const flags = readResetFlags(ctx);
|
|
240
|
+
await dispatchMutatingCommand({
|
|
241
|
+
dryRun: flags.dryRun,
|
|
242
|
+
headless: shouldRunHeadlessWithYes(ctx, { yes: flags.yes, hasRequiredArgs: !!flags.env, dryRun: flags.dryRun }),
|
|
243
|
+
resolve: () => resolveResetInput(flags),
|
|
244
|
+
/* … */
|
|
245
|
+
});
|
|
246
|
+
};
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
**JSON-only CLIs** — a single `readCommandOptions(ctx)` wrapping `readLeafInputs()` per shared option set is usually enough; full `resolve*` layering is optional.
|
|
250
|
+
|
|
198
251
|
## Headless-capable handlers
|
|
199
252
|
|
|
200
253
|
Simple leaves (read args, print stdout) are already headless — no extra work. **Any handler that might mount Ink, prompt, or open a browser should also implement a scriptable fast path** for:
|
|
@@ -19,5 +19,6 @@ When adding or changing argsbarg schema, leaf handlers, or MCP exposure:
|
|
|
19
19
|
- Interactive leaves: one headless path for MCP, non-TTY CLI, and `--yes` / `--dry-run` / `--json` (`shouldRunHeadless*`, `requireYesInNonTty`); not raw `isTTY`.
|
|
20
20
|
- String options: `format` / `default` / `pattern` per `cli-program.md`; `readLeafInputs()` for multi-flag leaves.
|
|
21
21
|
- Varargs (`argMax: 0`): CLI space-separated; MCP JSON array only — no comma-splitting positionals.
|
|
22
|
+
- Multi-surface leaves (Ink + headless + MCP): one **`read*Flags(ctx)`** per command (or shared family helper + extensions); one **`resolve*Input(flags)`** for cross-field rules — handler reads ctx once, all paths share the struct.
|
|
22
23
|
|
|
23
24
|
**App-specific conventions:** add below or in a separate `.cursor/rules/` file (shared flags path, Ink vs JSON-only, etc.).
|