argsbarg 1.4.1 → 1.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/plans/v1.3_parser_ergonomics_b3e91f02.plan.md +455 -0
- package/.private/scratch.md +2 -1
- package/CHANGELOG.md +27 -1
- package/README.md +20 -6
- package/docs/ai-skills.md +75 -0
- package/docs/mcp.md +12 -10
- package/index.d.ts +26 -12
- package/package.json +1 -1
- package/src/ai.ts +7 -0
- package/src/completion.ts +50 -9
- package/src/context.ts +38 -0
- package/src/index.test.ts +458 -12
- package/src/mcp/tools.ts +14 -6
- package/src/parse.ts +62 -10
- package/src/runtime.ts +42 -23
- package/src/skill/generate.ts +183 -0
- package/src/skill/install.ts +45 -0
- package/src/types.ts +23 -12
- package/src/validate.ts +21 -15
package/docs/mcp.md
CHANGED
|
@@ -22,17 +22,19 @@ const cli: CliCommand = {
|
|
|
22
22
|
2. Run the MCP server:
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
|
-
myapp mcp
|
|
25
|
+
myapp ai mcp
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
The process reads NDJSON requests from stdin and writes NDJSON responses to stdout. It stays alive until stdin closes.
|
|
29
29
|
|
|
30
30
|
3. Point your MCP client at that command. See [Client setup](#client-setup).
|
|
31
31
|
|
|
32
|
+
Optionally install an agent skill for discovery without MCP: see [docs/ai-skills.md](ai-skills.md).
|
|
33
|
+
|
|
32
34
|
The `examples/nested.ts` demo enables MCP — try:
|
|
33
35
|
|
|
34
36
|
```bash
|
|
35
|
-
bun run examples/nested.ts mcp
|
|
37
|
+
bun run examples/nested.ts ai mcp
|
|
36
38
|
```
|
|
37
39
|
|
|
38
40
|
## Client setup
|
|
@@ -46,17 +48,17 @@ Add a server entry under `mcpServers` in your Cursor MCP config:
|
|
|
46
48
|
"mcpServers": {
|
|
47
49
|
"myapp": {
|
|
48
50
|
"command": "bun",
|
|
49
|
-
"args": ["run", "myapp.ts", "mcp"]
|
|
51
|
+
"args": ["run", "myapp.ts", "ai", "mcp"]
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
54
|
}
|
|
53
55
|
```
|
|
54
56
|
|
|
55
|
-
Use your real binary or script path. For a compiled CLI, `command` can be the installed binary and `args` can be `["mcp"]
|
|
57
|
+
Use your real binary or script path. For a compiled CLI, `command` can be the installed binary and `args` can be `["ai", "mcp"]`.
|
|
56
58
|
|
|
57
59
|
### Other MCP hosts
|
|
58
60
|
|
|
59
|
-
Any host that spawns a subprocess and wires stdin/stdout works the same way: the **command** is your app, and **`mcp`**
|
|
61
|
+
Any host that spawns a subprocess and wires stdin/stdout works the same way: the **command** is your app, and **`ai mcp`** starts the server.
|
|
60
62
|
|
|
61
63
|
## Configuration
|
|
62
64
|
|
|
@@ -83,7 +85,7 @@ mcpServer: {
|
|
|
83
85
|
|
|
84
86
|
## Tools
|
|
85
87
|
|
|
86
|
-
Every **user-defined leaf command** in your schema becomes one MCP tool. Built-ins (`completion`, `
|
|
88
|
+
Every **user-defined leaf command** in your schema becomes one MCP tool. Built-ins (`completion`, `ai`) are not exposed as tools.
|
|
87
89
|
|
|
88
90
|
### Tool names
|
|
89
91
|
|
|
@@ -138,7 +140,7 @@ mcpTool: {
|
|
|
138
140
|
Each tool’s `inputSchema` is a JSON Schema object built from your CLI definition:
|
|
139
141
|
|
|
140
142
|
- **Options** — parent-scoped flags are included (e.g. `stat`’s `--json` appears on `stat_owner_lookup`). Presence options are `boolean`; string, number, and **enum** options match their `CliOptionKind` (`Enum` uses JSON Schema `enum`). Required options are listed in `required`.
|
|
141
|
-
- **Positionals** — one property per `CliPositional` on the leaf. Single-slot positionals are `string`; varargs tails (`argMax: 0`) are `string[]`. Required positionals are listed in `required`.
|
|
143
|
+
- **Positionals** — one property per `CliPositional` on the leaf. Single-slot positionals are `string`; varargs tails (`argMax: 0`) are `string[]`. Required positionals are listed in `required`. For varargs, agents may also pass a comma-separated string (`"a,b"`) or a single string (`"a"`) — both are coerced to separate argv tokens at dispatch time.
|
|
142
144
|
|
|
143
145
|
Arguments are a **flat JSON object** keyed by option and positional names (same names as in your schema, including hyphenated option names like `"user-name"`).
|
|
144
146
|
|
|
@@ -275,7 +277,7 @@ Requests without an `id` are treated as notifications and do not receive a respo
|
|
|
275
277
|
### Manual smoke test
|
|
276
278
|
|
|
277
279
|
```bash
|
|
278
|
-
printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' | bun run examples/nested.ts mcp
|
|
280
|
+
printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' | bun run examples/nested.ts ai mcp
|
|
279
281
|
```
|
|
280
282
|
|
|
281
283
|
You should get one JSON line on stdout with `result.capabilities` and `result.serverInfo`.
|
|
@@ -284,11 +286,11 @@ You should get one JSON line on stdout with `result.capabilities` and `result.se
|
|
|
284
286
|
|
|
285
287
|
When MCP is enabled:
|
|
286
288
|
|
|
287
|
-
- Do not declare a top-level command named **`
|
|
289
|
+
- Do not declare a top-level command named **`ai`** — it is reserved for the built-in AI integration group.
|
|
288
290
|
- Do not declare a top-level command named **`completion`** — reserved for shell completions.
|
|
289
291
|
- Do not declare an option named **`schema`** — reserved for `--schema`.
|
|
290
292
|
|
|
291
|
-
Running `myapp mcp` without `mcpServer` on the root fails with an error (exit 1).
|
|
293
|
+
Running `myapp ai mcp` without `mcpServer` on the root fails with an error (exit 1).
|
|
292
294
|
|
|
293
295
|
## Design notes
|
|
294
296
|
|
package/index.d.ts
CHANGED
|
@@ -23,6 +23,10 @@ export declare class CliContext {
|
|
|
23
23
|
* This is the TypeScript-native advantage over the Swift version.
|
|
24
24
|
*/
|
|
25
25
|
typedOpt<T>(name: string, parse: (s: string) => T): T | null;
|
|
26
|
+
/** Returns the value(s) for a named positional slot. Varargs slots return string[]; single slots return string | undefined. */
|
|
27
|
+
positional(name: string): string | string[] | undefined;
|
|
28
|
+
private _posMap;
|
|
29
|
+
private _positionalMap;
|
|
26
30
|
}
|
|
27
31
|
/**
|
|
28
32
|
* How a leaf handler was dispatched.
|
|
@@ -42,21 +46,20 @@ export declare enum CliOptionKind {
|
|
|
42
46
|
Enum = "enum"
|
|
43
47
|
}
|
|
44
48
|
/**
|
|
45
|
-
* When fallbackCommand is used for missing or unknown
|
|
46
|
-
* Only the program root may set a non-default mode or a non-nil fallbackCommand.
|
|
49
|
+
* When `fallbackCommand` is used for missing or unknown subcommand tokens at a routing node.
|
|
47
50
|
*/
|
|
48
51
|
export declare enum CliFallbackMode {
|
|
49
52
|
/**
|
|
50
|
-
* If argv has no
|
|
53
|
+
* If argv has no next subcommand, route to `fallbackCommand`; if the token is unknown, error.
|
|
51
54
|
*/
|
|
52
55
|
MissingOnly = "missingOnly",
|
|
53
56
|
/**
|
|
54
|
-
* If argv has no
|
|
57
|
+
* If argv has no next subcommand or the token is not a known child, route to `fallbackCommand`.
|
|
55
58
|
*/
|
|
56
59
|
MissingOrUnknown = "missingOrUnknown",
|
|
57
60
|
/**
|
|
58
|
-
* If the
|
|
59
|
-
* When the
|
|
61
|
+
* If the next token is present but not a known child, route to `fallbackCommand`.
|
|
62
|
+
* When the subcommand token is missing (exhausted argv), do not use fallback (implicit scoped help).
|
|
60
63
|
*/
|
|
61
64
|
UnknownOnly = "unknownOnly"
|
|
62
65
|
}
|
|
@@ -102,7 +105,7 @@ export interface CliPositional {
|
|
|
102
105
|
argMax?: number;
|
|
103
106
|
}
|
|
104
107
|
/**
|
|
105
|
-
* Root-only. Enables `myapp mcp` and MCP stdio server metadata.
|
|
108
|
+
* Root-only. Enables `myapp ai mcp` and MCP stdio server metadata.
|
|
106
109
|
*/
|
|
107
110
|
export interface CliMcpServerConfig {
|
|
108
111
|
/** `initialize` serverInfo.name (default: root `key`). */
|
|
@@ -162,6 +165,15 @@ export interface CliMcpToolConfig {
|
|
|
162
165
|
*/
|
|
163
166
|
requiresEnv?: string[];
|
|
164
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* Root-only. Opt out of `ai skill` install commands with `{ enabled: false }`.
|
|
170
|
+
*/
|
|
171
|
+
export interface CliAiSkillConfig {
|
|
172
|
+
/** When `false`, disable `ai skill *` install commands (default: enabled). */
|
|
173
|
+
enabled?: boolean;
|
|
174
|
+
/** Skill directory name (default: sanitized root `key`). */
|
|
175
|
+
name?: string;
|
|
176
|
+
}
|
|
165
177
|
/**
|
|
166
178
|
* Base properties shared by all command nodes.
|
|
167
179
|
*/
|
|
@@ -174,8 +186,10 @@ export interface CliCommandBase {
|
|
|
174
186
|
notes?: string;
|
|
175
187
|
/** Global or command-level flags/options. */
|
|
176
188
|
options?: CliOption[];
|
|
177
|
-
/** Root-only. When set, enables the `mcp` built-in subcommand. */
|
|
189
|
+
/** Root-only. When set, enables the `ai mcp` built-in subcommand. */
|
|
178
190
|
mcpServer?: CliMcpServerConfig;
|
|
191
|
+
/** Root-only. Opt out of `ai skill` install with `{ enabled: false }`. */
|
|
192
|
+
aiSkill?: CliAiSkillConfig;
|
|
179
193
|
/** Leaf-only. Per-tool MCP exposure and metadata. */
|
|
180
194
|
mcpTool?: CliMcpToolConfig;
|
|
181
195
|
}
|
|
@@ -192,16 +206,16 @@ export type CliCommand = (CliCommandBase & {
|
|
|
192
206
|
positionals?: CliPositional[];
|
|
193
207
|
/** Nested subcommands (empty for leaf commands). */
|
|
194
208
|
commands?: never;
|
|
195
|
-
/** Default
|
|
209
|
+
/** Default subcommand (routing commands only). */
|
|
196
210
|
fallbackCommand?: never;
|
|
197
|
-
/** How fallbackCommand is applied (routing commands only). */
|
|
211
|
+
/** How fallbackCommand is applied at this routing node (routing commands only). */
|
|
198
212
|
fallbackMode?: never;
|
|
199
213
|
}) | (CliCommandBase & {
|
|
200
214
|
/** Nested subcommands. */
|
|
201
215
|
commands: CliCommand[];
|
|
202
|
-
/** Default
|
|
216
|
+
/** Default subcommand when argv omits a command or uses an unknown token at this routing node. */
|
|
203
217
|
fallbackCommand?: string;
|
|
204
|
-
/** How fallbackCommand is applied. */
|
|
218
|
+
/** How fallbackCommand is applied at this routing node (not root-only). */
|
|
205
219
|
fallbackMode?: CliFallbackMode;
|
|
206
220
|
/** Handler function (leaf commands only). */
|
|
207
221
|
handler?: never;
|
package/package.json
CHANGED
package/src/ai.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/*
|
|
2
|
+
This module re-exports AI agent integration entry points (skill install).
|
|
3
|
+
MCP stdio serving remains in mcp.ts.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { cliSkillInstall, type SkillInstallOpts } from "./skill/install.ts";
|
|
7
|
+
export { generateSkillBundle, type SkillBundle, type SkillTarget } from "./skill/generate.ts";
|
package/src/completion.ts
CHANGED
|
@@ -597,21 +597,62 @@ export function cliPresentationRoot(root: CliCommand): CliCommand {
|
|
|
597
597
|
} as CliCommand;
|
|
598
598
|
}
|
|
599
599
|
|
|
600
|
+
/** Presence options for skill install leaves (`--global`, `--force`). */
|
|
601
|
+
function skillInstallOptions(): CliOption[] {
|
|
602
|
+
return [
|
|
603
|
+
{
|
|
604
|
+
name: "global",
|
|
605
|
+
description: "Install to the user skills directory (~/.cursor/skills or ~/.claude/skills).",
|
|
606
|
+
kind: CliOptionKind.Presence,
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
name: "force",
|
|
610
|
+
description: "Overwrite an existing skill directory.",
|
|
611
|
+
kind: CliOptionKind.Presence,
|
|
612
|
+
},
|
|
613
|
+
];
|
|
614
|
+
}
|
|
615
|
+
|
|
600
616
|
/** Built-in commands shown in help and merged for routing CLIs. */
|
|
601
617
|
function presentationBuiltins(root: CliCommand): CliCommand[] {
|
|
602
|
-
|
|
618
|
+
return [cliBuiltinCompletionGroup(root.key), cliBuiltinAiGroup(root)];
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/** Builds the `ai` built-in command group (MCP + skill install). */
|
|
622
|
+
export function cliBuiltinAiGroup(root: CliCommand): CliCommand {
|
|
623
|
+
const aiChildren: CliCommand[] = [
|
|
624
|
+
{
|
|
625
|
+
key: "skill",
|
|
626
|
+
description: "Install agent skill files for Cursor or Claude Code.",
|
|
627
|
+
commands: [
|
|
628
|
+
{
|
|
629
|
+
key: "cursor",
|
|
630
|
+
description: "Install Cursor skill (SKILL.md + reference.md).",
|
|
631
|
+
options: skillInstallOptions(),
|
|
632
|
+
handler: () => {},
|
|
633
|
+
},
|
|
634
|
+
{
|
|
635
|
+
key: "claude",
|
|
636
|
+
description: "Install Claude Code skill (SKILL.md + reference.md).",
|
|
637
|
+
options: skillInstallOptions(),
|
|
638
|
+
handler: () => {},
|
|
639
|
+
},
|
|
640
|
+
],
|
|
641
|
+
},
|
|
642
|
+
];
|
|
643
|
+
|
|
603
644
|
if (root.mcpServer !== undefined) {
|
|
604
|
-
|
|
645
|
+
aiChildren.unshift({
|
|
646
|
+
key: "mcp",
|
|
647
|
+
description: "Run as an MCP server over stdio (for AI agents).",
|
|
648
|
+
handler: () => {},
|
|
649
|
+
});
|
|
605
650
|
}
|
|
606
|
-
return cmds;
|
|
607
|
-
}
|
|
608
651
|
|
|
609
|
-
/** Builds the static `mcp` leaf command (merged when `root.mcpServer` is set). */
|
|
610
|
-
export function cliBuiltinMcpCommand(): CliCommand {
|
|
611
652
|
return {
|
|
612
|
-
key: "
|
|
613
|
-
description: "
|
|
614
|
-
|
|
653
|
+
key: "ai",
|
|
654
|
+
description: "AI agent integration (MCP server and skill install).",
|
|
655
|
+
commands: aiChildren,
|
|
615
656
|
};
|
|
616
657
|
}
|
|
617
658
|
|
package/src/context.ts
CHANGED
|
@@ -68,4 +68,42 @@ export class CliContext {
|
|
|
68
68
|
return null;
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
|
+
|
|
72
|
+
/** Returns the value(s) for a named positional slot. Varargs slots return string[]; single slots return string | undefined. */
|
|
73
|
+
positional(name: string): string | string[] | undefined {
|
|
74
|
+
return this._positionalMap()[name];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private _posMap: Record<string, string | string[]> | undefined;
|
|
78
|
+
|
|
79
|
+
private _positionalMap(): Record<string, string | string[]> {
|
|
80
|
+
if (this._posMap) return this._posMap;
|
|
81
|
+
|
|
82
|
+
let node: CliCommand = this.schema;
|
|
83
|
+
for (const seg of this.commandPath) {
|
|
84
|
+
const child = (node.commands ?? []).find((c) => c.key === seg);
|
|
85
|
+
if (!child) {
|
|
86
|
+
this._posMap = {};
|
|
87
|
+
return {};
|
|
88
|
+
}
|
|
89
|
+
node = child;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const map: Record<string, string | string[]> = {};
|
|
93
|
+
let argIdx = 0;
|
|
94
|
+
for (const p of node.positionals ?? []) {
|
|
95
|
+
const { argMax = 1 } = p;
|
|
96
|
+
if (argMax === 0) {
|
|
97
|
+
map[p.name] = this.args.slice(argIdx);
|
|
98
|
+
argIdx = this.args.length;
|
|
99
|
+
} else {
|
|
100
|
+
const val = this.args[argIdx];
|
|
101
|
+
if (val !== undefined) map[p.name] = val;
|
|
102
|
+
argIdx++;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
this._posMap = map;
|
|
107
|
+
return map;
|
|
108
|
+
}
|
|
71
109
|
}
|