argsbarg 3.6.4 → 4.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/CHANGELOG.md +32 -2
- package/README.md +21 -9
- package/docs/README.md +12 -8
- package/docs/bundled-docs.md +1 -1
- package/docs/cli-program.md +62 -2
- package/docs/config-schema.md +192 -0
- package/docs/developing.md +13 -0
- package/docs/install.md +38 -1
- package/docs/mcp.md +43 -19
- package/docs/output-schema.md +74 -52
- package/docs/templates/cursor/rules/cli-program.mdc +10 -5
- package/examples/config-app/main.ts +20 -0
- package/examples/config-app/program.ts +81 -0
- package/examples/config-app/schema.ts +37 -0
- package/examples/config-app/types.ts +19 -0
- package/examples/consumer-app/README.md +56 -0
- package/examples/consumer-app/bun.lock +75 -0
- package/examples/consumer-app/capabilities.test.ts +69 -0
- package/examples/consumer-app/package.json +17 -0
- package/examples/consumer-app/schemas/configSchemas.ts +6 -0
- package/examples/consumer-app/schemas/generated/app-config.json +40 -0
- package/examples/consumer-app/schemas/generated/status.json +28 -0
- package/examples/consumer-app/schemas/outputSchemas.ts +6 -0
- package/examples/consumer-app/scripts/schemagen/discover-schema-roots.test.ts +25 -0
- package/examples/consumer-app/scripts/schemagen/discover-schema-roots.ts +93 -0
- package/examples/consumer-app/scripts/schemagen/naming.ts +82 -0
- package/examples/consumer-app/scripts/schemagen.ts +76 -0
- package/examples/consumer-app/src/commands/status/types.ts +11 -0
- package/examples/consumer-app/src/main.ts +15 -0
- package/examples/consumer-app/src/program.ts +116 -0
- package/examples/consumer-app/src/types.ts +23 -0
- package/examples/consumer-app/tsconfig.json +14 -0
- package/examples/formats.ts +10 -3
- package/examples/mcp-test.ts +27 -8
- package/examples/minimal.ts +4 -3
- package/examples/nested.ts +5 -4
- package/examples/option-required.ts +8 -4
- package/index.d.ts +158 -75
- package/package.json +1 -1
- package/src/builtins/builtins.test.ts +13 -0
- package/src/builtins/config.test.ts +82 -0
- package/src/builtins/config.ts +220 -0
- package/src/builtins/dispatch.ts +18 -9
- package/src/builtins/export.ts +8 -33
- package/src/builtins/index.ts +1 -0
- package/src/builtins/install.ts +13 -0
- package/src/builtins/mcp.ts +1 -1
- package/src/builtins/presentation.ts +2 -17
- package/src/builtins/registry.ts +40 -0
- package/src/capabilities.ts +46 -0
- package/src/cli-errors.ts +15 -0
- package/src/cli.ts +389 -0
- package/src/config/bootstrap.ts +265 -0
- package/src/config/context.test.ts +79 -0
- package/src/config/context.ts +110 -0
- package/src/config/entry.ts +81 -0
- package/src/config/file.test.ts +112 -0
- package/src/config/file.ts +120 -0
- package/src/config/manifest.ts +62 -0
- package/src/config/resolve.test.ts +88 -0
- package/src/config/resolve.ts +167 -0
- package/src/config/schema.ts +101 -0
- package/src/config/validate.test.ts +63 -0
- package/src/config/validate.ts +292 -0
- package/src/config.integration.test.ts +100 -0
- package/src/context.ts +5 -0
- package/src/docs/docs.test.ts +16 -16
- package/src/docs/mcp-guide.ts +35 -11
- package/src/hidden-mcpb.test.ts +40 -2
- package/src/index.ts +4 -3
- package/src/install/index.ts +46 -3
- package/src/install/paths.ts +5 -20
- package/src/install/plan.ts +6 -0
- package/src/install/status.ts +12 -0
- package/src/install/uninstall.ts +11 -0
- package/src/install/update.test.ts +5 -5
- package/src/invoke.test.ts +207 -0
- package/src/mcp/bundle.ts +9 -116
- package/src/mcp/claude.test.ts +73 -0
- package/src/mcp/claude.ts +168 -0
- package/src/mcp/env.ts +3 -37
- package/src/mcp/server.ts +18 -10
- package/src/mcp/tools.ts +3 -7
- package/src/mcp/zip.ts +82 -0
- package/src/mcp.integration.test.ts +502 -0
- package/src/{index.test.ts → parse.test.ts} +24 -935
- package/src/paths/host.ts +40 -0
- package/src/schema.ts +1 -1
- package/src/skill/generate.ts +18 -5
- package/src/skill/hint.ts +18 -0
- package/src/skill/install.ts +1 -5
- package/src/test-fixtures.ts +192 -0
- package/src/types.ts +39 -13
- package/src/validate.ts +70 -0
- package/src/completion.ts +0 -13
- package/src/invoke.ts +0 -217
- package/src/mcp.ts +0 -28
- package/src/runtime.ts +0 -134
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Built-in `config get` / `config set` commands.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { bootstrapAppConfig } from "../config/bootstrap.ts";
|
|
6
|
+
import { configEntrySensitive, defaultConfigEntryTitle } from "../config/entry.ts";
|
|
7
|
+
import { writeAppConfigFile } from "../config/file.ts";
|
|
8
|
+
import { exportConfigToEnv, resolveAppConfig } from "../config/resolve.ts";
|
|
9
|
+
import { configPropertySchema, effectiveJsonSchema } from "../config/schema.ts";
|
|
10
|
+
import { parseConfigSetValue } from "../config/validate.ts";
|
|
11
|
+
import type { CliLeaf, CliOption, CliProgram, CliRouter } from "../types.ts";
|
|
12
|
+
import { CliOptionKind } from "../types.ts";
|
|
13
|
+
|
|
14
|
+
const JSON_OPTION: CliOption = {
|
|
15
|
+
name: "json",
|
|
16
|
+
description: "Emit JSON (compact).",
|
|
17
|
+
kind: CliOptionKind.Presence,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const PRETTY_OPTION: CliOption = {
|
|
21
|
+
name: "pretty",
|
|
22
|
+
description: "Pretty-print JSON (requires --json).",
|
|
23
|
+
kind: CliOptionKind.Presence,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function configGetOutput(
|
|
27
|
+
program: CliProgram,
|
|
28
|
+
key: string | undefined,
|
|
29
|
+
json: boolean,
|
|
30
|
+
pretty: boolean,
|
|
31
|
+
): void {
|
|
32
|
+
const appConfig = program.appConfig;
|
|
33
|
+
if (!appConfig) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const { resolved } = bootstrapAppConfig(program, { validateFile: true });
|
|
37
|
+
const entries = appConfig.entries;
|
|
38
|
+
|
|
39
|
+
if (key !== undefined) {
|
|
40
|
+
if (!(key in entries)) {
|
|
41
|
+
process.stderr.write(`Unknown configuration key: ${key}\n`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
const entry = entries[key];
|
|
45
|
+
if (!entry) {
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
const value = resolved[key];
|
|
49
|
+
if (json) {
|
|
50
|
+
const out =
|
|
51
|
+
configEntrySensitive(key, entry) && value !== undefined && String(value).length > 0
|
|
52
|
+
? { set: true }
|
|
53
|
+
: (value ?? null);
|
|
54
|
+
const space = pretty ? 2 : undefined;
|
|
55
|
+
process.stdout.write(`${JSON.stringify(out, null, space)}\n`);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (configEntrySensitive(key, entry)) {
|
|
59
|
+
const set = value !== undefined && value !== null && String(value).length > 0;
|
|
60
|
+
process.stdout.write(set ? "REDACTED\n" : "(not set)\n");
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (value === undefined) {
|
|
64
|
+
process.stdout.write("(not set)\n");
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (typeof value === "object") {
|
|
68
|
+
process.stdout.write(`${JSON.stringify(value)}\n`);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
process.stdout.write(`${String(value)}\n`);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const out: Record<string, unknown> = {};
|
|
76
|
+
for (const [k, entry] of Object.entries(entries)) {
|
|
77
|
+
const value = resolved[k];
|
|
78
|
+
if (configEntrySensitive(k, entry)) {
|
|
79
|
+
out[k] =
|
|
80
|
+
value !== undefined && value !== null && String(value).length > 0
|
|
81
|
+
? json
|
|
82
|
+
? { set: true }
|
|
83
|
+
: "REDACTED"
|
|
84
|
+
: json
|
|
85
|
+
? null
|
|
86
|
+
: "(not set)";
|
|
87
|
+
} else {
|
|
88
|
+
out[k] = value ?? (json ? null : "(not set)");
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (json) {
|
|
93
|
+
const space = pretty ? 2 : undefined;
|
|
94
|
+
process.stdout.write(`${JSON.stringify(out, null, space)}\n`);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
for (const [k, v] of Object.entries(out)) {
|
|
99
|
+
const title = entries[k]?.title ?? defaultConfigEntryTitle(k);
|
|
100
|
+
process.stdout.write(`${title}: ${String(v)}\n`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function configSetRun(program: CliProgram, key: string, rawValue: string, useJson: boolean): void {
|
|
105
|
+
const appConfig = program.appConfig;
|
|
106
|
+
if (!appConfig) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const entries = appConfig.entries;
|
|
110
|
+
if (!(key in entries)) {
|
|
111
|
+
process.stderr.write(`Unknown configuration key: ${key}\n`);
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const jsonSchema = effectiveJsonSchema(program);
|
|
116
|
+
if (!jsonSchema) {
|
|
117
|
+
process.stderr.write("Internal error: missing effective jsonSchema.\n");
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const propSchema = configPropertySchema(jsonSchema, key);
|
|
122
|
+
let parsed: unknown;
|
|
123
|
+
try {
|
|
124
|
+
parsed = parseConfigSetValue(rawValue, propSchema, jsonSchema, useJson);
|
|
125
|
+
} catch (err) {
|
|
126
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
127
|
+
process.stderr.write(`${msg}\n`);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const { fileData } = bootstrapAppConfig(program, { validateFile: true });
|
|
132
|
+
const next = { ...fileData, [key]: parsed };
|
|
133
|
+
writeAppConfigFile(program, next);
|
|
134
|
+
const resolved = resolveAppConfig(program, next);
|
|
135
|
+
exportConfigToEnv(program, resolved);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function configGetLeaf(program: CliProgram): CliLeaf {
|
|
139
|
+
return {
|
|
140
|
+
key: "get",
|
|
141
|
+
description: "Print resolved configuration value(s).",
|
|
142
|
+
options: [JSON_OPTION, PRETTY_OPTION],
|
|
143
|
+
positionals: [
|
|
144
|
+
{
|
|
145
|
+
name: "key",
|
|
146
|
+
description: "Schema key to read (omit for all keys).",
|
|
147
|
+
kind: CliOptionKind.String,
|
|
148
|
+
argMin: 0,
|
|
149
|
+
argMax: 1,
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
handler: (ctx) => {
|
|
153
|
+
const key = ctx.args[0];
|
|
154
|
+
configGetOutput(program, key, ctx.hasFlag("json"), ctx.hasFlag("pretty"));
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function configSetLeaf(program: CliProgram, mcpSetEnabled: boolean): CliLeaf {
|
|
160
|
+
return {
|
|
161
|
+
key: "set",
|
|
162
|
+
description: "Write one configuration key to the config file.",
|
|
163
|
+
options: [JSON_OPTION],
|
|
164
|
+
mcpTool: mcpSetEnabled ? undefined : { enabled: false },
|
|
165
|
+
positionals: [
|
|
166
|
+
{
|
|
167
|
+
name: "key",
|
|
168
|
+
description: "Schema key to write.",
|
|
169
|
+
kind: CliOptionKind.String,
|
|
170
|
+
argMin: 1,
|
|
171
|
+
argMax: 1,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: "value",
|
|
175
|
+
description: "Value to store (use --json for objects/arrays).",
|
|
176
|
+
kind: CliOptionKind.String,
|
|
177
|
+
argMin: 0,
|
|
178
|
+
argMax: 1,
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
handler: (ctx) => {
|
|
182
|
+
const key = ctx.args[0];
|
|
183
|
+
if (!key) {
|
|
184
|
+
process.stderr.write("config set requires a key.\n");
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}
|
|
187
|
+
const raw = ctx.args[1];
|
|
188
|
+
if (raw === undefined || raw.length === 0) {
|
|
189
|
+
if (!ctx.hasFlag("json")) {
|
|
190
|
+
process.stderr.write("config set requires a value (or --json).\n");
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
process.stderr.write("config set requires a value.\n");
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
configSetRun(program, key, raw, ctx.hasFlag("json"));
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
import { configCommandsEnabled, configMcpSetEnabled } from "../config/entry.ts";
|
|
202
|
+
|
|
203
|
+
/** Built-in `config` router when program.appConfig is set. */
|
|
204
|
+
export function cliBuiltinConfigGroup(program: CliProgram): CliRouter {
|
|
205
|
+
if (!program.appConfig) {
|
|
206
|
+
throw new Error("config not enabled");
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
key: "config",
|
|
210
|
+
description: "Read or write app configuration.",
|
|
211
|
+
commands: [configGetLeaf(program), configSetLeaf(program, configMcpSetEnabled(program))],
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function cliBuiltinConfigGroupIfEnabled(program: CliProgram): CliRouter | undefined {
|
|
216
|
+
if (!configCommandsEnabled(program)) {
|
|
217
|
+
return undefined;
|
|
218
|
+
}
|
|
219
|
+
return cliBuiltinConfigGroup(program);
|
|
220
|
+
}
|
package/src/builtins/dispatch.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { resolveCapabilities } from "../capabilities.ts";
|
|
1
|
+
import { capabilityDeniedMessage, resolveCapabilities } from "../capabilities.ts";
|
|
2
|
+
import { Cli } from "../cli.ts";
|
|
2
3
|
import { cliBuiltinDocsGroupIfEnabled } from "../docs/builtin.ts";
|
|
3
4
|
import { cliInstall } from "../install/index.ts";
|
|
4
5
|
import { runMcpBundle } from "../mcp/bundle.ts";
|
|
5
|
-
import { cliMcpServeStdio } from "../mcp.ts";
|
|
6
6
|
import type { ParseResult } from "../parse.ts";
|
|
7
7
|
import { ParseKind } from "../parse.ts";
|
|
8
8
|
import type { CliNode, CliProgram, CliRouter } from "../types.ts";
|
|
@@ -11,6 +11,7 @@ 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 { cliBuiltinConfigGroupIfEnabled } from "./config.ts";
|
|
14
15
|
import { cliBuiltinInstallCommand } from "./install.ts";
|
|
15
16
|
import { cliBuiltinMcpCommand } from "./mcp.ts";
|
|
16
17
|
import { cliPresentationRoot } from "./presentation.ts";
|
|
@@ -70,14 +71,12 @@ export async function dispatchBuiltin(
|
|
|
70
71
|
|
|
71
72
|
if (pr.path[0] === "mcp") {
|
|
72
73
|
if (!caps.mcp) {
|
|
73
|
-
process.stderr.write(
|
|
74
|
-
"MCP is not enabled. Set mcpServer: { enabled: true } on the program root.\n",
|
|
75
|
-
);
|
|
74
|
+
process.stderr.write(capabilityDeniedMessage("mcp"));
|
|
76
75
|
process.exit(1);
|
|
77
76
|
}
|
|
78
77
|
const sub = pr.path[1];
|
|
79
78
|
if (pr.path.length === 1 || sub === "serve") {
|
|
80
|
-
await
|
|
79
|
+
await new Cli(program).serveMcp();
|
|
81
80
|
process.exit(0);
|
|
82
81
|
}
|
|
83
82
|
if (pr.path.length === 2 && sub === "bundle") {
|
|
@@ -95,9 +94,7 @@ export async function dispatchBuiltin(
|
|
|
95
94
|
|
|
96
95
|
if (pr.path[0] === "install") {
|
|
97
96
|
if (!caps.install) {
|
|
98
|
-
process.stderr.write(
|
|
99
|
-
"install is disabled. Remove install.enabled: false from the program root.\n",
|
|
100
|
-
);
|
|
97
|
+
process.stderr.write(capabilityDeniedMessage("install"));
|
|
101
98
|
process.exit(1);
|
|
102
99
|
}
|
|
103
100
|
if (pr.path.length !== 1) {
|
|
@@ -176,5 +173,17 @@ export function builtinInterceptRoot(
|
|
|
176
173
|
};
|
|
177
174
|
}
|
|
178
175
|
|
|
176
|
+
const configGroup = cliBuiltinConfigGroupIfEnabled(program);
|
|
177
|
+
if (first === "config" && configGroup) {
|
|
178
|
+
return {
|
|
179
|
+
parseRoot: {
|
|
180
|
+
key: program.key,
|
|
181
|
+
description: program.description,
|
|
182
|
+
commands: [configGroup],
|
|
183
|
+
},
|
|
184
|
+
isLeafCompletionIntercept: false,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
179
188
|
return { parseRoot: program, isLeafCompletionIntercept: false };
|
|
180
189
|
}
|
package/src/builtins/export.ts
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { cliBuiltinDocsGroupIfEnabled } from "../docs/builtin.ts";
|
|
1
|
+
import { resolveCapabilities } from "../capabilities.ts";
|
|
3
2
|
import { visibleOptions } from "../hidden.ts";
|
|
4
3
|
import type { CliFallbackMode, CliNode, CliOption, CliPositional, CliProgram } from "../types.ts";
|
|
5
4
|
import { isCliRouter } from "../types.ts";
|
|
6
|
-
import {
|
|
7
|
-
import { cliBuiltinInstallCommand } from "./install.ts";
|
|
8
|
-
import { cliBuiltinMcpCommand } from "./mcp.ts";
|
|
9
|
-
import { cliBuiltinVersionCommand } from "./version.ts";
|
|
5
|
+
import { resolveBuiltins } from "./registry.ts";
|
|
10
6
|
|
|
11
7
|
/** JSON-safe command node (no handlers). */
|
|
12
8
|
export interface CliSchemaExport {
|
|
13
9
|
key: string;
|
|
14
10
|
description: string;
|
|
15
11
|
notes?: string;
|
|
16
|
-
/** JSON Schema for structured stdout when set on the leaf
|
|
12
|
+
/** JSON Schema for structured stdout when set on the leaf. */
|
|
17
13
|
outputSchema?: Record<string, unknown>;
|
|
18
14
|
options?: CliOption[];
|
|
19
15
|
fallbackCommand?: string;
|
|
@@ -55,31 +51,10 @@ function exportBuiltinNode(cmd: CliNode): CliSchemaExport | null {
|
|
|
55
51
|
return out;
|
|
56
52
|
}
|
|
57
53
|
|
|
58
|
-
function pushExportedBuiltin(builtins: CliSchemaExport[], cmd: CliNode): void {
|
|
59
|
-
const node = exportBuiltinNode(cmd);
|
|
60
|
-
if (node) {
|
|
61
|
-
builtins.push(node);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
54
|
/** Built-in subtrees matching help visibility for `--schema` export. */
|
|
66
|
-
export function exportPresentationBuiltins(
|
|
67
|
-
program
|
|
68
|
-
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
const builtins: CliSchemaExport[] = [];
|
|
72
|
-
pushExportedBuiltin(builtins, cliBuiltinCompletionGroup(program));
|
|
73
|
-
pushExportedBuiltin(builtins, cliBuiltinVersionCommand());
|
|
74
|
-
if (resolved.install) {
|
|
75
|
-
pushExportedBuiltin(builtins, cliBuiltinInstallCommand(program));
|
|
76
|
-
}
|
|
77
|
-
const docsGroup = cliBuiltinDocsGroupIfEnabled(program);
|
|
78
|
-
if (docsGroup) {
|
|
79
|
-
pushExportedBuiltin(builtins, docsGroup);
|
|
80
|
-
}
|
|
81
|
-
if (resolved.mcp) {
|
|
82
|
-
pushExportedBuiltin(builtins, cliBuiltinMcpCommand(program));
|
|
83
|
-
}
|
|
84
|
-
return builtins;
|
|
55
|
+
export function exportPresentationBuiltins(program: CliProgram): CliSchemaExport[] {
|
|
56
|
+
const caps = resolveCapabilities(program);
|
|
57
|
+
return resolveBuiltins(program, caps)
|
|
58
|
+
.map((cmd) => exportBuiltinNode(cmd))
|
|
59
|
+
.filter((node): node is CliSchemaExport => node !== null);
|
|
85
60
|
}
|
package/src/builtins/index.ts
CHANGED
package/src/builtins/install.ts
CHANGED
|
@@ -92,6 +92,19 @@ export function installBuiltinOptions(root: CliProgram): CliOption[] {
|
|
|
92
92
|
});
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
if (root.appConfig) {
|
|
96
|
+
opts.push({
|
|
97
|
+
name: "configure",
|
|
98
|
+
description: "Interactively write app config (~/.config/<key>/config).",
|
|
99
|
+
kind: CliOptionKind.Presence,
|
|
100
|
+
});
|
|
101
|
+
opts.push({
|
|
102
|
+
name: "config",
|
|
103
|
+
description: "Remove app config file (with --uninstall).",
|
|
104
|
+
kind: CliOptionKind.Presence,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
95
108
|
return opts;
|
|
96
109
|
}
|
|
97
110
|
|
package/src/builtins/mcp.ts
CHANGED
|
@@ -28,7 +28,7 @@ export function cliBuiltinMcpCommand(program: CliProgram): CliRouter {
|
|
|
28
28
|
|
|
29
29
|
const bundle: CliLeaf = {
|
|
30
30
|
key: "bundle",
|
|
31
|
-
description: "Pack
|
|
31
|
+
description: "Pack Claude Desktop `.mcpb` and Claude Code plugin zip from dist/<key>.",
|
|
32
32
|
handler: () => {},
|
|
33
33
|
};
|
|
34
34
|
|
|
@@ -1,28 +1,13 @@
|
|
|
1
1
|
import type { CliCapabilities } from "../capabilities.ts";
|
|
2
2
|
import { resolveCapabilities } from "../capabilities.ts";
|
|
3
|
-
import { cliBuiltinDocsGroupIfEnabled } from "../docs/builtin.ts";
|
|
4
3
|
import { presentationNode, visibleOptions } from "../hidden.ts";
|
|
5
4
|
import type { CliLeaf, CliNode, CliProgram, CliRouter } from "../types.ts";
|
|
6
5
|
import { isCliLeaf } from "../types.ts";
|
|
7
|
-
import {
|
|
8
|
-
import { cliBuiltinInstallCommand } from "./install.ts";
|
|
9
|
-
import { cliBuiltinMcpCommand } from "./mcp.ts";
|
|
10
|
-
import { cliBuiltinVersionCommand } from "./version.ts";
|
|
6
|
+
import { resolveBuiltins } from "./registry.ts";
|
|
11
7
|
|
|
12
8
|
/** All built-in command nodes for argv parsing (includes hidden builtins). */
|
|
13
9
|
export function parseBuiltins(program: CliProgram, caps: CliCapabilities): CliNode[] {
|
|
14
|
-
|
|
15
|
-
if (caps.install) {
|
|
16
|
-
builtins.push(cliBuiltinInstallCommand(program));
|
|
17
|
-
}
|
|
18
|
-
const docsGroup = cliBuiltinDocsGroupIfEnabled(program);
|
|
19
|
-
if (docsGroup) {
|
|
20
|
-
builtins.push(docsGroup);
|
|
21
|
-
}
|
|
22
|
-
if (caps.mcp) {
|
|
23
|
-
builtins.push(cliBuiltinMcpCommand(program));
|
|
24
|
-
}
|
|
25
|
-
return builtins;
|
|
10
|
+
return resolveBuiltins(program, caps);
|
|
26
11
|
}
|
|
27
12
|
|
|
28
13
|
/** Built-in subtrees visible in help, schema, and completions (hidden builtins omitted). */
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { CliCapabilities } from "../capabilities.ts";
|
|
2
|
+
import { cliBuiltinDocsGroupIfEnabled } from "../docs/builtin.ts";
|
|
3
|
+
import type { CliNode, CliProgram } from "../types.ts";
|
|
4
|
+
import { cliBuiltinCompletionGroup } from "./completion-group.ts";
|
|
5
|
+
import { cliBuiltinConfigGroupIfEnabled } from "./config.ts";
|
|
6
|
+
import { cliBuiltinInstallCommand } from "./install.ts";
|
|
7
|
+
import { cliBuiltinMcpCommand } from "./mcp.ts";
|
|
8
|
+
import { cliBuiltinVersionCommand } from "./version.ts";
|
|
9
|
+
|
|
10
|
+
type BuiltinFactory = (program: CliProgram) => CliNode | null;
|
|
11
|
+
|
|
12
|
+
function pushBuiltin(
|
|
13
|
+
builtins: CliNode[],
|
|
14
|
+
program: CliProgram,
|
|
15
|
+
factory: BuiltinFactory | null,
|
|
16
|
+
): void {
|
|
17
|
+
if (!factory) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const node = factory(program);
|
|
21
|
+
if (node) {
|
|
22
|
+
builtins.push(node);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Capability-gated built-in command nodes in stable order (parse, help, export). */
|
|
27
|
+
export function resolveBuiltins(program: CliProgram, caps: CliCapabilities): CliNode[] {
|
|
28
|
+
const builtins: CliNode[] = [];
|
|
29
|
+
pushBuiltin(builtins, program, (p) => cliBuiltinCompletionGroup(p));
|
|
30
|
+
pushBuiltin(builtins, program, () => cliBuiltinVersionCommand());
|
|
31
|
+
if (caps.install) {
|
|
32
|
+
pushBuiltin(builtins, program, (p) => cliBuiltinInstallCommand(p));
|
|
33
|
+
}
|
|
34
|
+
pushBuiltin(builtins, program, (p) => cliBuiltinDocsGroupIfEnabled(p) ?? null);
|
|
35
|
+
if (caps.mcp) {
|
|
36
|
+
pushBuiltin(builtins, program, (p) => cliBuiltinMcpCommand(p));
|
|
37
|
+
}
|
|
38
|
+
pushBuiltin(builtins, program, (p) => cliBuiltinConfigGroupIfEnabled(p) ?? null);
|
|
39
|
+
return builtins;
|
|
40
|
+
}
|
package/src/capabilities.ts
CHANGED
|
@@ -3,6 +3,7 @@ Internal capability resolver — decides which platform builtins are active for
|
|
|
3
3
|
Not exported from the public package barrel.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { configCommandsEnabled } from "./config/entry.ts";
|
|
6
7
|
import type { CliProgram } from "./types.ts";
|
|
7
8
|
|
|
8
9
|
/** Platform builtins derived from program config and runtime. */
|
|
@@ -12,6 +13,7 @@ export interface CliCapabilities {
|
|
|
12
13
|
install: boolean;
|
|
13
14
|
docs: boolean;
|
|
14
15
|
update: boolean;
|
|
16
|
+
configCommands: boolean;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
/** Resolves which capabilities are enabled for a program. */
|
|
@@ -23,6 +25,7 @@ export function resolveCapabilities(program: CliProgram): CliCapabilities {
|
|
|
23
25
|
install,
|
|
24
26
|
docs: program.docs?.enabled === true,
|
|
25
27
|
update: install && typeof program.install?.updateGetLatest === "function",
|
|
28
|
+
configCommands: configCommandsEnabled(program),
|
|
26
29
|
};
|
|
27
30
|
}
|
|
28
31
|
|
|
@@ -38,5 +41,48 @@ export function reservedCommandNames(caps: CliCapabilities): string[] {
|
|
|
38
41
|
if (caps.mcp) {
|
|
39
42
|
names.push("mcp");
|
|
40
43
|
}
|
|
44
|
+
if (caps.configCommands) {
|
|
45
|
+
names.push("config");
|
|
46
|
+
}
|
|
41
47
|
return names;
|
|
42
48
|
}
|
|
49
|
+
|
|
50
|
+
export type CapabilityFeature = "mcp" | "install" | "docs" | "config";
|
|
51
|
+
|
|
52
|
+
/** Stderr message when a disabled built-in is invoked from the CLI. */
|
|
53
|
+
export function capabilityDeniedMessage(feature: CapabilityFeature): string {
|
|
54
|
+
switch (feature) {
|
|
55
|
+
case "mcp":
|
|
56
|
+
return "MCP is not enabled. Set mcpServer: { enabled: true } on the program root.\n";
|
|
57
|
+
case "install":
|
|
58
|
+
return "install is disabled. Remove install.enabled: false from the program root.\n";
|
|
59
|
+
case "docs":
|
|
60
|
+
return "docs is not enabled. Set docs: { enabled: true } on the program root.\n";
|
|
61
|
+
case "config":
|
|
62
|
+
return "config commands are disabled. Set program.appConfig or enable config.commands.\n";
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Exit 1 when argv[0] names a built-in that capabilities disallow. */
|
|
67
|
+
export function assertBuiltinAllowed(argv: string[], caps: CliCapabilities): void {
|
|
68
|
+
if (argv.length < 1) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const first = argv[0];
|
|
72
|
+
if (first === "mcp" && !caps.mcp) {
|
|
73
|
+
process.stderr.write(capabilityDeniedMessage("mcp"));
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
if (first === "install" && !caps.install) {
|
|
77
|
+
process.stderr.write(capabilityDeniedMessage("install"));
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
if (first === "docs" && !caps.docs) {
|
|
81
|
+
process.stderr.write(capabilityDeniedMessage("docs"));
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
if (first === "config" && !caps.configCommands) {
|
|
85
|
+
process.stderr.write(capabilityDeniedMessage("config"));
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Handler error helper with contextual help.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { cliPresentationRoot } from "./builtins/presentation.ts";
|
|
6
|
+
import type { CliContext } from "./context.ts";
|
|
7
|
+
import { cliHelpRender } from "./help.ts";
|
|
8
|
+
|
|
9
|
+
export function cliErrWithHelp(ctx: CliContext, msg: string): never {
|
|
10
|
+
const color = process.stderr.isTTY;
|
|
11
|
+
const line = color ? `\u001B[31m${msg}\u001B[0m` : msg;
|
|
12
|
+
process.stderr.write(`${line}\n`);
|
|
13
|
+
process.stderr.write(cliHelpRender(cliPresentationRoot(ctx.program), ctx.commandPath, true));
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|