argsbarg 5.0.2 → 5.1.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.
- package/CHANGELOG.md +16 -2
- package/docs/cli-program.md +4 -4
- package/docs/config-schema.md +9 -9
- package/docs/configure.md +4 -0
- package/docs/mcp.md +1 -1
- package/examples/full-example/README.md +2 -2
- package/examples/full-example/src/program.ts +0 -3
- package/index.d.ts +12 -7
- package/package.json +1 -1
- package/src/builtins/builtins.test.ts +9 -14
- package/src/builtins/config.test.ts +4 -4
- package/src/builtins/config.ts +19 -22
- package/src/builtins/configure.ts +35 -4
- package/src/builtins/dispatch.ts +10 -15
- package/src/builtins/registry.ts +0 -2
- package/src/capabilities.ts +6 -12
- package/src/cli-tool/cli-smoke.test.ts +0 -3
- package/src/cli-tool/create.test.ts +0 -3
- package/src/cli-tool/full-example-capabilities.test.ts +0 -2
- package/src/config/context.test.ts +0 -1
- package/src/config/entry.ts +2 -2
- package/src/config/file.test.ts +0 -5
- package/src/config/resolve.test.ts +0 -7
- package/src/config/resolve.ts +2 -2
- package/src/config/validate.test.ts +0 -6
- package/src/configure/configure.test.ts +0 -11
- package/src/docs/api-guide.test.ts +0 -4
- package/src/docs/docs.test.ts +1 -21
- package/src/docs/mcp-resources.test.ts +0 -6
- package/src/formats.test.ts +0 -5
- package/src/headless.test.ts +0 -4
- package/src/headless.ts +5 -4
- package/src/hidden-mcpb.test.ts +0 -12
- package/src/install/binary-placement.test.ts +0 -5
- package/src/install/gh-release-update.test.ts +0 -5
- package/src/install/install-validate.test.ts +0 -5
- package/src/install/mcp-opencode.test.ts +0 -4
- package/src/install/targets.test.ts +0 -10
- package/src/invoke.test.ts +0 -7
- package/src/mcp/claude.test.ts +0 -4
- package/src/mcp/env.test.ts +0 -5
- package/src/mcp/zip.test.ts +0 -1
- package/src/mcp.integration.test.ts +0 -22
- package/src/parse.test.ts +0 -28
- package/src/schema.ts +1 -1
- package/src/types.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,11 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [5.1.0] - 2026-07-04
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **Breaking: `config get`/`set` → `configure get`/`set`** — top-level `config` removed; app config introspection lives under the `configure` built-in.
|
|
15
|
+
|
|
16
|
+
## [5.0.3] - 2026-07-04
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- **JSDoc style** — short `test()` callbacks no longer carry redundant one-liners; `describe` blocks still documented. `requireYesInNonTty` uses per-parameter JSDoc instead of `@param`. Pure re-export barrels stay comment-free.
|
|
21
|
+
|
|
10
22
|
## [5.0.2] - 2026-07-04
|
|
11
23
|
|
|
12
24
|
### Changed
|
|
13
25
|
|
|
14
|
-
- **JSDoc style** — every `describe`
|
|
26
|
+
- **JSDoc style** — every `describe` block in `src/**/*.test.ts` now has a human-readable JSDoc; `configure` modules brought in line with file headers and symbol docs.
|
|
15
27
|
|
|
16
28
|
## [5.0.1] - 2026-07-04
|
|
17
29
|
|
|
@@ -573,7 +585,9 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
|
|
|
573
585
|
- 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`).
|
|
574
586
|
- Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
|
|
575
587
|
|
|
576
|
-
[Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v5.0
|
|
588
|
+
[Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v5.1.0...HEAD
|
|
589
|
+
[5.1.0]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.1.0
|
|
590
|
+
[5.0.3]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.0.3
|
|
577
591
|
[5.0.2]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.0.2
|
|
578
592
|
[5.0.1]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.0.1
|
|
579
593
|
[5.0.0]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.0.0
|
package/docs/cli-program.md
CHANGED
|
@@ -412,11 +412,11 @@ await cli.run();
|
|
|
412
412
|
|
|
413
413
|
| Field | Default | Purpose |
|
|
414
414
|
| --- | --- | --- |
|
|
415
|
-
| `description` | *(required)* | Shown in prompts, `
|
|
415
|
+
| `description` | *(required)* | Shown in prompts, `configure get`, and bundle manifests |
|
|
416
416
|
| `title` | config key | Short label in interactive `configure` |
|
|
417
417
|
| `default` | — | Used when `jsonSchema` omitted (all-string mode) |
|
|
418
418
|
| `required` | `true` | When `false`, optional unless required by `jsonSchema` |
|
|
419
|
-
| `sensitive` | name heuristic (`token`, `secret`, …) | Redact in prompts, `
|
|
419
|
+
| `sensitive` | name heuristic (`token`, `secret`, …) | Redact in prompts, `configure get`, and status |
|
|
420
420
|
| `env` | — | When set: non-empty host env overrides file; consulted again after `resolve` when `resolve` returns `undefined`; exported to `process.env` after resolve |
|
|
421
421
|
| `resolve` | — | Optional fallback after file; return `undefined` to fall back to `env` (if set) and defaults |
|
|
422
422
|
|
|
@@ -425,7 +425,7 @@ await cli.run();
|
|
|
425
425
|
- Default: `$XDG_CONFIG_HOME/<sanitized-key>/config` or `%APPDATA%/<key>/config`.
|
|
426
426
|
- JSON: flat object keyed by schema names — `{ "apiToken": "…", "maxRetries": 5 }`.
|
|
427
427
|
- **Strict:** unknown keys rejected on load.
|
|
428
|
-
- **CLI:** missing required config exits 1 before the leaf handler (TTY prompt when interactive). Built-in `docs` and `
|
|
428
|
+
- **CLI:** missing required config exits 1 before the leaf handler (TTY prompt when interactive). Built-in `docs` and `configure get`/`set` skip this exit.
|
|
429
429
|
- **MCP:** server stays up; missing config returns `isError: true` at `tools/call`.
|
|
430
430
|
- **Configure:** interactive `configure` runs the app config wizard; **`configure --sync`** refreshes agent artifacts.
|
|
431
431
|
- **Agent integration:** `configure.agentIntegration` (`mcp` | `skill` | `both`) sets default sync targets; see [configure.md](configure.md#configuretargets).
|
|
@@ -436,7 +436,7 @@ See [config-schema.md](config-schema.md) for codegen, [configure.md](configure.m
|
|
|
436
436
|
|
|
437
437
|
## Reserved names
|
|
438
438
|
|
|
439
|
-
Do not declare user commands named `completion`, `configure`, `mcp`, `version`,
|
|
439
|
+
Do not declare user commands named `completion`, `configure`, `mcp`, `version`, or `docs` at the root — ArgsBarg injects these when configured. App config uses `configure get` / `configure set` subcommands (not a top-level `config` command).
|
|
440
440
|
|
|
441
441
|
## Cursor rule for consumer repos
|
|
442
442
|
|
package/docs/config-schema.md
CHANGED
|
@@ -40,7 +40,7 @@ await cli.run();
|
|
|
40
40
|
| --- | --- |
|
|
41
41
|
| Config file | Flat JSON keyed by schema names; strict load (unknown keys rejected) |
|
|
42
42
|
| Interactive `configure` / `--status` | Auto-runs config wizard when `entries` is non-empty; `--status` for read-only inventory |
|
|
43
|
-
| Built-in `
|
|
43
|
+
| Built-in `configure get` / `configure set` | Read/write resolved values (opt-out via `commands: false`) |
|
|
44
44
|
| MCP bundle / Claude plugin | `userConfig` for entries with `env` set |
|
|
45
45
|
| `ctx.appConfig` in handlers | `get`, `require`, `set`, `read`, `path`, `dir` — prefer over `process.env` |
|
|
46
46
|
|
|
@@ -48,7 +48,7 @@ await cli.run();
|
|
|
48
48
|
|
|
49
49
|
**No public config I/O exports** — consumers use `program.appConfig` for authoring and `ctx.appConfig` in handlers.
|
|
50
50
|
|
|
51
|
-
See [cli-program.md — Configuration](cli-program.md#configuration-programappconfig) for resolution order, bootstrap timing, and
|
|
51
|
+
See [cli-program.md — Configuration](cli-program.md#configuration-programappconfig) for resolution order, bootstrap timing, and `configure get`/`set`.
|
|
52
52
|
|
|
53
53
|
## `CliAppConfig` and `CliAppConfigEntry`
|
|
54
54
|
|
|
@@ -191,26 +191,26 @@ appConfig: {
|
|
|
191
191
|
|
|
192
192
|
All file values are strings. Defaults come from `entry.default`.
|
|
193
193
|
|
|
194
|
-
## Built-in `
|
|
194
|
+
## Built-in `configure get` / `configure set`
|
|
195
195
|
|
|
196
196
|
When `program.appConfig` is set and `commands !== false`:
|
|
197
197
|
|
|
198
198
|
| Subcommand | Purpose |
|
|
199
199
|
| --- | --- |
|
|
200
|
-
| `
|
|
201
|
-
| `
|
|
200
|
+
| `configure get [key]` | Resolved value(s); `--json`; `--json --pretty` |
|
|
201
|
+
| `configure set <key> <value>` | One key; full document re-validated after merge |
|
|
202
202
|
|
|
203
|
-
`
|
|
203
|
+
`configure get`/`set` skip required-config exit and TTY prompts. Sensitive values redact on `get` (`REDACTED` / `{ "set": true }` with `--json`).
|
|
204
204
|
|
|
205
|
-
Object/array/`$ref` properties require `--json` on `
|
|
205
|
+
Object/array/`$ref` properties require `--json` on `configure set`.
|
|
206
206
|
|
|
207
207
|
## Example in this repo
|
|
208
208
|
|
|
209
209
|
| Example | Role |
|
|
210
210
|
| --- | --- |
|
|
211
|
-
| [`examples/full-example/`](../examples/full-example/) | **Copy template** — schemagen discovery, `APP_CONFIG_JSON_SCHEMA` bridge, `program.appConfig`, built-in `
|
|
211
|
+
| [`examples/full-example/`](../examples/full-example/) | **Copy template** — schemagen discovery, `APP_CONFIG_JSON_SCHEMA` bridge, `program.appConfig`, built-in `configure get`/`set` |
|
|
212
212
|
|
|
213
213
|
```bash
|
|
214
214
|
cd examples/full-example && just setup && just schemagen
|
|
215
|
-
FULL_EXAMPLE_API_TOKEN=dev just run
|
|
215
|
+
FULL_EXAMPLE_API_TOKEN=dev just run configure get apiToken --json
|
|
216
216
|
```
|
package/docs/configure.md
CHANGED
|
@@ -47,6 +47,10 @@ Dev flow matches release: formula `install` copies the binary and generates comp
|
|
|
47
47
|
|
|
48
48
|
# Remove app config only (not skills/MCP)
|
|
49
49
|
<key> configure --remove-config --yes
|
|
50
|
+
|
|
51
|
+
# Read or write app config (non-interactive; when program.appConfig is set)
|
|
52
|
+
<key> configure get [key] [--json] [--pretty]
|
|
53
|
+
<key> configure set <key> <value> [--json]
|
|
50
54
|
```
|
|
51
55
|
|
|
52
56
|
Non-interactive / CI: pass **`--yes`** (or **`--json`**, **`--sync`**, **`--remove-all`**, **`--remove-config`**) — see [Confirmation](#confirmation).
|
package/docs/mcp.md
CHANGED
|
@@ -327,7 +327,7 @@ At server start (`Cli.serveMcp()`), before the NDJSON loop:
|
|
|
327
327
|
- Loaded at MCP startup; host `process.env` wins for mapped env vars already set.
|
|
328
328
|
- Missing required config does **not** exit the MCP server — enforced at `tools/call` with a helpful error.
|
|
329
329
|
- Configure interactively: `myapp configure` (see [configure.md](configure.md)).
|
|
330
|
-
- Built-in `
|
|
330
|
+
- Built-in `configure get` / `configure set` when `program.appConfig.commands` is enabled (default). Hosts inject `user_config` → env at spawn; they never write the argsbarg config file.
|
|
331
331
|
|
|
332
332
|
Example:
|
|
333
333
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
| Area | Files / wiring |
|
|
8
8
|
| --- | --- |
|
|
9
|
-
| All builtins | `completion`, `version`, `
|
|
9
|
+
| All builtins | `completion`, `version`, `configure`, `docs`, `mcp`, `configure get`/`set` |
|
|
10
10
|
| `program.appConfig` | `src/types.ts` (`AppConfig`) → `schemas/configSchemas.ts` |
|
|
11
11
|
| `outputSchema` | `src/commands/status/types.ts` (`StatusJsonOutput`) → `schemas/outputSchemas.ts` |
|
|
12
12
|
| Schemagen | `scripts/schemagen.ts` + `scripts/schemagen/discover-schema-roots.ts` |
|
|
@@ -47,7 +47,7 @@ cd examples/full-example
|
|
|
47
47
|
just setup
|
|
48
48
|
just schemagen # after changing src/**/types.ts
|
|
49
49
|
FULL_EXAMPLE_API_TOKEN=dev just run status --json
|
|
50
|
-
FULL_EXAMPLE_API_TOKEN=dev just run
|
|
50
|
+
FULL_EXAMPLE_API_TOKEN=dev just run configure get apiToken --json
|
|
51
51
|
FULL_EXAMPLE_API_TOKEN=dev just run docs readme
|
|
52
52
|
```
|
|
53
53
|
|
|
@@ -50,8 +50,5 @@ export const program = {
|
|
|
50
50
|
mcpd: true,
|
|
51
51
|
claudePlugin: true,
|
|
52
52
|
},
|
|
53
|
-
configure: {
|
|
54
|
-
// Defaults: agentIntegration picks skill vs MCP for sync; app config is opt-in in interactive configure.
|
|
55
|
-
},
|
|
56
53
|
commands: [echoCommand, statusCommand],
|
|
57
54
|
} satisfies CliProgram;
|
package/index.d.ts
CHANGED
|
@@ -281,7 +281,7 @@ export interface CliAppConfigEntry {
|
|
|
281
281
|
/** When `false`, optional for bootstrap and MCP enforcement. Default: `true`. */
|
|
282
282
|
required?: boolean;
|
|
283
283
|
/**
|
|
284
|
-
* Mask stdin during prompts and redact on `
|
|
284
|
+
* Mask stdin during prompts and redact on `configure get`.
|
|
285
285
|
* Default: `/key|token|secret|password/i.test(name)`.
|
|
286
286
|
*/
|
|
287
287
|
sensitive?: boolean;
|
|
@@ -297,7 +297,7 @@ export interface CliAppConfigEntry {
|
|
|
297
297
|
* App configuration block on the program root ({@link CliProgram.appConfig}).
|
|
298
298
|
*/
|
|
299
299
|
export interface CliAppConfig {
|
|
300
|
-
/** Built-in `
|
|
300
|
+
/** Built-in `configure get` / `configure set`. Default: enabled when `appConfig` is set. */
|
|
301
301
|
commands?: boolean | {
|
|
302
302
|
enabled?: boolean;
|
|
303
303
|
mcpSet?: boolean;
|
|
@@ -551,11 +551,16 @@ export declare function shouldRunHeadlessWithYes(ctx: HeadlessContext, opts: {
|
|
|
551
551
|
hasRequiredArgs: boolean;
|
|
552
552
|
dryRun?: boolean;
|
|
553
553
|
}, interactive?: boolean): boolean;
|
|
554
|
-
/**
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
554
|
+
/** Exits when non-interactive mode is used without `--yes`. */
|
|
555
|
+
export declare function requireYesInNonTty(
|
|
556
|
+
/** True when `--yes` was passed on the command line. */
|
|
557
|
+
yes: boolean,
|
|
558
|
+
/** Command-specific guidance appended to the error message. */
|
|
559
|
+
hint: string,
|
|
560
|
+
/** When true, skip the check (dry-run preview). */
|
|
561
|
+
dryRun?: boolean,
|
|
562
|
+
/** Injectable TTY probe for tests. */
|
|
563
|
+
interactive?: boolean): void;
|
|
559
564
|
/** Prefixes a success message when running in dry-run mode. */
|
|
560
565
|
export declare function formatDryRunMessage(message: string, dryRun: boolean): string;
|
|
561
566
|
/** Resolved paths for `mcp bundle`. */
|
package/package.json
CHANGED
|
@@ -37,7 +37,6 @@ const noMcp: CliProgram = {
|
|
|
37
37
|
|
|
38
38
|
/** Tests for builtins help copy. */
|
|
39
39
|
describe("builtins help copy", () => {
|
|
40
|
-
/** Configure command includes Homebrew-oriented description. */
|
|
41
40
|
test("configure command includes Homebrew-oriented description", () => {
|
|
42
41
|
const configure = cliBuiltinConfigureCommand(fixture);
|
|
43
42
|
expect(configure.description).toContain("agent skills");
|
|
@@ -51,7 +50,6 @@ describe("builtins help copy", () => {
|
|
|
51
50
|
expect(yesOpt?.shortName).toBe("y");
|
|
52
51
|
});
|
|
53
52
|
|
|
54
|
-
/** Configure copy omits MCP when mcpServer unset. */
|
|
55
53
|
test("configure copy omits MCP when mcpServer unset", () => {
|
|
56
54
|
const caps = resolveCapabilities(noMcp);
|
|
57
55
|
expect(configureCommandDescription(noMcp, caps)).toBe(
|
|
@@ -79,7 +77,6 @@ describe("builtins help copy", () => {
|
|
|
79
77
|
expect(configureBuiltinOptions(withConfig).map((o) => o.name)).toContain("remove-config");
|
|
80
78
|
});
|
|
81
79
|
|
|
82
|
-
/** Configure -y parses as --yes. */
|
|
83
80
|
test("configure -y parses as --yes", () => {
|
|
84
81
|
const root = cliParseRoot(fixture);
|
|
85
82
|
const pr = postParseValidate(root, parse(root, ["configure", "-y", "--sync"]));
|
|
@@ -90,7 +87,6 @@ describe("builtins help copy", () => {
|
|
|
90
87
|
}
|
|
91
88
|
});
|
|
92
89
|
|
|
93
|
-
/** Mcp builtin description is user-facing. */
|
|
94
90
|
test("mcp builtin description is user-facing", () => {
|
|
95
91
|
const withDocs: CliProgram = {
|
|
96
92
|
...fixture,
|
|
@@ -105,7 +101,6 @@ describe("builtins help copy", () => {
|
|
|
105
101
|
|
|
106
102
|
/** Tests for presentation root. */
|
|
107
103
|
describe("presentation root", () => {
|
|
108
|
-
/** Includes mcp and configure when enabled. */
|
|
109
104
|
test("includes mcp and configure when enabled", () => {
|
|
110
105
|
const root = cliPresentationRoot(fixture);
|
|
111
106
|
const keys = root.commands?.map((c) => c.key) ?? [];
|
|
@@ -115,14 +110,12 @@ describe("presentation root", () => {
|
|
|
115
110
|
expect(keys).not.toContain("install");
|
|
116
111
|
});
|
|
117
112
|
|
|
118
|
-
/** Omits configure when configure.enabled is false. */
|
|
119
113
|
test("omits configure when configure.enabled is false", () => {
|
|
120
114
|
const disabled: CliProgram = { ...fixture, configure: { enabled: false } };
|
|
121
115
|
const root = cliPresentationRoot(disabled);
|
|
122
116
|
expect(root.commands?.map((c) => c.key)).not.toContain("configure");
|
|
123
117
|
});
|
|
124
118
|
|
|
125
|
-
/** Includes version builtin. */
|
|
126
119
|
test("includes version builtin", () => {
|
|
127
120
|
const root = cliPresentationRoot(fixture);
|
|
128
121
|
expect(root.commands?.map((c) => c.key)).toContain("version");
|
|
@@ -131,7 +124,6 @@ describe("presentation root", () => {
|
|
|
131
124
|
|
|
132
125
|
/** Tests for completion emitters. */
|
|
133
126
|
describe("completion emitters", () => {
|
|
134
|
-
/** Tests that fish script references app key and subcommands. */
|
|
135
127
|
test("fish script references app key and subcommands", () => {
|
|
136
128
|
const schema = cliPresentationRoot(fixture);
|
|
137
129
|
const fish = completionFishScript(schema);
|
|
@@ -140,7 +132,6 @@ describe("completion emitters", () => {
|
|
|
140
132
|
expect(fish).toContain("configure");
|
|
141
133
|
});
|
|
142
134
|
|
|
143
|
-
/** Tests that bash script includes configure flags. */
|
|
144
135
|
test("bash script includes configure flags", () => {
|
|
145
136
|
const schema = cliPresentationRoot(fixture);
|
|
146
137
|
const bash = completionBashScript(schema);
|
|
@@ -148,7 +139,6 @@ describe("completion emitters", () => {
|
|
|
148
139
|
expect(bash).toContain("--sync");
|
|
149
140
|
});
|
|
150
141
|
|
|
151
|
-
/** Tests that zsh script registers compdef. */
|
|
152
142
|
test("zsh script registers compdef", () => {
|
|
153
143
|
const schema = cliPresentationRoot({
|
|
154
144
|
key: "zapp",
|
|
@@ -164,8 +154,8 @@ describe("completion emitters", () => {
|
|
|
164
154
|
|
|
165
155
|
/** Tests for schema export builtins. */
|
|
166
156
|
describe("schema export builtins", () => {
|
|
167
|
-
/** ExportPresentationBuiltins
|
|
168
|
-
test("exportPresentationBuiltins
|
|
157
|
+
/** ExportPresentationBuiltins nests configure get/set when appConfig set. */
|
|
158
|
+
test("exportPresentationBuiltins nests configure get/set when appConfig set", () => {
|
|
169
159
|
const withConfig: CliProgram = {
|
|
170
160
|
...fixture,
|
|
171
161
|
appConfig: {
|
|
@@ -175,11 +165,16 @@ describe("schema export builtins", () => {
|
|
|
175
165
|
},
|
|
176
166
|
};
|
|
177
167
|
const builtins = exportPresentationBuiltins(withConfig);
|
|
178
|
-
expect(builtins.map((b) => b.key)).toContain("config");
|
|
179
168
|
expect(builtins.map((b) => b.key)).toContain("configure");
|
|
169
|
+
expect(builtins.map((b) => b.key)).not.toContain("config");
|
|
170
|
+
const configureNode = builtins.find((b) => b.key === "configure");
|
|
171
|
+
expect(configureNode && "commands" in configureNode).toBe(true);
|
|
172
|
+
if (configureNode && "commands" in configureNode) {
|
|
173
|
+
const keys = configureNode.commands?.map((c) => c.key) ?? [];
|
|
174
|
+
expect(keys).toEqual(expect.arrayContaining(["get", "set"]));
|
|
175
|
+
}
|
|
180
176
|
});
|
|
181
177
|
|
|
182
|
-
/** ExportPresentationBuiltins omits hidden completion. */
|
|
183
178
|
test("exportPresentationBuiltins omits hidden completion", () => {
|
|
184
179
|
const builtins = exportPresentationBuiltins(fixture);
|
|
185
180
|
expect(builtins.map((b) => b.key)).not.toContain("completion");
|
|
@@ -38,7 +38,7 @@ describe("builtins/config", () => {
|
|
|
38
38
|
const configPath = resolveAppConfigPath(program);
|
|
39
39
|
mkdirSync(dirname(configPath), { recursive: true });
|
|
40
40
|
writeFileSync(configPath, `${JSON.stringify({ apiToken: "secret" })}\n`);
|
|
41
|
-
const result = await new Cli(program).invoke(["
|
|
41
|
+
const result = await new Cli(program).invoke(["configure", "get", "apiToken"]);
|
|
42
42
|
expect(result.exitCode).toBe(0);
|
|
43
43
|
expect(result.stdout.trim()).toBe("REDACTED");
|
|
44
44
|
} finally {
|
|
@@ -61,7 +61,7 @@ describe("builtins/config", () => {
|
|
|
61
61
|
const configPath = resolveAppConfigPath(program);
|
|
62
62
|
mkdirSync(dirname(configPath), { recursive: true });
|
|
63
63
|
writeFileSync(configPath, `${JSON.stringify({ apiToken: "secret" })}\n`);
|
|
64
|
-
const result = await new Cli(program).invoke(["
|
|
64
|
+
const result = await new Cli(program).invoke(["configure", "get", "apiToken", "--json"]);
|
|
65
65
|
expect(result.exitCode).toBe(0);
|
|
66
66
|
expect(JSON.parse(result.stdout)).toEqual({ set: true });
|
|
67
67
|
} finally {
|
|
@@ -82,9 +82,9 @@ describe("builtins/config", () => {
|
|
|
82
82
|
try {
|
|
83
83
|
const program = configFixture();
|
|
84
84
|
writeAppConfigFile(program, { apiToken: "seed" });
|
|
85
|
-
const result = await new Cli(program).invoke(["
|
|
85
|
+
const result = await new Cli(program).invoke(["configure", "set", "port", "9090"]);
|
|
86
86
|
expect(result.exitCode).toBe(0);
|
|
87
|
-
const get = await new Cli(program).invoke(["
|
|
87
|
+
const get = await new Cli(program).invoke(["configure", "get", "port"]);
|
|
88
88
|
expect(get.stdout.trim()).toBe("9090");
|
|
89
89
|
} finally {
|
|
90
90
|
if (prevHome === undefined) delete process.env.HOME;
|
package/src/builtins/config.ts
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Built-in `
|
|
2
|
+
Built-in `configure get` / `configure set` subcommands.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { bootstrapAppConfig } from "../config/bootstrap.ts";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
configCommandsEnabled,
|
|
8
|
+
configEntrySensitive,
|
|
9
|
+
configMcpSetEnabled,
|
|
10
|
+
defaultConfigEntryTitle,
|
|
11
|
+
} from "../config/entry.ts";
|
|
7
12
|
import { writeAppConfigFile } from "../config/file.ts";
|
|
8
13
|
import { captureMappedHostEnv, exportConfigToEnv, resolveAppConfig } from "../config/resolve.ts";
|
|
9
14
|
import { configPropertySchema, effectiveJsonSchema } from "../config/schema.ts";
|
|
10
15
|
import { parseConfigSetValue } from "../config/validate.ts";
|
|
11
|
-
import type { CliLeaf, CliOption, CliProgram
|
|
16
|
+
import type { CliLeaf, CliOption, CliProgram } from "../types.ts";
|
|
12
17
|
import { CliOptionKind } from "../types.ts";
|
|
13
18
|
|
|
14
19
|
const JSON_OPTION: CliOption = {
|
|
@@ -182,16 +187,16 @@ function configSetLeaf(program: CliProgram, mcpSetEnabled: boolean): CliLeaf {
|
|
|
182
187
|
handler: (ctx) => {
|
|
183
188
|
const key = ctx.args[0];
|
|
184
189
|
if (!key) {
|
|
185
|
-
process.stderr.write("
|
|
190
|
+
process.stderr.write("configure set requires a key.\n");
|
|
186
191
|
process.exit(1);
|
|
187
192
|
}
|
|
188
193
|
const raw = ctx.args[1];
|
|
189
194
|
if (raw === undefined || raw.length === 0) {
|
|
190
195
|
if (!ctx.hasFlag("json")) {
|
|
191
|
-
process.stderr.write("
|
|
196
|
+
process.stderr.write("configure set requires a value (or --json).\n");
|
|
192
197
|
process.exit(1);
|
|
193
198
|
}
|
|
194
|
-
process.stderr.write("
|
|
199
|
+
process.stderr.write("configure set requires a value.\n");
|
|
195
200
|
process.exit(1);
|
|
196
201
|
}
|
|
197
202
|
configSetRun(program, key, raw, ctx.hasFlag("json"));
|
|
@@ -199,23 +204,15 @@ function configSetLeaf(program: CliProgram, mcpSetEnabled: boolean): CliLeaf {
|
|
|
199
204
|
};
|
|
200
205
|
}
|
|
201
206
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
207
|
+
/** `configure get` / `configure set` leaves when app config commands are enabled. */
|
|
208
|
+
export function configureConfigSubcommands(
|
|
209
|
+
program: CliProgram,
|
|
210
|
+
mcpSetEnabled = configMcpSetEnabled(program),
|
|
211
|
+
): CliLeaf[] {
|
|
206
212
|
if (!program.appConfig) {
|
|
207
|
-
throw new Error("config
|
|
213
|
+
throw new Error("configure config subcommands require program.appConfig");
|
|
208
214
|
}
|
|
209
|
-
return
|
|
210
|
-
key: "config",
|
|
211
|
-
description: "Read or write app configuration.",
|
|
212
|
-
commands: [configGetLeaf(program), configSetLeaf(program, configMcpSetEnabled(program))],
|
|
213
|
-
};
|
|
215
|
+
return [configGetLeaf(program), configSetLeaf(program, mcpSetEnabled)];
|
|
214
216
|
}
|
|
215
217
|
|
|
216
|
-
export
|
|
217
|
-
if (!configCommandsEnabled(program)) {
|
|
218
|
-
return undefined;
|
|
219
|
-
}
|
|
220
|
-
return cliBuiltinConfigGroup(program);
|
|
221
|
-
}
|
|
218
|
+
export { configCommandsEnabled };
|
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
import { resolveCapabilities } from "../capabilities.ts";
|
|
2
|
-
import {
|
|
2
|
+
import { configCommandsEnabled, configMcpSetEnabled } from "../config/entry.ts";
|
|
3
|
+
import {
|
|
4
|
+
CliFallbackMode,
|
|
5
|
+
type CliLeaf,
|
|
6
|
+
type CliOption,
|
|
7
|
+
CliOptionKind,
|
|
8
|
+
type CliProgram,
|
|
9
|
+
type CliRouter,
|
|
10
|
+
} from "../types.ts";
|
|
11
|
+
import { configureConfigSubcommands } from "./config.ts";
|
|
3
12
|
import {
|
|
4
13
|
configureCommandDescription,
|
|
5
14
|
configureCommandNotes,
|
|
6
15
|
configureSyncOptionDescription,
|
|
7
16
|
} from "./configure-copy.ts";
|
|
8
17
|
|
|
18
|
+
/** Hidden fallback leaf for bare `configure` (interactive / flag modes). */
|
|
19
|
+
export const CONFIGURE_RUN_KEY = "run";
|
|
20
|
+
|
|
9
21
|
/** Configure command options. */
|
|
10
22
|
export function configureBuiltinOptions(root: CliProgram): CliOption[] {
|
|
11
23
|
const caps = resolveCapabilities(root);
|
|
@@ -57,14 +69,33 @@ export function configureBuiltinOptions(root: CliProgram): CliOption[] {
|
|
|
57
69
|
return opts;
|
|
58
70
|
}
|
|
59
71
|
|
|
60
|
-
/**
|
|
61
|
-
export function
|
|
72
|
+
/** True when argv resolved to `configure get` or `configure set`. */
|
|
73
|
+
export function isConfigureConfigPath(path: string[]): boolean {
|
|
74
|
+
return path.length >= 2 && (path[1] === "get" || path[1] === "set");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Builds the `configure` built-in router. */
|
|
78
|
+
export function cliBuiltinConfigureCommand(root: CliProgram): CliRouter {
|
|
62
79
|
const caps = resolveCapabilities(root);
|
|
80
|
+
const run: CliLeaf = {
|
|
81
|
+
key: CONFIGURE_RUN_KEY,
|
|
82
|
+
hidden: true,
|
|
83
|
+
description: "Interactive or flag-driven configure (skills, MCP, app config).",
|
|
84
|
+
handler: () => {},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const commands: CliLeaf[] = [run];
|
|
88
|
+
if (configCommandsEnabled(root)) {
|
|
89
|
+
commands.push(...configureConfigSubcommands(root, configMcpSetEnabled(root)));
|
|
90
|
+
}
|
|
91
|
+
|
|
63
92
|
return {
|
|
64
93
|
key: "configure",
|
|
65
94
|
description: configureCommandDescription(root, caps),
|
|
66
95
|
options: configureBuiltinOptions(root),
|
|
67
96
|
notes: configureCommandNotes(root, caps),
|
|
68
|
-
|
|
97
|
+
fallbackCommand: CONFIGURE_RUN_KEY,
|
|
98
|
+
fallbackMode: CliFallbackMode.MissingOnly,
|
|
99
|
+
commands,
|
|
69
100
|
};
|
|
70
101
|
}
|
package/src/builtins/dispatch.ts
CHANGED
|
@@ -11,8 +11,11 @@ import { completionBashScript } from "./completion-bash.ts";
|
|
|
11
11
|
import { completionFishScript } from "./completion-fish.ts";
|
|
12
12
|
import { cliBuiltinCompletionGroup as completionGroup } from "./completion-group.ts";
|
|
13
13
|
import { completionZshScript } from "./completion-zsh.ts";
|
|
14
|
-
import {
|
|
15
|
-
|
|
14
|
+
import {
|
|
15
|
+
CONFIGURE_RUN_KEY,
|
|
16
|
+
cliBuiltinConfigureCommand,
|
|
17
|
+
isConfigureConfigPath,
|
|
18
|
+
} from "./configure.ts";
|
|
16
19
|
import { cliBuiltinMcpCommand } from "./mcp.ts";
|
|
17
20
|
import { cliPresentationRoot } from "./presentation.ts";
|
|
18
21
|
import { cliBuiltinVersionCommand } from "./version.ts";
|
|
@@ -101,7 +104,11 @@ export async function dispatchBuiltin(
|
|
|
101
104
|
process.stderr.write(capabilityDeniedMessage("configure"));
|
|
102
105
|
process.exit(1);
|
|
103
106
|
}
|
|
104
|
-
if (pr.path
|
|
107
|
+
if (isConfigureConfigPath(pr.path)) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const runSeg = pr.path[1];
|
|
111
|
+
if (pr.path.length > 2 || (pr.path.length === 2 && runSeg !== CONFIGURE_RUN_KEY)) {
|
|
105
112
|
process.stderr.write(`Unknown subcommand: configure ${pr.path.slice(1).join(" ")}\n`);
|
|
106
113
|
process.exit(1);
|
|
107
114
|
}
|
|
@@ -177,17 +184,5 @@ export function builtinInterceptRoot(
|
|
|
177
184
|
};
|
|
178
185
|
}
|
|
179
186
|
|
|
180
|
-
const configGroup = cliBuiltinConfigGroupIfEnabled(program);
|
|
181
|
-
if (first === "config" && configGroup) {
|
|
182
|
-
return {
|
|
183
|
-
parseRoot: {
|
|
184
|
-
key: program.key,
|
|
185
|
-
description: program.description,
|
|
186
|
-
commands: [configGroup],
|
|
187
|
-
},
|
|
188
|
-
isLeafCompletionIntercept: false,
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
|
|
192
187
|
return { parseRoot: program, isLeafCompletionIntercept: false };
|
|
193
188
|
}
|
package/src/builtins/registry.ts
CHANGED
|
@@ -2,7 +2,6 @@ import type { CliCapabilities } from "../capabilities.ts";
|
|
|
2
2
|
import { cliBuiltinDocsGroupIfEnabled } from "../docs/builtin.ts";
|
|
3
3
|
import type { CliNode, CliProgram } from "../types.ts";
|
|
4
4
|
import { cliBuiltinCompletionGroup } from "./completion-group.ts";
|
|
5
|
-
import { cliBuiltinConfigGroupIfEnabled } from "./config.ts";
|
|
6
5
|
import { cliBuiltinConfigureCommand } from "./configure.ts";
|
|
7
6
|
import { cliBuiltinMcpCommand } from "./mcp.ts";
|
|
8
7
|
import { cliBuiltinVersionCommand } from "./version.ts";
|
|
@@ -37,6 +36,5 @@ export function resolveBuiltins(program: CliProgram, caps: CliCapabilities): Cli
|
|
|
37
36
|
if (caps.mcp) {
|
|
38
37
|
pushBuiltin(builtins, program, (p) => cliBuiltinMcpCommand(p));
|
|
39
38
|
}
|
|
40
|
-
pushBuiltin(builtins, program, (p) => cliBuiltinConfigGroupIfEnabled(p) ?? null);
|
|
41
39
|
return builtins;
|
|
42
40
|
}
|
package/src/capabilities.ts
CHANGED
|
@@ -42,17 +42,17 @@ export function reservedCommandNames(caps: CliCapabilities): string[] {
|
|
|
42
42
|
if (caps.mcp) {
|
|
43
43
|
names.push("mcp");
|
|
44
44
|
}
|
|
45
|
-
if (caps.configCommands) {
|
|
46
|
-
names.push("config");
|
|
47
|
-
}
|
|
48
45
|
return names;
|
|
49
46
|
}
|
|
50
47
|
|
|
51
48
|
/** Commands that may run without required appConfig values (read-only / config introspection). */
|
|
52
49
|
export function skipsRequiredAppConfigExit(path: string[], caps: CliCapabilities): boolean {
|
|
53
50
|
const root = path[0];
|
|
54
|
-
if (root === "
|
|
55
|
-
|
|
51
|
+
if (root === "configure" && caps.configCommands) {
|
|
52
|
+
const sub = path[1];
|
|
53
|
+
if (sub === "get" || sub === "set") {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
56
|
}
|
|
57
57
|
if (root === "docs" && caps.docs) {
|
|
58
58
|
return true;
|
|
@@ -60,7 +60,7 @@ export function skipsRequiredAppConfigExit(path: string[], caps: CliCapabilities
|
|
|
60
60
|
return false;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
export type CapabilityFeature = "mcp" | "configure" | "docs" | "
|
|
63
|
+
export type CapabilityFeature = "mcp" | "configure" | "docs" | "completion";
|
|
64
64
|
|
|
65
65
|
/** Stderr message when a disabled built-in is invoked from the CLI. */
|
|
66
66
|
export function capabilityDeniedMessage(feature: CapabilityFeature): string {
|
|
@@ -73,8 +73,6 @@ export function capabilityDeniedMessage(feature: CapabilityFeature): string {
|
|
|
73
73
|
return "Configure is not available for this app.\n";
|
|
74
74
|
case "docs":
|
|
75
75
|
return "Documentation commands are not available for this app.\n";
|
|
76
|
-
case "config":
|
|
77
|
-
return "Configuration commands are not available for this app.\n";
|
|
78
76
|
}
|
|
79
77
|
}
|
|
80
78
|
|
|
@@ -100,8 +98,4 @@ export function assertBuiltinAllowed(argv: string[], caps: CliCapabilities): voi
|
|
|
100
98
|
process.stderr.write(capabilityDeniedMessage("docs"));
|
|
101
99
|
process.exit(1);
|
|
102
100
|
}
|
|
103
|
-
if (first === "config" && !caps.configCommands) {
|
|
104
|
-
process.stderr.write(capabilityDeniedMessage("config"));
|
|
105
|
-
process.exit(1);
|
|
106
|
-
}
|
|
107
101
|
}
|
|
@@ -10,14 +10,12 @@ const main = join(import.meta.dir, "main.ts");
|
|
|
10
10
|
|
|
11
11
|
/** Tests for argsbarg cli-tool. */
|
|
12
12
|
describe("argsbarg cli-tool", () => {
|
|
13
|
-
/** Tests that version subcommand prints version. */
|
|
14
13
|
test("version subcommand prints version", () => {
|
|
15
14
|
const proc = spawnSync("bun", [main, "version"], { encoding: "utf8" });
|
|
16
15
|
expect(proc.status).toBe(0);
|
|
17
16
|
expect(proc.stdout.trim().length).toBeGreaterThan(0);
|
|
18
17
|
});
|
|
19
18
|
|
|
20
|
-
/** Tests that help lists create and version only (no install, completion, mcp). */
|
|
21
19
|
test("help lists create and version only (no install, completion, mcp)", () => {
|
|
22
20
|
const proc = spawnSync("bun", [main, "--help"], { encoding: "utf8" });
|
|
23
21
|
expect(proc.status).toBe(0);
|
|
@@ -28,7 +26,6 @@ describe("argsbarg cli-tool", () => {
|
|
|
28
26
|
expect(proc.stdout).not.toContain("mcp");
|
|
29
27
|
});
|
|
30
28
|
|
|
31
|
-
/** Completion subcommand is disabled. */
|
|
32
29
|
test("completion subcommand is disabled", () => {
|
|
33
30
|
const proc = spawnSync("bun", [main, "completion", "bash"], { encoding: "utf8" });
|
|
34
31
|
expect(proc.status).toBe(1);
|
|
@@ -44,7 +44,6 @@ describe("argsbarg create", () => {
|
|
|
44
44
|
expect(out).not.toContain("{key}");
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
-
/** Tests that classNameFromKey. */
|
|
48
47
|
test("classNameFromKey", () => {
|
|
49
48
|
expect(classNameFromKey("sqsp-i18n")).toBe("SqspI18n");
|
|
50
49
|
expect(classNameFromKey("at1")).toBe("At1");
|
|
@@ -67,7 +66,6 @@ describe("argsbarg create", () => {
|
|
|
67
66
|
expect(opts.desc).toBe("At1 CLI");
|
|
68
67
|
});
|
|
69
68
|
|
|
70
|
-
/** ResolveCreateOptions requires release repo. */
|
|
71
69
|
test("resolveCreateOptions requires release repo", () => {
|
|
72
70
|
expect(() => resolveCreateOptions({ key: "at1" })).toThrow(/release repo/i);
|
|
73
71
|
});
|
|
@@ -146,7 +144,6 @@ describe("argsbarg create", () => {
|
|
|
146
144
|
}
|
|
147
145
|
});
|
|
148
146
|
|
|
149
|
-
/** Tests that --diff captures drift details. */
|
|
150
147
|
test("--diff captures drift details", () => {
|
|
151
148
|
const drifts = diffCreateDetails("/nonexistent", { key: "x", releaseRepo: "org/x" });
|
|
152
149
|
expect(drifts.length).toBeGreaterThan(0);
|