argsbarg 5.0.3 → 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 +8 -1
- 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 +2 -2
- package/package.json +1 -1
- package/src/builtins/builtins.test.ts +9 -3
- 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/config/entry.ts +2 -2
- package/src/config/resolve.ts +2 -2
- package/src/docs/docs.test.ts +1 -1
- package/src/schema.ts +1 -1
- package/src/types.ts +2 -2
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
|
+
## [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
|
+
|
|
10
16
|
## [5.0.3] - 2026-07-04
|
|
11
17
|
|
|
12
18
|
### Changed
|
|
@@ -579,7 +585,8 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
|
|
|
579
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`).
|
|
580
586
|
- Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
|
|
581
587
|
|
|
582
|
-
[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
|
|
583
590
|
[5.0.3]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.0.3
|
|
584
591
|
[5.0.2]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.0.2
|
|
585
592
|
[5.0.1]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.0.1
|
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;
|
package/package.json
CHANGED
|
@@ -154,8 +154,8 @@ describe("completion emitters", () => {
|
|
|
154
154
|
|
|
155
155
|
/** Tests for schema export builtins. */
|
|
156
156
|
describe("schema export builtins", () => {
|
|
157
|
-
/** ExportPresentationBuiltins
|
|
158
|
-
test("exportPresentationBuiltins
|
|
157
|
+
/** ExportPresentationBuiltins nests configure get/set when appConfig set. */
|
|
158
|
+
test("exportPresentationBuiltins nests configure get/set when appConfig set", () => {
|
|
159
159
|
const withConfig: CliProgram = {
|
|
160
160
|
...fixture,
|
|
161
161
|
appConfig: {
|
|
@@ -165,8 +165,14 @@ describe("schema export builtins", () => {
|
|
|
165
165
|
},
|
|
166
166
|
};
|
|
167
167
|
const builtins = exportPresentationBuiltins(withConfig);
|
|
168
|
-
expect(builtins.map((b) => b.key)).toContain("config");
|
|
169
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
|
+
}
|
|
170
176
|
});
|
|
171
177
|
|
|
172
178
|
test("exportPresentationBuiltins omits hidden 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
|
}
|
package/src/config/entry.ts
CHANGED
|
@@ -29,7 +29,7 @@ export function configEntryRequired(
|
|
|
29
29
|
return true;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
/** Whether prompts and `
|
|
32
|
+
/** Whether prompts and `configure get` should redact this entry. */
|
|
33
33
|
export function configEntrySensitive(key: string, entry: CliAppConfigEntry): boolean {
|
|
34
34
|
return entry.sensitive ?? defaultConfigEntrySensitive(key);
|
|
35
35
|
}
|
|
@@ -56,7 +56,7 @@ export function jsonSchemaRequiredKeys(
|
|
|
56
56
|
return new Set(required.filter((k): k is string => typeof k === "string"));
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
/** Whether built-in
|
|
59
|
+
/** Whether built-in `configure get` / `configure set` subcommands are enabled. */
|
|
60
60
|
export function configCommandsEnabled(program: CliProgram): boolean {
|
|
61
61
|
if (!program.appConfig) {
|
|
62
62
|
return false;
|
package/src/config/resolve.ts
CHANGED
|
@@ -227,7 +227,7 @@ export function formatMissingConfigMessage(program: CliProgram, keys: string[]):
|
|
|
227
227
|
return [
|
|
228
228
|
`Missing required configuration: ${list}`,
|
|
229
229
|
`Configure interactively: ${program.key} configure`,
|
|
230
|
-
`Or set via: ${program.key}
|
|
230
|
+
`Or set via: ${program.key} configure set <key> <value>`,
|
|
231
231
|
`Config file: ${path}`,
|
|
232
232
|
`See: ${program.key} docs mcp`,
|
|
233
233
|
].join("\n");
|
|
@@ -240,7 +240,7 @@ export function formatMcpMissingConfigMessage(program: CliProgram, keys: string[
|
|
|
240
240
|
return [
|
|
241
241
|
`Missing required configuration: ${list}`,
|
|
242
242
|
`Configure: ${program.key} configure`,
|
|
243
|
-
`Or set via: ${program.key}
|
|
243
|
+
`Or set via: ${program.key} configure set`,
|
|
244
244
|
`Config file: ${path}`,
|
|
245
245
|
].join("\n");
|
|
246
246
|
}
|
package/src/docs/docs.test.ts
CHANGED
|
@@ -167,7 +167,7 @@ test("skipsRequiredAppConfigExit includes docs and config builtins", () => {
|
|
|
167
167
|
};
|
|
168
168
|
const caps = resolveCapabilities(program);
|
|
169
169
|
expect(skipsRequiredAppConfigExit(["docs", "api"], caps)).toBe(true);
|
|
170
|
-
expect(skipsRequiredAppConfigExit(["
|
|
170
|
+
expect(skipsRequiredAppConfigExit(["configure", "get"], caps)).toBe(true);
|
|
171
171
|
expect(skipsRequiredAppConfigExit(["run"], caps)).toBe(false);
|
|
172
172
|
});
|
|
173
173
|
|
package/src/schema.ts
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
leafOutputSchema,
|
|
14
14
|
} from "./types.ts";
|
|
15
15
|
|
|
16
|
-
const RESERVED = new Set(["completion", "configure", "docs", "mcp", "version"
|
|
16
|
+
const RESERVED = new Set(["completion", "configure", "docs", "mcp", "version"]);
|
|
17
17
|
|
|
18
18
|
function exportCommand(cmd: CliNode, root: CliProgram): CliSchemaExport | null {
|
|
19
19
|
if (cmd.hidden) {
|
package/src/types.ts
CHANGED
|
@@ -236,7 +236,7 @@ export interface CliAppConfigEntry {
|
|
|
236
236
|
/** When `false`, optional for bootstrap and MCP enforcement. Default: `true`. */
|
|
237
237
|
required?: boolean;
|
|
238
238
|
/**
|
|
239
|
-
* Mask stdin during prompts and redact on `
|
|
239
|
+
* Mask stdin during prompts and redact on `configure get`.
|
|
240
240
|
* Default: `/key|token|secret|password/i.test(name)`.
|
|
241
241
|
*/
|
|
242
242
|
sensitive?: boolean;
|
|
@@ -253,7 +253,7 @@ export interface CliAppConfigEntry {
|
|
|
253
253
|
* App configuration block on the program root ({@link CliProgram.appConfig}).
|
|
254
254
|
*/
|
|
255
255
|
export interface CliAppConfig {
|
|
256
|
-
/** Built-in `
|
|
256
|
+
/** Built-in `configure get` / `configure set`. Default: enabled when `appConfig` is set. */
|
|
257
257
|
commands?: boolean | { enabled?: boolean; mcpSet?: boolean };
|
|
258
258
|
/** Block JSON Schema (draft-07). When omitted, synthesize all-string schema from `entries`. */
|
|
259
259
|
jsonSchema?: Record<string, unknown>;
|