argsbarg 3.3.4 → 3.3.6
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 +23 -1
- package/docs/ai-skills.md +2 -0
- package/docs/bundled-docs.md +1 -1
- package/examples/nested.ts +1 -1
- package/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/builtins/install.ts +6 -5
- package/src/docs/api-guide.test.ts +30 -0
- package/src/docs/api-guide.ts +12 -3
- package/src/docs/save.ts +3 -10
- package/src/help.ts +9 -6
- package/src/index.test.ts +46 -0
- package/src/schema.ts +17 -4
- package/src/skill/hint.ts +42 -0
- package/src/skill/install.ts +4 -2
- package/src/types.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [3.3.6] - 2026-06-21
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`install --skill`** — installed `SKILL.md` and `reference.md` include a `Generated by … install --skill` HTML comment (after SKILL.md frontmatter).
|
|
15
|
+
|
|
16
|
+
## [3.3.5] - 2026-06-21
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- **`notes` placeholders** — use `{argsbarg:program}` for the root program key in consumer `notes`. Built-in copy (e.g. `install` notes) interpolates the program key directly.
|
|
21
|
+
|
|
22
|
+
### Removed
|
|
23
|
+
|
|
24
|
+
- **`{app}` notes placeholder** — use `{argsbarg:program}` instead.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- **`docs schema` / `docs api` / MCP schema resource** — `{argsbarg:program}` in `notes` is resolved to the program key (same as help). Schema export uses the root program key for built-in subtrees on nested leaves.
|
|
29
|
+
|
|
10
30
|
## [3.3.4] - 2026-06-21
|
|
11
31
|
|
|
12
32
|
|
|
@@ -262,7 +282,9 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
|
|
|
262
282
|
- 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`).
|
|
263
283
|
- Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
|
|
264
284
|
|
|
265
|
-
[Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v3.3.
|
|
285
|
+
[Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v3.3.6...HEAD
|
|
286
|
+
[3.3.6]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.6
|
|
287
|
+
[3.3.5]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.5
|
|
266
288
|
[3.3.4]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.4
|
|
267
289
|
[3.3.3]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.3
|
|
268
290
|
[3.3.2]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.2
|
package/docs/ai-skills.md
CHANGED
|
@@ -34,6 +34,8 @@ For library use, call `cliSkillInstall(root, "cursor" | "claude", { global: true
|
|
|
34
34
|
- **`SKILL.md`** — YAML frontmatter, when-to-use guidance, shell command catalog, pitfalls
|
|
35
35
|
- **`reference.md`** — full `docs schema` JSON export
|
|
36
36
|
|
|
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
|
+
|
|
37
39
|
Skills describe **shell invocation only** — no MCP setup, `mcp.json`, or `tools/call` guidance. Use **`myapp docs mcp`** (when `docs` and `mcpServer` are enabled) or connect the MCP server for agent execution.
|
|
38
40
|
|
|
39
41
|
## MCP vs skills vs docs
|
package/docs/bundled-docs.md
CHANGED
|
@@ -102,4 +102,4 @@ Pass **`--save`** on `docs` or any docs subcommand to write files under **`./doc
|
|
|
102
102
|
| `docs api --save` | `./docs/api.md` |
|
|
103
103
|
| `docs skill --save` | `./docs/skill.md` |
|
|
104
104
|
|
|
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).
|
|
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). `schema.json` and argsbarg-generated markdown resolve `{argsbarg:program}` in `notes` to the program key. Consumer-authored topic files are written as-is.
|
package/examples/nested.ts
CHANGED
|
@@ -79,7 +79,7 @@ const cli = {
|
|
|
79
79
|
{
|
|
80
80
|
key: "read",
|
|
81
81
|
description: "Print the first line of each file.",
|
|
82
|
-
notes: "Pass one or more file paths.
|
|
82
|
+
notes: "Pass one or more file paths. The program prints the first line of each.",
|
|
83
83
|
positionals: [
|
|
84
84
|
{
|
|
85
85
|
name: "files",
|
package/index.d.ts
CHANGED
|
@@ -224,7 +224,7 @@ export interface CliNodeBase {
|
|
|
224
224
|
key: string;
|
|
225
225
|
/** Short description shown in help. */
|
|
226
226
|
description: string;
|
|
227
|
-
/** Additional notes shown in help (
|
|
227
|
+
/** Additional notes shown in help (`{argsbarg:program}` → program key). */
|
|
228
228
|
notes?: string;
|
|
229
229
|
/** Global or command-level flags/options. */
|
|
230
230
|
options?: CliOption[];
|
package/package.json
CHANGED
package/src/builtins/install.ts
CHANGED
|
@@ -85,19 +85,20 @@ export function installBuiltinOptions(root: CliProgram): CliOption[] {
|
|
|
85
85
|
|
|
86
86
|
/** Builds the `install` built-in command. */
|
|
87
87
|
export function cliBuiltinInstallCommand(root: CliProgram): CliLeaf {
|
|
88
|
+
const app = root.key;
|
|
88
89
|
return {
|
|
89
90
|
key: "install",
|
|
90
91
|
description: "Install the binary, shell completions, agent skills, and MCP config to your user environment.",
|
|
91
92
|
notes:
|
|
92
93
|
"First-time setup:\n" +
|
|
93
|
-
` {app} install --all --yes\n\n` +
|
|
94
|
+
` ${app} install --all --yes\n\n` +
|
|
94
95
|
"Refresh after upgrading:\n" +
|
|
95
|
-
` {app} install --reinstall\n` +
|
|
96
|
-
` {app} update\n\n` +
|
|
96
|
+
` ${app} install --reinstall\n` +
|
|
97
|
+
` ${app} update\n\n` +
|
|
97
98
|
"See what is installed:\n" +
|
|
98
|
-
` {app} install --status\n\n` +
|
|
99
|
+
` ${app} install --status\n\n` +
|
|
99
100
|
"Remove everything installed with --all:\n" +
|
|
100
|
-
` {app} install --uninstall --all --yes\n\n` +
|
|
101
|
+
` ${app} install --uninstall --all --yes\n\n` +
|
|
101
102
|
"Use --dry to preview changes, --json for machine-readable output.",
|
|
102
103
|
options: installBuiltinOptions(root),
|
|
103
104
|
handler: () => {},
|
|
@@ -53,3 +53,33 @@ test("generateApiGuide covers the same command keys as cliSchemaExport", () => {
|
|
|
53
53
|
expect(md).toContain("`<path>`");
|
|
54
54
|
expect(schema.commands?.map((c) => c.key)).toEqual(["stat"]);
|
|
55
55
|
});
|
|
56
|
+
|
|
57
|
+
test("generateApiGuide resolves program key in install notes", () => {
|
|
58
|
+
const fixture: CliProgram = {
|
|
59
|
+
key: "myapp",
|
|
60
|
+
version: "1.0.0",
|
|
61
|
+
description: "Demo app.",
|
|
62
|
+
commands: [{ key: "run", description: "Run.", handler: () => {} }],
|
|
63
|
+
};
|
|
64
|
+
const md = generateApiGuide(fixture);
|
|
65
|
+
expect(md).not.toContain("{argsbarg:program}");
|
|
66
|
+
expect(md).toContain("myapp install --all --yes");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("generateApiGuide resolves {argsbarg:program} in consumer notes", () => {
|
|
70
|
+
const fixture: CliProgram = {
|
|
71
|
+
key: "myapp",
|
|
72
|
+
version: "1.0.0",
|
|
73
|
+
description: "Demo app.",
|
|
74
|
+
commands: [
|
|
75
|
+
{
|
|
76
|
+
key: "run",
|
|
77
|
+
description: "Run.",
|
|
78
|
+
notes: "Invoke `{argsbarg:program} run`.",
|
|
79
|
+
handler: () => {},
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
};
|
|
83
|
+
const md = generateApiGuide(fixture);
|
|
84
|
+
expect(md).toContain("Invoke `myapp run`.");
|
|
85
|
+
});
|
package/src/docs/api-guide.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CliSchemaExport } from "../builtins/export.ts";
|
|
2
|
-
import { cliPositionalLabel } from "../help.ts";
|
|
2
|
+
import { cliPositionalLabel, cliResolveNotes } from "../help.ts";
|
|
3
3
|
import { cliSchemaExport } from "../schema.ts";
|
|
4
4
|
import type { CliOption, CliPositional, CliProgram } from "../types.ts";
|
|
5
5
|
import { CliFallbackMode, CliOptionKind } from "../types.ts";
|
|
@@ -43,6 +43,15 @@ function formatPositionalRow(p: CliPositional): string {
|
|
|
43
43
|
return `| \`${label}\` | ${p.kind} | ${req} | ${p.description} |`;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
/** Markdown blockquote for command notes (`{argsbarg:program}` resolved to root key). */
|
|
47
|
+
function formatNotesBlockquote(notes: string, appKey: string): string {
|
|
48
|
+
const resolved = cliResolveNotes(notes, appKey);
|
|
49
|
+
return resolved
|
|
50
|
+
.split("\n")
|
|
51
|
+
.map((line) => `> ${line}`)
|
|
52
|
+
.join("\n");
|
|
53
|
+
}
|
|
54
|
+
|
|
46
55
|
/** Fallback routing note when present on a router node. */
|
|
47
56
|
function fallbackLine(node: CliSchemaExport): string | null {
|
|
48
57
|
if (node.fallbackCommand === undefined) {
|
|
@@ -66,7 +75,7 @@ function renderCommandNode(
|
|
|
66
75
|
lines.push(`${heading} \`${cmd}\``, "", node.description, "");
|
|
67
76
|
|
|
68
77
|
if (node.notes) {
|
|
69
|
-
lines.push(
|
|
78
|
+
lines.push(formatNotesBlockquote(node.notes, rootKey), "");
|
|
70
79
|
}
|
|
71
80
|
|
|
72
81
|
const fb = fallbackLine(node);
|
|
@@ -121,7 +130,7 @@ export function generateApiGuide(program: CliProgram): string {
|
|
|
121
130
|
];
|
|
122
131
|
|
|
123
132
|
if (schema.notes) {
|
|
124
|
-
lines.push(
|
|
133
|
+
lines.push(formatNotesBlockquote(schema.notes, program.key), "");
|
|
125
134
|
}
|
|
126
135
|
|
|
127
136
|
renderCommandNode(program.key, [], schema, lines);
|
package/src/docs/save.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { mkdirSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import type { CliProgram } from "../types.ts";
|
|
4
4
|
import { docsTopicContent } from "./resolve.ts";
|
|
5
|
+
import { generatedFileHtmlComment, insertGeneratedHint } from "../skill/hint.ts";
|
|
5
6
|
|
|
6
7
|
/** Relative output directory for `docs --save`. */
|
|
7
8
|
export const DOCS_SAVE_DIR = "docs";
|
|
@@ -16,24 +17,16 @@ export function docsTopicIsGeneratedByArgsbarg(topic: string): boolean {
|
|
|
16
17
|
|
|
17
18
|
/** HTML comment for generated markdown saved with `--save`. */
|
|
18
19
|
export function docsSaveGeneratedHint(program: CliProgram, topic: string): string {
|
|
19
|
-
return
|
|
20
|
+
return generatedFileHtmlComment(`${program.key} docs ${topic} --save`);
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
const SKILL_FRONTMATTER_RE = /^---\r?\n[\s\S]*?\r?\n---\r?\n/;
|
|
23
|
-
|
|
24
23
|
/** Inserts save hint without breaking YAML frontmatter (`docs skill`). */
|
|
25
24
|
export function applySaveGeneratedHint(program: CliProgram, topic: string, content: string): string {
|
|
26
25
|
if (!docsTopicIsGeneratedByArgsbarg(topic)) {
|
|
27
26
|
return content;
|
|
28
27
|
}
|
|
29
28
|
const hint = docsSaveGeneratedHint(program, topic);
|
|
30
|
-
|
|
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}`;
|
|
29
|
+
return insertGeneratedHint(content, hint, topic === "skill" ? { afterFrontmatter: true } : undefined);
|
|
37
30
|
}
|
|
38
31
|
|
|
39
32
|
/** File body for `--save` (hint on argsbarg-generated markdown only). */
|
package/src/help.ts
CHANGED
|
@@ -185,6 +185,14 @@ export function cliOptionLabel(o: CliOption, color: boolean): string {
|
|
|
185
185
|
return style.aquaBold(left) + " " + style.greenBright(right);
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
+
/** Placeholder in `notes` for the root program key (resolved in help, schema, and docs api). */
|
|
189
|
+
export const CLI_NOTES_PROGRAM = "{argsbarg:program}";
|
|
190
|
+
|
|
191
|
+
/** Replaces `{argsbarg:program}` in notes/help text with the program key. */
|
|
192
|
+
export function cliResolveNotes(notes: string, appKey: string): string {
|
|
193
|
+
return notes.replaceAll(CLI_NOTES_PROGRAM, appKey);
|
|
194
|
+
}
|
|
195
|
+
|
|
188
196
|
/** Formats a positional slot label (`<n>`, `[n]`, or varargs) for help. */
|
|
189
197
|
export function cliPositionalLabel(p: CliPositional, color: boolean): string {
|
|
190
198
|
const { argMin = 1, argMax = 1 } = p;
|
|
@@ -471,12 +479,7 @@ export function cliHelpRender(schema: CliRouter, helpPath: string[], useStderr:
|
|
|
471
479
|
}
|
|
472
480
|
|
|
473
481
|
if ((node.notes ?? "").length > 0) {
|
|
474
|
-
|
|
475
|
-
while (true) {
|
|
476
|
-
const r = resolved.indexOf("{app}");
|
|
477
|
-
if (r === -1) break;
|
|
478
|
-
resolved = resolved.slice(0, r) + schema.key + resolved.slice(r + 5);
|
|
479
|
-
}
|
|
482
|
+
const resolved = cliResolveNotes(node.notes!, schema.key);
|
|
480
483
|
lines.push("");
|
|
481
484
|
lines.push(
|
|
482
485
|
renderTextBox("Notes", wrapText(resolved, hw - 4), hw, color).join("\n"),
|
package/src/index.test.ts
CHANGED
|
@@ -597,6 +597,44 @@ test("cliSchemaJson omits handlers and completion built-ins", () => {
|
|
|
597
597
|
expect(schema).not.toHaveProperty("handler");
|
|
598
598
|
});
|
|
599
599
|
|
|
600
|
+
test("cliSchemaExport resolves program key in install notes", () => {
|
|
601
|
+
const root = testProgram({
|
|
602
|
+
key: "myapp",
|
|
603
|
+
version: "1.0.0",
|
|
604
|
+
description: "demo",
|
|
605
|
+
commands: [
|
|
606
|
+
{
|
|
607
|
+
key: "run",
|
|
608
|
+
description: "run",
|
|
609
|
+
handler: () => {},
|
|
610
|
+
},
|
|
611
|
+
],
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
const json = cliSchemaJson(root);
|
|
615
|
+
expect(json).not.toContain("{argsbarg:program}");
|
|
616
|
+
expect(json).toContain("myapp install --all --yes");
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
test("cliSchemaExport resolves {argsbarg:program} in consumer notes", () => {
|
|
620
|
+
const root = testProgram({
|
|
621
|
+
key: "myapp",
|
|
622
|
+
version: "1.0.0",
|
|
623
|
+
description: "demo",
|
|
624
|
+
commands: [
|
|
625
|
+
{
|
|
626
|
+
key: "run",
|
|
627
|
+
description: "run",
|
|
628
|
+
notes: "Run `{argsbarg:program} run` to start.",
|
|
629
|
+
handler: () => {},
|
|
630
|
+
},
|
|
631
|
+
],
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
const schema = JSON.parse(cliSchemaJson(root));
|
|
635
|
+
expect(schema.commands[0].notes).toBe("Run `myapp run` to start.");
|
|
636
|
+
});
|
|
637
|
+
|
|
600
638
|
test("docs help lists schema, api, and skill subcommands", () => {
|
|
601
639
|
const root = testProgram({
|
|
602
640
|
key: "app",
|
|
@@ -1735,7 +1773,9 @@ test("generateSkillBundle includes frontmatter and command catalog", () => {
|
|
|
1735
1773
|
expect(bundle.skillMd).not.toContain("mcp.json");
|
|
1736
1774
|
expect(bundle.skillMd).not.toContain("Prefer MCP");
|
|
1737
1775
|
expect(bundle.skillMd).not.toContain("tools/call");
|
|
1776
|
+
expect(bundle.skillMd).not.toContain("Generated by");
|
|
1738
1777
|
expect(bundle.referenceMd).toContain("```json");
|
|
1778
|
+
expect(bundle.referenceMd).not.toContain("Generated by");
|
|
1739
1779
|
expect(() => JSON.parse(bundle.referenceMd.match(/```json\n([\s\S]*?)\n```/)![1]!)).not.toThrow();
|
|
1740
1780
|
});
|
|
1741
1781
|
|
|
@@ -1750,6 +1790,12 @@ test("cliSkillInstall writes project Cursor skill files", () => {
|
|
|
1750
1790
|
expect(existsSync(join(skillDir, "SKILL.md"))).toBe(true);
|
|
1751
1791
|
expect(existsSync(join(skillDir, "reference.md"))).toBe(true);
|
|
1752
1792
|
expect(readFileSync(join(skillDir, "SKILL.md"), "utf8")).toContain("stat owner lookup");
|
|
1793
|
+
const skillText = readFileSync(join(skillDir, "SKILL.md"), "utf8");
|
|
1794
|
+
expect(skillText.startsWith("---\n")).toBe(true);
|
|
1795
|
+
const hint = "<!-- Generated by nested.ts install --skill; do not edit. -->";
|
|
1796
|
+
expect(skillText.indexOf(hint)).toBeGreaterThan(skillText.indexOf("---\n", 4));
|
|
1797
|
+
const refText = readFileSync(join(skillDir, "reference.md"), "utf8");
|
|
1798
|
+
expect(refText.startsWith(hint)).toBe(true);
|
|
1753
1799
|
} finally {
|
|
1754
1800
|
process.chdir(prev);
|
|
1755
1801
|
rmSync(cwd, { recursive: true, force: true });
|
package/src/schema.ts
CHANGED
|
@@ -4,10 +4,11 @@ This module serializes the CLI schema tree to JSON for machine-readable introspe
|
|
|
4
4
|
|
|
5
5
|
import { type CliNode, type CliProgram, isCliLeaf, isCliRouter } from "./types.ts";
|
|
6
6
|
import { exportPresentationBuiltins, type CliSchemaExport } from "./builtins/export.ts";
|
|
7
|
+
import { cliResolveNotes } from "./help.ts";
|
|
7
8
|
|
|
8
9
|
const RESERVED = new Set(["completion", "install", "docs", "mcp", "version", "update"]);
|
|
9
10
|
|
|
10
|
-
function exportCommand(cmd: CliNode): CliSchemaExport {
|
|
11
|
+
function exportCommand(cmd: CliNode, root: CliProgram): CliSchemaExport {
|
|
11
12
|
const out: CliSchemaExport = {
|
|
12
13
|
key: cmd.key,
|
|
13
14
|
description: cmd.description,
|
|
@@ -25,7 +26,7 @@ function exportCommand(cmd: CliNode): CliSchemaExport {
|
|
|
25
26
|
if ((cmd.positionals ?? []).length > 0) {
|
|
26
27
|
out.positionals = cmd.positionals;
|
|
27
28
|
}
|
|
28
|
-
out.commands = exportPresentationBuiltins(
|
|
29
|
+
out.commands = exportPresentationBuiltins(root);
|
|
29
30
|
return out;
|
|
30
31
|
}
|
|
31
32
|
|
|
@@ -38,15 +39,27 @@ function exportCommand(cmd: CliNode): CliSchemaExport {
|
|
|
38
39
|
|
|
39
40
|
const children = isCliRouter(cmd) ? cmd.commands.filter((ch) => !RESERVED.has(ch.key)) : [];
|
|
40
41
|
if (children.length > 0) {
|
|
41
|
-
out.commands = children.map(exportCommand);
|
|
42
|
+
out.commands = children.map((ch) => exportCommand(ch, root));
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
return out;
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
/** Resolves `{argsbarg:program}` in exported notes using the root program key. */
|
|
49
|
+
function resolveSchemaNotes(node: CliSchemaExport, appKey: string): CliSchemaExport {
|
|
50
|
+
const out: CliSchemaExport = { ...node };
|
|
51
|
+
if ((out.notes ?? "").length > 0) {
|
|
52
|
+
out.notes = cliResolveNotes(out.notes!, appKey);
|
|
53
|
+
}
|
|
54
|
+
if (out.commands) {
|
|
55
|
+
out.commands = out.commands.map((ch) => resolveSchemaNotes(ch, appKey));
|
|
56
|
+
}
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
|
|
47
60
|
/** Returns the JSON-safe command tree (handlers omitted). */
|
|
48
61
|
export function cliSchemaExport(root: CliProgram): CliSchemaExport {
|
|
49
|
-
return exportCommand(root);
|
|
62
|
+
return resolveSchemaNotes(exportCommand(root, root), root.key);
|
|
50
63
|
}
|
|
51
64
|
|
|
52
65
|
export function cliSchemaJson(root: CliProgram): string {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { CliProgram } from "../types.ts";
|
|
2
|
+
|
|
3
|
+
/** YAML frontmatter block at the start of SKILL.md. */
|
|
4
|
+
export const MARKDOWN_FRONTMATTER_RE = /^---\r?\n[\s\S]*?\r?\n---\r?\n/;
|
|
5
|
+
|
|
6
|
+
/** HTML comment marking argsbarg-generated markdown. */
|
|
7
|
+
export function generatedFileHtmlComment(source: string): string {
|
|
8
|
+
return `<!-- Generated by ${source}; do not edit. -->\n\n`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Prepends a hint, or inserts after frontmatter when requested. */
|
|
12
|
+
export function insertGeneratedHint(
|
|
13
|
+
content: string,
|
|
14
|
+
hint: string,
|
|
15
|
+
options?: { afterFrontmatter?: boolean },
|
|
16
|
+
): string {
|
|
17
|
+
if (options?.afterFrontmatter) {
|
|
18
|
+
const match = content.match(MARKDOWN_FRONTMATTER_RE);
|
|
19
|
+
if (match) {
|
|
20
|
+
return `${match[0]}${hint}${content.slice(match[0].length)}`;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return `${hint}${content}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Hint for `install --skill` output files. */
|
|
27
|
+
export function skillInstallHint(program: CliProgram): string {
|
|
28
|
+
return generatedFileHtmlComment(`${program.key} install --skill`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Applies install hints to SKILL.md (after frontmatter) and reference.md. */
|
|
32
|
+
export function applySkillInstallHints(
|
|
33
|
+
program: CliProgram,
|
|
34
|
+
skillMd: string,
|
|
35
|
+
referenceMd: string,
|
|
36
|
+
): { skillMd: string; referenceMd: string } {
|
|
37
|
+
const hint = skillInstallHint(program);
|
|
38
|
+
return {
|
|
39
|
+
skillMd: insertGeneratedHint(skillMd, hint, { afterFrontmatter: true }),
|
|
40
|
+
referenceMd: insertGeneratedHint(referenceMd, hint),
|
|
41
|
+
};
|
|
42
|
+
}
|
package/src/skill/install.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { homedir } from "node:os";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { CliProgram } from "../types.ts";
|
|
5
5
|
import { generateSkillBundle, type SkillTarget } from "./generate.ts";
|
|
6
|
+
import { applySkillInstallHints } from "./hint.ts";
|
|
6
7
|
|
|
7
8
|
export interface SkillInstallOpts {
|
|
8
9
|
global?: boolean;
|
|
@@ -26,6 +27,7 @@ function resolveSkillDir(target: SkillTarget, dirName: string, global: boolean):
|
|
|
26
27
|
/** Writes SKILL.md and reference.md; returns changed file paths. */
|
|
27
28
|
export function cliSkillInstall(root: CliProgram, target: SkillTarget, opts: SkillInstallOpts): string[] {
|
|
28
29
|
const bundle = generateSkillBundle(root, target);
|
|
30
|
+
const { skillMd, referenceMd } = applySkillInstallHints(root, bundle.skillMd, bundle.referenceMd);
|
|
29
31
|
const dir = resolveSkillDir(target, bundle.dirName, opts.global ?? false);
|
|
30
32
|
const changed: string[] = [];
|
|
31
33
|
|
|
@@ -38,8 +40,8 @@ export function cliSkillInstall(root: CliProgram, target: SkillTarget, opts: Ski
|
|
|
38
40
|
|
|
39
41
|
if (!opts.dry) {
|
|
40
42
|
mkdirSync(dir, { recursive: true });
|
|
41
|
-
writeFileSync(skillPath,
|
|
42
|
-
writeFileSync(refPath,
|
|
43
|
+
writeFileSync(skillPath, skillMd, "utf8");
|
|
44
|
+
writeFileSync(refPath, referenceMd, "utf8");
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
changed.push(skillPath, refPath);
|
package/src/types.ts
CHANGED
|
@@ -213,7 +213,7 @@ export interface CliNodeBase {
|
|
|
213
213
|
key: string;
|
|
214
214
|
/** Short description shown in help. */
|
|
215
215
|
description: string;
|
|
216
|
-
/** Additional notes shown in help (
|
|
216
|
+
/** Additional notes shown in help (`{argsbarg:program}` → program key). */
|
|
217
217
|
notes?: string;
|
|
218
218
|
/** Global or command-level flags/options. */
|
|
219
219
|
options?: CliOption[];
|