argsbarg 3.3.12 → 3.3.14
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 +15 -1
- package/docs/ai-skills.md +4 -4
- package/docs/bundled-docs.md +3 -3
- package/package.json +1 -1
- package/src/builtins/builtins.test.ts +9 -3
- package/src/builtins/completion-group.ts +18 -17
- package/src/builtins/dispatch.ts +2 -2
- package/src/builtins/export.ts +2 -2
- package/src/builtins/install.ts +2 -1
- package/src/builtins/mcp.ts +19 -5
- package/src/builtins/presentation.ts +3 -4
- package/src/docs/builtin.ts +8 -2
- package/src/docs/docs.test.ts +17 -2
- package/src/index.test.ts +14 -10
- package/src/skill/generate.ts +60 -24
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [3.3.14] - 2026-06-21
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **Generated notes** — deduplicated agent, docs, MCP, and completion help; each topic owns its guidance in one place.
|
|
15
|
+
|
|
16
|
+
## [3.3.13] - 2026-06-21
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- **Agent skills** — `SKILL.md` is a compact command index; `reference.md` holds the full `docs api` guide. `docs skill` notes recommend `install --skill` for the optimized persisted bundle.
|
|
21
|
+
|
|
10
22
|
## [3.3.12] - 2026-06-21
|
|
11
23
|
|
|
12
24
|
### Changed
|
|
@@ -323,7 +335,9 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
|
|
|
323
335
|
- 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`).
|
|
324
336
|
- Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
|
|
325
337
|
|
|
326
|
-
[Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v3.3.
|
|
338
|
+
[Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v3.3.14...HEAD
|
|
339
|
+
[3.3.14]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.14
|
|
340
|
+
[3.3.13]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.13
|
|
327
341
|
[3.3.12]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.12
|
|
328
342
|
[3.3.11]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.11
|
|
329
343
|
[3.3.10]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.10
|
package/docs/ai-skills.md
CHANGED
|
@@ -31,8 +31,8 @@ For library use, call `cliSkillInstall(root, "cursor" | "claude", { global: true
|
|
|
31
31
|
|
|
32
32
|
## Generated content
|
|
33
33
|
|
|
34
|
-
- **`SKILL.md`** — YAML frontmatter,
|
|
35
|
-
- **`reference.md`** — full `docs
|
|
34
|
+
- **`SKILL.md`** — YAML frontmatter, compact command index, pitfalls, and a pointer to `reference.md`
|
|
35
|
+
- **`reference.md`** — full `docs api` markdown reference
|
|
36
36
|
|
|
37
37
|
Installed files include an HTML comment hint (`Generated by myapp install --skill; do not edit.`) after SKILL.md frontmatter and at the top of `reference.md`.
|
|
38
38
|
|
|
@@ -43,10 +43,10 @@ Skills describe **shell invocation only** — no MCP setup, `mcp.json`, or `tool
|
|
|
43
43
|
| Mechanism | Role |
|
|
44
44
|
| --- | --- |
|
|
45
45
|
| **`myapp mcp`** (requires `mcpServer`) | Runtime tool execution over MCP |
|
|
46
|
-
| **`myapp install --skill`** |
|
|
46
|
+
| **`myapp install --skill`** | Persists optimized skill bundle (`SKILL.md` index + `reference.md` full API) |
|
|
47
47
|
| **`myapp docs`** (requires `docs`) | Bundled markdown on stdout (`docs mcp` when MCP enabled) |
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
`SKILL.md` is the routing index; `reference.md` matches `docs api`. Prefer `install --skill` over `docs skill` for agents. See [cli-program.md](cli-program.md).
|
|
50
50
|
|
|
51
51
|
See also:
|
|
52
52
|
|
package/docs/bundled-docs.md
CHANGED
|
@@ -35,7 +35,7 @@ myapp docs readme --save # write ./docs/readme.md
|
|
|
35
35
|
myapp docs schema --save # write ./docs/schema.json
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
When `docs` is enabled, top-level `myapp --help`
|
|
38
|
+
When `docs` is enabled, top-level `myapp --help` points agents at `myapp docs skill`. The `docs skill` subcommand description recommends `install --skill` for a persisted bundle.
|
|
39
39
|
|
|
40
40
|
## Configuration
|
|
41
41
|
|
|
@@ -68,7 +68,7 @@ When `docs.enabled` is `true`:
|
|
|
68
68
|
|
|
69
69
|
- **`docs schema`** — same JSON as the former root `--schema` flag (handlers omitted; built-in subtrees included for leaf roots).
|
|
70
70
|
- **`docs api`** — markdown rendering of the same command tree (options, positionals, subcommands, fallback routing).
|
|
71
|
-
- **`docs skill`** — prints
|
|
71
|
+
- **`docs skill`** — prints the compact `SKILL.md` index. Prefer `install --skill --yes` for agents (persists index + full API in `reference.md`).
|
|
72
72
|
|
|
73
73
|
## MCP guide (`docs mcp`)
|
|
74
74
|
|
|
@@ -84,7 +84,7 @@ All `docs` subcommands are hidden from MCP `tools/list` (`mcpTool: { enabled: fa
|
|
|
84
84
|
|
|
85
85
|
| Channel | Role |
|
|
86
86
|
| --- | --- |
|
|
87
|
-
| `install --skill` | Writes `SKILL.md`
|
|
87
|
+
| `install --skill` | Writes compact `SKILL.md` + full-API `reference.md` to disk |
|
|
88
88
|
| `docs skill` | Print generated `SKILL.md` to stdout |
|
|
89
89
|
| `docs api` | Print command tree markdown to stdout |
|
|
90
90
|
| `docs schema` | Print command tree JSON to stdout |
|
package/package.json
CHANGED
|
@@ -57,9 +57,14 @@ describe("builtins help copy", () => {
|
|
|
57
57
|
});
|
|
58
58
|
|
|
59
59
|
test("mcp builtin description is user-facing", () => {
|
|
60
|
-
const
|
|
60
|
+
const withDocs: CliProgram = {
|
|
61
|
+
...fixture,
|
|
62
|
+
docs: { enabled: true, topics: { readme: { text: "# r\n" } } },
|
|
63
|
+
};
|
|
64
|
+
const mcp = cliBuiltinMcpCommand(withDocs);
|
|
61
65
|
expect(mcp.description).toContain("MCP server");
|
|
62
|
-
expect(mcp.notes).toContain(
|
|
66
|
+
expect(mcp.notes).toContain("install --mcp --yes");
|
|
67
|
+
expect(mcp.notes).toContain("docs mcp");
|
|
63
68
|
});
|
|
64
69
|
});
|
|
65
70
|
|
|
@@ -87,7 +92,8 @@ describe("presentation root", () => {
|
|
|
87
92
|
docs: { enabled: true, topics: { readme: { text: "# r\n" } } },
|
|
88
93
|
};
|
|
89
94
|
const root = cliPresentationRoot(withDocs);
|
|
90
|
-
expect(root.notes).toContain("
|
|
95
|
+
expect(root.notes).toContain("For AI agents: `myapp docs skill`.");
|
|
96
|
+
expect(root.notes).not.toContain("install --skill");
|
|
91
97
|
});
|
|
92
98
|
});
|
|
93
99
|
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolveCapabilities } from "../capabilities.ts";
|
|
2
|
+
import { type CliLeaf, type CliProgram, type CliRouter } from "../types.ts";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Builds the static `completion` / `bash` / `zsh` / `fish` command subtree (merged into the program root at runtime).
|
|
5
6
|
*/
|
|
6
|
-
export function cliBuiltinCompletionGroup(
|
|
7
|
-
|
|
7
|
+
export function cliBuiltinCompletionGroup(program: CliProgram): CliRouter {
|
|
8
|
+
const appName = program.key;
|
|
9
|
+
const caps = resolveCapabilities(program);
|
|
10
|
+
const router: CliRouter = {
|
|
8
11
|
key: "completion",
|
|
9
12
|
description: "Generate the autocompletion script for shells.",
|
|
10
13
|
commands: [
|
|
@@ -12,15 +15,10 @@ export function cliBuiltinCompletionGroup(appName: string): CliRouter {
|
|
|
12
15
|
key: "bash",
|
|
13
16
|
description: "Print a bash tab-completion script.",
|
|
14
17
|
notes:
|
|
15
|
-
"
|
|
16
|
-
"Pipe it to a file, or feed it straight into your shell.\n\n" +
|
|
17
|
-
"To keep it across restarts, save it and source that file from ~/.bashrc.\n\n" +
|
|
18
|
-
"For example:\n\n" +
|
|
19
|
-
`echo 'eval \"$(${appName} completion bash)\"' >> ~/.bashrc\n` +
|
|
20
|
-
`\nor\n` +
|
|
18
|
+
"Manual install:\n\n" +
|
|
21
19
|
` ${appName} completion bash > ~/.bash_completion.d/${appName}\n` +
|
|
22
20
|
` echo 'source ~/.bash_completion.d/${appName}' >> ~/.bashrc\n\n` +
|
|
23
|
-
"
|
|
21
|
+
"Try this session only:\n\n" +
|
|
24
22
|
` source <(${appName} completion bash)`,
|
|
25
23
|
handler: () => {},
|
|
26
24
|
},
|
|
@@ -28,23 +26,26 @@ export function cliBuiltinCompletionGroup(appName: string): CliRouter {
|
|
|
28
26
|
key: "zsh",
|
|
29
27
|
description: "Print a zsh tab-completion script.",
|
|
30
28
|
notes:
|
|
31
|
-
"
|
|
32
|
-
`
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
` eval
|
|
29
|
+
"Manual install:\n\n" +
|
|
30
|
+
` ${appName} completion zsh > ~/.zsh/completions/_${appName}\n\n` +
|
|
31
|
+
"Ensure ~/.zsh/completions is on your fpath, then restart zsh.\n\n" +
|
|
32
|
+
"Try this session only:\n\n" +
|
|
33
|
+
` eval "$(${appName} completion zsh)"`,
|
|
36
34
|
handler: () => {},
|
|
37
35
|
},
|
|
38
36
|
{
|
|
39
37
|
key: "fish",
|
|
40
38
|
description: "Print a fish tab-completion script.",
|
|
41
39
|
notes:
|
|
42
|
-
"
|
|
43
|
-
"Install:\n" +
|
|
40
|
+
"Manual install:\n\n" +
|
|
44
41
|
` ${appName} completion fish > ~/.config/fish/completions/${appName}.fish\n\n` +
|
|
45
42
|
"Fish loads completions from that directory automatically.",
|
|
46
43
|
handler: () => {},
|
|
47
44
|
},
|
|
48
45
|
],
|
|
49
46
|
};
|
|
47
|
+
if (caps.install) {
|
|
48
|
+
router.notes = `Install for all shells:\n\n ${appName} install --completions --yes`;
|
|
49
|
+
}
|
|
50
|
+
return router;
|
|
50
51
|
}
|
package/src/builtins/dispatch.ts
CHANGED
|
@@ -110,7 +110,7 @@ export function builtinInterceptRoot(
|
|
|
110
110
|
parseRoot: {
|
|
111
111
|
key: program.key,
|
|
112
112
|
description: program.description,
|
|
113
|
-
commands: [completionGroup(program
|
|
113
|
+
commands: [completionGroup(program)],
|
|
114
114
|
},
|
|
115
115
|
isLeafCompletionIntercept: true,
|
|
116
116
|
};
|
|
@@ -132,7 +132,7 @@ export function builtinInterceptRoot(
|
|
|
132
132
|
parseRoot: {
|
|
133
133
|
key: program.key,
|
|
134
134
|
description: program.description,
|
|
135
|
-
commands: [cliBuiltinMcpCommand()],
|
|
135
|
+
commands: [cliBuiltinMcpCommand(program)],
|
|
136
136
|
},
|
|
137
137
|
isLeafCompletionIntercept: false,
|
|
138
138
|
};
|
package/src/builtins/export.ts
CHANGED
|
@@ -45,7 +45,7 @@ function exportBuiltinNode(cmd: {
|
|
|
45
45
|
export function exportPresentationBuiltins(program: CliProgram, caps?: CliCapabilities): CliSchemaExport[] {
|
|
46
46
|
const resolved = caps ?? resolveCapabilities(program);
|
|
47
47
|
const builtins: CliSchemaExport[] = [
|
|
48
|
-
exportBuiltinNode(cliBuiltinCompletionGroup(program
|
|
48
|
+
exportBuiltinNode(cliBuiltinCompletionGroup(program)),
|
|
49
49
|
exportBuiltinNode(cliBuiltinVersionCommand()),
|
|
50
50
|
];
|
|
51
51
|
if (resolved.install) {
|
|
@@ -56,7 +56,7 @@ export function exportPresentationBuiltins(program: CliProgram, caps?: CliCapabi
|
|
|
56
56
|
builtins.push(exportBuiltinNode(docsGroup));
|
|
57
57
|
}
|
|
58
58
|
if (resolved.mcp) {
|
|
59
|
-
builtins.push(exportBuiltinNode(cliBuiltinMcpCommand()));
|
|
59
|
+
builtins.push(exportBuiltinNode(cliBuiltinMcpCommand(program)));
|
|
60
60
|
}
|
|
61
61
|
return builtins;
|
|
62
62
|
}
|
package/src/builtins/install.ts
CHANGED
|
@@ -117,7 +117,8 @@ export function cliBuiltinInstallCommand(root: CliProgram): CliLeaf {
|
|
|
117
117
|
"Remove everything installed with --all:",
|
|
118
118
|
` ${app} install --uninstall --all --yes`,
|
|
119
119
|
"",
|
|
120
|
-
"Use --dry to preview changes
|
|
120
|
+
"Use --dry to preview changes without writing files.",
|
|
121
|
+
"Use --json for machine-readable output.",
|
|
121
122
|
);
|
|
122
123
|
return {
|
|
123
124
|
key: "install",
|
package/src/builtins/mcp.ts
CHANGED
|
@@ -1,13 +1,27 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolveCapabilities } from "../capabilities.ts";
|
|
2
|
+
import { docsEnabled } from "../docs/resolve.ts";
|
|
3
|
+
import { type CliLeaf, type CliProgram } from "../types.ts";
|
|
2
4
|
|
|
3
5
|
/** Presence options for the top-level `mcp` built-in (leaf). */
|
|
4
|
-
export function cliBuiltinMcpCommand(): CliLeaf {
|
|
6
|
+
export function cliBuiltinMcpCommand(program: CliProgram): CliLeaf {
|
|
7
|
+
const caps = resolveCapabilities(program);
|
|
8
|
+
const lines = [
|
|
9
|
+
"Stdio MCP server. Add to Cursor or Claude:",
|
|
10
|
+
"",
|
|
11
|
+
" command: {argsbarg:program}",
|
|
12
|
+
" args: mcp",
|
|
13
|
+
"",
|
|
14
|
+
];
|
|
15
|
+
if (caps.install) {
|
|
16
|
+
lines.push("Or:", "", " {argsbarg:program} install --mcp --yes", "");
|
|
17
|
+
}
|
|
18
|
+
if (docsEnabled(program)) {
|
|
19
|
+
lines.push("Full setup guide: {argsbarg:program} docs mcp");
|
|
20
|
+
}
|
|
5
21
|
return {
|
|
6
22
|
key: "mcp",
|
|
7
23
|
description: "Run as an MCP server over stdio for AI agents.",
|
|
8
|
-
notes:
|
|
9
|
-
"Configure MCP clients with `command` set to this program name and `args` set to `[\"mcp\"]`.\n\n" +
|
|
10
|
-
"See docs/mcp.md for setup details.",
|
|
24
|
+
notes: lines.join("\n"),
|
|
11
25
|
handler: () => {},
|
|
12
26
|
};
|
|
13
27
|
}
|
|
@@ -11,7 +11,7 @@ import { cliBuiltinDocsGroupIfEnabled } from "../docs/builtin.ts";
|
|
|
11
11
|
/** Built-in command nodes injected for help, schema, and completions. */
|
|
12
12
|
export function presentationBuiltins(program: CliProgram, caps: CliCapabilities): CliNode[] {
|
|
13
13
|
const builtins: CliNode[] = [
|
|
14
|
-
cliBuiltinCompletionGroup(program
|
|
14
|
+
cliBuiltinCompletionGroup(program),
|
|
15
15
|
cliBuiltinVersionCommand(),
|
|
16
16
|
];
|
|
17
17
|
if (caps.install) {
|
|
@@ -22,7 +22,7 @@ export function presentationBuiltins(program: CliProgram, caps: CliCapabilities)
|
|
|
22
22
|
builtins.push(docsGroup);
|
|
23
23
|
}
|
|
24
24
|
if (caps.mcp) {
|
|
25
|
-
builtins.push(cliBuiltinMcpCommand());
|
|
25
|
+
builtins.push(cliBuiltinMcpCommand(program));
|
|
26
26
|
}
|
|
27
27
|
return builtins;
|
|
28
28
|
}
|
|
@@ -64,8 +64,7 @@ export function presentationRootNotes(program: CliProgram, caps: CliCapabilities
|
|
|
64
64
|
parts.push(program.notes!.trim());
|
|
65
65
|
}
|
|
66
66
|
if (caps.docs) {
|
|
67
|
-
|
|
68
|
-
parts.push(`Agents: run \`${cmd}\` to learn how to use this app`);
|
|
67
|
+
parts.push(`For AI agents: \`${program.key} docs skill\`.`);
|
|
69
68
|
}
|
|
70
69
|
if (parts.length === 0) {
|
|
71
70
|
return undefined;
|
package/src/docs/builtin.ts
CHANGED
|
@@ -36,6 +36,11 @@ function docsLeaf(program: CliProgram, key: string, description: string): CliLea
|
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/** Help notes for the `docs` router. */
|
|
40
|
+
function docsRouterNotes(): string {
|
|
41
|
+
return "Topics print to stdout. Add --save to write files under ./docs/.";
|
|
42
|
+
}
|
|
43
|
+
|
|
39
44
|
/** Built-in `docs` router with bundled topic subcommands. */
|
|
40
45
|
export function cliBuiltinDocsGroup(program: CliProgram): CliRouter {
|
|
41
46
|
const docs = program.docs!;
|
|
@@ -54,13 +59,14 @@ export function cliBuiltinDocsGroup(program: CliProgram): CliRouter {
|
|
|
54
59
|
|
|
55
60
|
leaves.push(
|
|
56
61
|
docsLeaf(program, "schema", "Print the full command tree as JSON."),
|
|
57
|
-
docsLeaf(program, "api", "Print the command
|
|
58
|
-
docsLeaf(program, "skill", "Print
|
|
62
|
+
docsLeaf(program, "api", "Print the full command reference as markdown."),
|
|
63
|
+
docsLeaf(program, "skill", "Print a reference agent SKILL, use `install --skill` for optimized."),
|
|
59
64
|
);
|
|
60
65
|
|
|
61
66
|
return {
|
|
62
67
|
key: "docs",
|
|
63
68
|
description: docs.description ?? DOCS_ROUTER_DESCRIPTION,
|
|
69
|
+
notes: docsRouterNotes(),
|
|
64
70
|
options: [DOCS_SAVE_OPTION],
|
|
65
71
|
fallbackCommand: docsEffectiveDefaultTopic(docs),
|
|
66
72
|
fallbackMode: CliFallbackMode.MissingOnly,
|
package/src/docs/docs.test.ts
CHANGED
|
@@ -152,11 +152,26 @@ test("docs skill prints Cursor SKILL.md", async () => {
|
|
|
152
152
|
expect(result.exitCode).toBe(0);
|
|
153
153
|
expect(result.stdout).toContain("---");
|
|
154
154
|
expect(result.stdout).toContain("name: myapp");
|
|
155
|
-
expect(result.stdout).toContain("
|
|
156
|
-
expect(result.stdout).
|
|
155
|
+
expect(result.stdout).toContain("## Commands");
|
|
156
|
+
expect(result.stdout).toContain("For full detail, open `reference.md`");
|
|
157
|
+
expect(result.stdout).not.toContain("#### Options");
|
|
157
158
|
expect(result.stdout).not.toContain("mcp.json");
|
|
158
159
|
});
|
|
159
160
|
|
|
161
|
+
test("docs skill help recommends install --skill", async () => {
|
|
162
|
+
const presentation = cliPresentationRoot(docsFixture());
|
|
163
|
+
const docsNode = presentation.commands.find((c) => c.key === "docs");
|
|
164
|
+
expect(docsNode && "commands" in docsNode).toBe(true);
|
|
165
|
+
if (docsNode && "commands" in docsNode) {
|
|
166
|
+
const skill = docsNode.commands.find((c) => c.key === "skill");
|
|
167
|
+
expect(skill?.description).toContain("reference agent SKILL");
|
|
168
|
+
expect(skill?.description).toContain("install --skill");
|
|
169
|
+
expect(skill?.notes).toBeUndefined();
|
|
170
|
+
expect(docsNode.notes).toContain("--save");
|
|
171
|
+
expect(docsNode.notes).not.toContain("install --skill");
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
160
175
|
test("presentation includes docs schema and skill", () => {
|
|
161
176
|
const presentation = cliPresentationRoot(docsFixture());
|
|
162
177
|
const docsNode = presentation.commands.find((c) => c.key === "docs");
|
package/src/index.test.ts
CHANGED
|
@@ -496,7 +496,7 @@ test("leaf completion help prints correctly", async () => {
|
|
|
496
496
|
const out = stdout.toString();
|
|
497
497
|
expect(exitCode).toBe(0);
|
|
498
498
|
expect(out).toContain("Show help for this command.");
|
|
499
|
-
expect(out).toContain("
|
|
499
|
+
expect(out).toContain("Manual install:");
|
|
500
500
|
expect(stderr.toString()).toBe("");
|
|
501
501
|
});
|
|
502
502
|
|
|
@@ -658,7 +658,7 @@ test("docs help lists schema, api, and skill subcommands", () => {
|
|
|
658
658
|
expect(help).toContain("api");
|
|
659
659
|
expect(help).toContain("markdown");
|
|
660
660
|
expect(help).toContain("skill");
|
|
661
|
-
expect(help).toContain("SKILL
|
|
661
|
+
expect(help).toContain("reference agent SKILL");
|
|
662
662
|
});
|
|
663
663
|
|
|
664
664
|
test("root help omits legacy --schema flag", () => {
|
|
@@ -690,7 +690,8 @@ test("root help shows agent docs hint when docs enabled", () => {
|
|
|
690
690
|
commands: [{ key: "run", description: "Run.", handler: () => {} }],
|
|
691
691
|
});
|
|
692
692
|
const help = cliHelpRender(cliPresentationRoot(root), [], false);
|
|
693
|
-
expect(help).toContain("
|
|
693
|
+
expect(help).toContain("For AI agents: `myapp docs skill`.");
|
|
694
|
+
expect(help).not.toContain("install --skill");
|
|
694
695
|
});
|
|
695
696
|
|
|
696
697
|
test("root help omits agent hint when docs disabled", () => {
|
|
@@ -1808,22 +1809,24 @@ test("install config on non-root node is rejected", () => {
|
|
|
1808
1809
|
expect(() => cliValidateProgram(root)).toThrow(/install is only supported on the program root/);
|
|
1809
1810
|
});
|
|
1810
1811
|
|
|
1811
|
-
test("generateSkillBundle includes frontmatter and
|
|
1812
|
+
test("generateSkillBundle includes frontmatter and compact command index", () => {
|
|
1812
1813
|
const bundle = generateSkillBundle(nestedMcpFixture, "cursor");
|
|
1813
1814
|
expect(bundle.dirName).toBe("nested_ts");
|
|
1814
1815
|
expect(bundle.skillMd).toMatch(/^---\nname: nested_ts\n/);
|
|
1815
|
-
expect(bundle.skillMd).toContain("
|
|
1816
|
-
expect(bundle.skillMd).toContain("
|
|
1816
|
+
expect(bundle.skillMd).toContain("## Commands");
|
|
1817
|
+
expect(bundle.skillMd).toContain("`nested.ts stat owner lookup <path>`");
|
|
1817
1818
|
expect(bundle.skillMd).toContain("Invoke via shell:");
|
|
1818
|
-
expect(bundle.skillMd).
|
|
1819
|
+
expect(bundle.skillMd).toContain("For full detail, open `reference.md`");
|
|
1820
|
+
expect(bundle.skillMd).not.toContain("#### Options");
|
|
1819
1821
|
expect(bundle.skillMd).not.toContain("CLI API reference");
|
|
1820
1822
|
expect(bundle.skillMd).not.toContain("mcp.json");
|
|
1821
1823
|
expect(bundle.skillMd).not.toContain("Prefer MCP");
|
|
1822
1824
|
expect(bundle.skillMd).not.toContain("tools/call");
|
|
1823
1825
|
expect(bundle.skillMd).not.toContain("Generated by");
|
|
1824
|
-
expect(bundle.referenceMd).toContain("
|
|
1826
|
+
expect(bundle.referenceMd).toContain("CLI API reference");
|
|
1827
|
+
expect(bundle.referenceMd).toContain("#### Options");
|
|
1825
1828
|
expect(bundle.referenceMd).not.toContain("Generated by");
|
|
1826
|
-
expect(
|
|
1829
|
+
expect(bundle.referenceMd).not.toContain("```json");
|
|
1827
1830
|
});
|
|
1828
1831
|
|
|
1829
1832
|
test("cliSkillInstall writes project Cursor skill files", () => {
|
|
@@ -1836,7 +1839,8 @@ test("cliSkillInstall writes project Cursor skill files", () => {
|
|
|
1836
1839
|
const skillDir = join(cwd, ".cursor", "skills", "nested_ts");
|
|
1837
1840
|
expect(existsSync(join(skillDir, "SKILL.md"))).toBe(true);
|
|
1838
1841
|
expect(existsSync(join(skillDir, "reference.md"))).toBe(true);
|
|
1839
|
-
expect(readFileSync(join(skillDir, "SKILL.md"), "utf8")).toContain("
|
|
1842
|
+
expect(readFileSync(join(skillDir, "SKILL.md"), "utf8")).toContain("## Commands");
|
|
1843
|
+
expect(readFileSync(join(skillDir, "reference.md"), "utf8")).toContain("CLI API reference");
|
|
1840
1844
|
const skillText = readFileSync(join(skillDir, "SKILL.md"), "utf8");
|
|
1841
1845
|
expect(skillText.startsWith("---\n")).toBe(true);
|
|
1842
1846
|
const hint = "<!-- Generated by nested.ts install --skill; do not edit. -->";
|
package/src/skill/generate.ts
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
This module generates Agent Skills content (SKILL.md + reference.md) from a CLI schema.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { collectMcpTools, sanitizeToolSegment } from "../mcp/tools.ts";
|
|
8
|
-
import { CliProgram } from "../types.ts";
|
|
5
|
+
import { generateApiGuide } from "../docs/api-guide.ts";
|
|
6
|
+
import { collectOptionDefs } from "../parse.ts";
|
|
7
|
+
import { collectMcpTools, sanitizeToolSegment, type McpToolDef } from "../mcp/tools.ts";
|
|
8
|
+
import { CliProgram, CliOptionKind } from "../types.ts";
|
|
9
9
|
|
|
10
10
|
export type SkillTarget = "cursor" | "claude";
|
|
11
11
|
|
|
@@ -31,10 +31,47 @@ function skillDescription(root: CliProgram): string {
|
|
|
31
31
|
return truncate(desc, 1024);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
/** CLI path with required single-slot positionals for the compact catalog. */
|
|
35
|
+
function commandCatalogPath(root: CliProgram, tool: McpToolDef): string {
|
|
36
|
+
const base = tool.path.length > 0 ? `${root.key} ${tool.path.join(" ")}` : root.key;
|
|
37
|
+
const slots = (tool.leaf.positionals ?? [])
|
|
38
|
+
.filter((p) => (p.argMin ?? 1) > 0 && (p.argMax ?? 1) === 1)
|
|
39
|
+
.map((p) => `<${p.name}>`);
|
|
40
|
+
if (slots.length === 0) {
|
|
41
|
+
return base;
|
|
42
|
+
}
|
|
43
|
+
return `${base} ${slots.join(" ")}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Formats one command line for the SKILL.md index (details live in reference.md). */
|
|
47
|
+
function formatCommandEntry(root: CliProgram, tool: McpToolDef): string {
|
|
48
|
+
const cliPath = commandCatalogPath(root, tool);
|
|
49
|
+
let line = `- **\`${cliPath}\`** — ${tool.leaf.description}`;
|
|
50
|
+
const opts = collectOptionDefs(root, tool.path);
|
|
51
|
+
const flags = opts.filter((o) => o.kind === CliOptionKind.Presence).map((o) => `--${o.name}`);
|
|
52
|
+
if (flags.length > 0) {
|
|
53
|
+
line += ` (flags: ${flags.join(", ")})`;
|
|
54
|
+
}
|
|
55
|
+
const enums = opts.filter((o) => o.kind === CliOptionKind.Enum && o.choices?.length);
|
|
56
|
+
for (const e of enums) {
|
|
57
|
+
line += ` (\`--${e.name}\`: ${e.choices!.join(" | ")})`;
|
|
58
|
+
}
|
|
59
|
+
const varargs = (tool.leaf.positionals ?? []).filter((p) => (p.argMax ?? 1) === 0);
|
|
60
|
+
if (varargs.length > 0) {
|
|
61
|
+
line += ` (varargs: ${varargs.map((p) => p.name).join(", ")})`;
|
|
62
|
+
}
|
|
63
|
+
const env = tool.leaf.mcpTool?.requiresEnv;
|
|
64
|
+
if (env && env.length > 0) {
|
|
65
|
+
line += ` [requires env: ${env.join(", ")}]`;
|
|
66
|
+
}
|
|
67
|
+
return line;
|
|
68
|
+
}
|
|
69
|
+
|
|
34
70
|
/** Builds SKILL.md body for the given target. */
|
|
35
71
|
function buildSkillMd(root: CliProgram, target: SkillTarget, dirName: string): string {
|
|
36
72
|
const name = sanitizeToolSegment(root.key);
|
|
37
73
|
const description = skillDescription(root);
|
|
74
|
+
const tools = collectMcpTools(root);
|
|
38
75
|
|
|
39
76
|
const lines: string[] = [
|
|
40
77
|
"---",
|
|
@@ -46,10 +83,6 @@ function buildSkillMd(root: CliProgram, target: SkillTarget, dirName: string): s
|
|
|
46
83
|
"",
|
|
47
84
|
root.description,
|
|
48
85
|
"",
|
|
49
|
-
"## When to use",
|
|
50
|
-
"",
|
|
51
|
-
`Use this skill when working with **${root.key}** — shell commands and automation for this application.`,
|
|
52
|
-
"",
|
|
53
86
|
"## Execution",
|
|
54
87
|
"",
|
|
55
88
|
"Invoke via shell:",
|
|
@@ -58,18 +91,30 @@ function buildSkillMd(root: CliProgram, target: SkillTarget, dirName: string): s
|
|
|
58
91
|
`${root.key} <subcommand> [options] [args]`,
|
|
59
92
|
"```",
|
|
60
93
|
"",
|
|
61
|
-
|
|
94
|
+
"## Commands",
|
|
62
95
|
"",
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
if (tools.length === 0) {
|
|
99
|
+
lines.push("(No leaf commands in schema.)", "");
|
|
100
|
+
} else {
|
|
101
|
+
for (const tool of tools) {
|
|
102
|
+
lines.push(formatCommandEntry(root, tool));
|
|
103
|
+
}
|
|
104
|
+
lines.push("");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
lines.push(
|
|
63
108
|
"## Pitfalls",
|
|
64
109
|
"",
|
|
65
|
-
"-
|
|
66
|
-
"-
|
|
110
|
+
"- Pass `--` before arguments that look like flags.",
|
|
111
|
+
"- Commands marked `[requires env: ...]` need those variables set in the shell.",
|
|
67
112
|
"",
|
|
68
113
|
"## Reference",
|
|
69
114
|
"",
|
|
70
|
-
|
|
115
|
+
`For full detail, open \`reference.md\` in this skill directory (same as \`${root.key} docs api\`).`,
|
|
71
116
|
"",
|
|
72
|
-
|
|
117
|
+
);
|
|
73
118
|
|
|
74
119
|
if (target === "cursor") {
|
|
75
120
|
lines.push(
|
|
@@ -96,18 +141,9 @@ function buildSkillMd(root: CliProgram, target: SkillTarget, dirName: string): s
|
|
|
96
141
|
return lines.join("\n");
|
|
97
142
|
}
|
|
98
143
|
|
|
99
|
-
/** Builds reference.md with
|
|
144
|
+
/** Builds reference.md with the full `docs api` markdown guide. */
|
|
100
145
|
function buildReferenceMd(root: CliProgram): string {
|
|
101
|
-
return
|
|
102
|
-
`# ${root.key} — CLI reference`,
|
|
103
|
-
"",
|
|
104
|
-
"Generated from the program `docs schema` export. Handlers and runtime-only nodes are omitted.",
|
|
105
|
-
"",
|
|
106
|
-
"```json",
|
|
107
|
-
cliSchemaJson(root).trimEnd(),
|
|
108
|
-
"```",
|
|
109
|
-
"",
|
|
110
|
-
].join("\n");
|
|
146
|
+
return generateApiGuide(root);
|
|
111
147
|
}
|
|
112
148
|
|
|
113
149
|
/** Generates SKILL.md and reference.md for Cursor or Claude Code. */
|