argsbarg 3.3.3 → 3.3.4
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 +10 -2
- package/docs/bundled-docs.md +3 -7
- package/package.json +1 -1
- package/src/docs/builtin.ts +3 -6
- package/src/docs/docs.test.ts +21 -26
- package/src/docs/resolve.ts +1 -21
- package/src/docs/save.ts +28 -22
package/CHANGELOG.md
CHANGED
|
@@ -7,11 +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.4] - 2026-06-21
|
|
11
|
+
|
|
12
|
+
|
|
10
13
|
## [3.3.3] - 2026-06-21
|
|
11
14
|
|
|
12
15
|
### Added
|
|
13
16
|
|
|
14
|
-
- **`docs --save`** — write
|
|
17
|
+
- **`docs --save`** — write one docs subcommand to `./docs/`; argsbarg-generated markdown (`mcp`, `api`, `skill`) is prefixed with a `Generated by … docs … --save` HTML comment.
|
|
18
|
+
|
|
19
|
+
### Removed
|
|
20
|
+
|
|
21
|
+
- **`docs all`** — use individual subcommands (`docs readme`, `docs schema`, `docs api`, …) or `--save` per topic.
|
|
15
22
|
|
|
16
23
|
## [3.3.2] - 2026-06-21
|
|
17
24
|
|
|
@@ -255,7 +262,8 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
|
|
|
255
262
|
- 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`).
|
|
256
263
|
- Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
|
|
257
264
|
|
|
258
|
-
[Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v3.3.
|
|
265
|
+
[Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v3.3.4...HEAD
|
|
266
|
+
[3.3.4]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.4
|
|
259
267
|
[3.3.3]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.3
|
|
260
268
|
[3.3.2]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.2
|
|
261
269
|
[3.3.1]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.1
|
package/docs/bundled-docs.md
CHANGED
|
@@ -30,10 +30,9 @@ myapp docs architecture
|
|
|
30
30
|
myapp docs schema # full command tree as JSON
|
|
31
31
|
myapp docs api # command tree as markdown
|
|
32
32
|
myapp docs skill # generated Cursor SKILL.md
|
|
33
|
-
myapp docs all # all user topics; includes auto mcp when MCP enabled
|
|
34
33
|
myapp docs mcp # auto-generated when mcpServer.enabled
|
|
35
34
|
myapp docs readme --save # write ./docs/readme.md
|
|
36
|
-
myapp docs
|
|
35
|
+
myapp docs schema --save # write ./docs/schema.json
|
|
37
36
|
```
|
|
38
37
|
|
|
39
38
|
## Configuration
|
|
@@ -45,7 +44,7 @@ myapp docs all --save # write ./docs/<topic>.md for each bundled topic
|
|
|
45
44
|
| `defaultTopic` | first key in `topics` | `fallbackCommand` for bare `myapp docs` |
|
|
46
45
|
| `topics` | *(required)* | Topic key → `{ text, description? }` |
|
|
47
46
|
|
|
48
|
-
Reserved topic keys in `topics`: **`mcp`**, **`all`**, **`schema`**, **`api`**, **`skill`** (
|
|
47
|
+
Reserved topic keys in `topics`: **`mcp`**, **`all`**, **`schema`**, **`api`**, **`skill`** (reserved — use the matching `docs <name>` subcommand instead).
|
|
49
48
|
|
|
50
49
|
When `description` is omitted on a topic, ArgsBarg generates leaf help (`readme` → "Print README (user guide).").
|
|
51
50
|
|
|
@@ -102,8 +101,5 @@ Pass **`--save`** on `docs` or any docs subcommand to write files under **`./doc
|
|
|
102
101
|
| `docs schema --save` | `./docs/schema.json` |
|
|
103
102
|
| `docs api --save` | `./docs/api.md` |
|
|
104
103
|
| `docs skill --save` | `./docs/skill.md` |
|
|
105
|
-
| `docs all --save` | one file per bundled topic (`readme.md`, `mcp.md`, …) — not a combined file |
|
|
106
104
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
Argsbarg-generated markdown (`mcp`, `api`, `skill`) is prefixed with an HTML comment: `<!-- Generated by myapp docs api --save; do not edit. -->`. Consumer topic files and `schema.json` are written as-is.
|
|
105
|
+
Argsbarg-generated markdown (`mcp`, `api`, `skill`) includes a `Generated by … docs … --save` HTML comment (`skill` places it after YAML frontmatter so parsers still work). Consumer topic files and `schema.json` are written as-is.
|
package/package.json
CHANGED
package/src/docs/builtin.ts
CHANGED
|
@@ -8,19 +8,17 @@ import {
|
|
|
8
8
|
docsUserTopicKeys,
|
|
9
9
|
printDocsTopic,
|
|
10
10
|
} from "./resolve.ts";
|
|
11
|
-
import {
|
|
11
|
+
import { saveDocsTopic } from "./save.ts";
|
|
12
12
|
|
|
13
13
|
const DOCS_SAVE_OPTION: CliOption = {
|
|
14
14
|
name: "save",
|
|
15
|
-
description: "Write documentation to ./docs
|
|
15
|
+
description: "Write documentation to ./docs/.",
|
|
16
16
|
kind: CliOptionKind.Presence,
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
function runDocsTopic(program: CliProgram, topic: string, ctx: { hasFlag(name: string): boolean }): void {
|
|
20
20
|
if (ctx.hasFlag("save")) {
|
|
21
|
-
|
|
22
|
-
process.stdout.write(`${path}\n`);
|
|
23
|
-
}
|
|
21
|
+
process.stdout.write(`${saveDocsTopic(program, topic)}\n`);
|
|
24
22
|
return;
|
|
25
23
|
}
|
|
26
24
|
printDocsTopic(program, topic);
|
|
@@ -58,7 +56,6 @@ export function cliBuiltinDocsGroup(program: CliProgram): CliRouter {
|
|
|
58
56
|
docsLeaf(program, "schema", "Print the full command tree as JSON."),
|
|
59
57
|
docsLeaf(program, "api", "Print the command tree as markdown."),
|
|
60
58
|
docsLeaf(program, "skill", "Print generated Cursor SKILL.md content."),
|
|
61
|
-
docsLeaf(program, "all", "Print all bundled documentation combined."),
|
|
62
59
|
);
|
|
63
60
|
|
|
64
61
|
return {
|
package/src/docs/docs.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test, beforeEach, afterEach } from "bun:test";
|
|
2
|
-
import {
|
|
2
|
+
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { cliPresentationRoot } from "../builtins/presentation.ts";
|
|
@@ -7,9 +7,9 @@ import { completionBashScript } from "../completion.ts";
|
|
|
7
7
|
import { cliInvoke } from "../index.ts";
|
|
8
8
|
import type { CliProgram } from "../types.ts";
|
|
9
9
|
import { cliValidateProgram } from "../validate.ts";
|
|
10
|
-
import {
|
|
10
|
+
import { docsEffectiveDefaultTopic } from "./resolve.ts";
|
|
11
11
|
import { generateMcpGuide } from "./mcp-guide.ts";
|
|
12
|
-
import {
|
|
12
|
+
import { saveDocsTopic } from "./save.ts";
|
|
13
13
|
|
|
14
14
|
let workDir: string;
|
|
15
15
|
let prevCwd: string;
|
|
@@ -104,6 +104,11 @@ test("docs mcp when MCP enabled", async () => {
|
|
|
104
104
|
expect(result.stdout).toContain("myapp mcp");
|
|
105
105
|
});
|
|
106
106
|
|
|
107
|
+
test("docs rejects unknown subcommand", async () => {
|
|
108
|
+
const result = await cliInvoke(docsFixture(), ["docs", "all"]);
|
|
109
|
+
expect(result.exitCode).not.toBe(0);
|
|
110
|
+
});
|
|
111
|
+
|
|
107
112
|
test("docs mcp absent from router when MCP disabled", async () => {
|
|
108
113
|
const root = docsFixture(false);
|
|
109
114
|
const presentation = cliPresentationRoot(root);
|
|
@@ -116,14 +121,6 @@ test("docs mcp absent from router when MCP disabled", async () => {
|
|
|
116
121
|
expect(result.exitCode).not.toBe(0);
|
|
117
122
|
});
|
|
118
123
|
|
|
119
|
-
test("docs all concatenates user topics and mcp", () => {
|
|
120
|
-
const program = docsFixture(true);
|
|
121
|
-
const combined = combineAllDocs(program);
|
|
122
|
-
expect(combined).toContain("Hello README");
|
|
123
|
-
expect(combined).toContain("Architecture");
|
|
124
|
-
expect(combined).toContain("MCP server (myapp)");
|
|
125
|
-
});
|
|
126
|
-
|
|
127
124
|
test("presentation includes docs subtree", () => {
|
|
128
125
|
const presentation = cliPresentationRoot(docsFixture());
|
|
129
126
|
const docsNode = presentation.commands.find((c) => c.key === "docs");
|
|
@@ -203,6 +200,16 @@ test("docs api --save prepends generated hint", async () => {
|
|
|
203
200
|
expect(text).toContain("CLI API reference");
|
|
204
201
|
});
|
|
205
202
|
|
|
203
|
+
test("docs skill --save keeps frontmatter first", async () => {
|
|
204
|
+
const result = await cliInvoke(docsFixture(), ["docs", "skill", "--save"]);
|
|
205
|
+
expect(result.exitCode).toBe(0);
|
|
206
|
+
const text = readFileSync(join(workDir, "docs/skill.md"), "utf8");
|
|
207
|
+
expect(text.startsWith("---\n")).toBe(true);
|
|
208
|
+
expect(text).toContain("name: myapp");
|
|
209
|
+
const hint = "<!-- Generated by myapp docs skill --save; do not edit. -->";
|
|
210
|
+
expect(text.indexOf(hint)).toBeGreaterThan(text.indexOf("---\n", 4));
|
|
211
|
+
});
|
|
212
|
+
|
|
206
213
|
test("docs schema --save writes JSON file", async () => {
|
|
207
214
|
const result = await cliInvoke(docsFixture(), ["docs", "schema", "--save"]);
|
|
208
215
|
expect(result.exitCode).toBe(0);
|
|
@@ -213,21 +220,9 @@ test("docs schema --save writes JSON file", async () => {
|
|
|
213
220
|
expect(schema.key).toBe("myapp");
|
|
214
221
|
});
|
|
215
222
|
|
|
216
|
-
test("
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
expect(result.exitCode).toBe(0);
|
|
220
|
-
const lines = result.stdout.trim().split("\n");
|
|
221
|
-
expect(lines).toEqual(docsTopicsToSave(program, "all").map((key) => `docs/${key}.md`));
|
|
222
|
-
expect(readFileSync(join(workDir, "docs/readme.md"), "utf8")).not.toContain("Generated by");
|
|
223
|
-
expect(readFileSync(join(workDir, "docs/mcp.md"), "utf8")).toContain(
|
|
224
|
-
"<!-- Generated by myapp docs mcp --save; do not edit. -->",
|
|
225
|
-
);
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
test("saveDocsTopics returns relative paths", () => {
|
|
229
|
-
const paths = saveDocsTopics(docsFixture(), "api");
|
|
230
|
-
expect(paths).toEqual(["docs/api.md"]);
|
|
223
|
+
test("saveDocsTopic returns relative path", () => {
|
|
224
|
+
const path = saveDocsTopic(docsFixture(), "api");
|
|
225
|
+
expect(path).toBe("docs/api.md");
|
|
231
226
|
const text = readFileSync(join(workDir, "docs/api.md"), "utf8");
|
|
232
227
|
expect(text).toContain("<!-- Generated by myapp docs api --save; do not edit. -->");
|
|
233
228
|
expect(text).toContain("CLI API reference");
|
package/src/docs/resolve.ts
CHANGED
|
@@ -51,16 +51,6 @@ export function docsTopicDescription(key: string, custom?: string): string {
|
|
|
51
51
|
return `Print ${label} documentation.`;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
/** Ordered keys for `docs all` (user topics, then auto `mcp` when enabled). */
|
|
55
|
-
export function docsPrintOrder(program: CliProgram): string[] {
|
|
56
|
-
const docs = program.docs!;
|
|
57
|
-
const order = docsUserTopicKeys(docs);
|
|
58
|
-
if (docsIncludesMcpTopic(program)) {
|
|
59
|
-
order.push("mcp");
|
|
60
|
-
}
|
|
61
|
-
return order;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
54
|
/** Markdown body for one docs topic key. */
|
|
65
55
|
export function docsTopicText(program: CliProgram, topic: string): string {
|
|
66
56
|
const docs = program.docs!;
|
|
@@ -88,21 +78,11 @@ export function docsTopicContent(program: CliProgram, topic: string): string {
|
|
|
88
78
|
if (topic === "skill") {
|
|
89
79
|
return `${generateSkillBundle(program, "cursor").skillMd}\n`;
|
|
90
80
|
}
|
|
91
|
-
if (topic === "all") {
|
|
92
|
-
return `${combineAllDocs(program)}\n`;
|
|
93
|
-
}
|
|
94
81
|
const text = docsTopicText(program, topic);
|
|
95
82
|
return text.endsWith("\n") ? text : `${text}\n`;
|
|
96
83
|
}
|
|
97
84
|
|
|
98
|
-
/**
|
|
99
|
-
export function combineAllDocs(program: CliProgram): string {
|
|
100
|
-
return docsPrintOrder(program)
|
|
101
|
-
.map((key) => docsTopicText(program, key).trim())
|
|
102
|
-
.join("\n\n---\n\n");
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/** Writes one docs topic (or `all`) to stdout. */
|
|
85
|
+
/** Writes one docs topic to stdout. */
|
|
106
86
|
export function printDocsTopic(program: CliProgram, topic: string): void {
|
|
107
87
|
process.stdout.write(docsTopicContent(program, topic));
|
|
108
88
|
}
|
package/src/docs/save.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import type { CliProgram } from "../types.ts";
|
|
4
|
-
import {
|
|
4
|
+
import { docsTopicContent } from "./resolve.ts";
|
|
5
5
|
|
|
6
6
|
/** Relative output directory for `docs --save`. */
|
|
7
7
|
export const DOCS_SAVE_DIR = "docs";
|
|
@@ -14,18 +14,31 @@ export function docsTopicIsGeneratedByArgsbarg(topic: string): boolean {
|
|
|
14
14
|
return (DOCS_GENERATED_SAVE_TOPICS as readonly string[]).includes(topic);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
/** HTML comment
|
|
17
|
+
/** HTML comment for generated markdown saved with `--save`. */
|
|
18
18
|
export function docsSaveGeneratedHint(program: CliProgram, topic: string): string {
|
|
19
19
|
return `<!-- Generated by ${program.key} docs ${topic} --save; do not edit. -->\n\n`;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
const SKILL_FRONTMATTER_RE = /^---\r?\n[\s\S]*?\r?\n---\r?\n/;
|
|
23
|
+
|
|
24
|
+
/** Inserts save hint without breaking YAML frontmatter (`docs skill`). */
|
|
25
|
+
export function applySaveGeneratedHint(program: CliProgram, topic: string, content: string): string {
|
|
25
26
|
if (!docsTopicIsGeneratedByArgsbarg(topic)) {
|
|
26
27
|
return content;
|
|
27
28
|
}
|
|
28
|
-
|
|
29
|
+
const hint = docsSaveGeneratedHint(program, topic);
|
|
30
|
+
if (topic === "skill") {
|
|
31
|
+
const match = content.match(SKILL_FRONTMATTER_RE);
|
|
32
|
+
if (match) {
|
|
33
|
+
return `${match[0]}${hint}${content.slice(match[0].length)}`;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return `${hint}${content}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** File body for `--save` (hint on argsbarg-generated markdown only). */
|
|
40
|
+
export function docsTopicContentForSave(program: CliProgram, topic: string): string {
|
|
41
|
+
return applySaveGeneratedHint(program, topic, docsTopicContent(program, topic));
|
|
29
42
|
}
|
|
30
43
|
|
|
31
44
|
/** Filename for a saved docs topic. */
|
|
@@ -36,25 +49,18 @@ export function docsSaveFilename(topic: string): string {
|
|
|
36
49
|
return `${topic}.md`;
|
|
37
50
|
}
|
|
38
51
|
|
|
39
|
-
/**
|
|
40
|
-
export function
|
|
41
|
-
|
|
42
|
-
return docsPrintOrder(program);
|
|
43
|
-
}
|
|
44
|
-
return [topic];
|
|
52
|
+
/** Relative path under cwd for a saved docs topic. */
|
|
53
|
+
export function docsSaveRelativePath(topic: string): string {
|
|
54
|
+
return join(DOCS_SAVE_DIR, docsSaveFilename(topic));
|
|
45
55
|
}
|
|
46
56
|
|
|
47
|
-
/** Writes docs topic
|
|
48
|
-
export function
|
|
57
|
+
/** Writes one docs topic under `./docs/`; returns relative path written. */
|
|
58
|
+
export function saveDocsTopic(program: CliProgram, topic: string): string {
|
|
49
59
|
const dir = join(process.cwd(), DOCS_SAVE_DIR);
|
|
50
60
|
mkdirSync(dir, { recursive: true });
|
|
51
61
|
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
writeFileSync(abs, docsTopicContentForSave(program, key), "utf8");
|
|
57
|
-
saved.push(rel);
|
|
58
|
-
}
|
|
59
|
-
return saved;
|
|
62
|
+
const rel = docsSaveRelativePath(topic);
|
|
63
|
+
const abs = join(process.cwd(), rel);
|
|
64
|
+
writeFileSync(abs, docsTopicContentForSave(program, topic), "utf8");
|
|
65
|
+
return rel;
|
|
60
66
|
}
|