argsbarg 3.3.9 → 3.3.11

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 CHANGED
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.3.11] - 2026-06-21
11
+
12
+ ### Added
13
+
14
+ - **Root help agent hint** — when `docs` is enabled, top-level `-h` includes a Notes line: `Agents: run \`myapp docs skill\` to learn how to use this app`. Root help also renders `program.notes`.
15
+
16
+ ## [3.3.10] - 2026-06-21
17
+
18
+ ### Changed
19
+
20
+ - **`install` help copy** — `--update` and `--quiet` option descriptions match behavior; `install --update` error message clarified.
21
+ - **`docs/install.md`** — quick-start and `--yes` flag docs aligned with install notes.
22
+
10
23
  ## [3.3.9] - 2026-06-21
11
24
 
12
25
  ### Changed
@@ -304,7 +317,9 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
304
317
  - 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`).
305
318
  - Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
306
319
 
307
- [Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v3.3.9...HEAD
320
+ [Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v3.3.11...HEAD
321
+ [3.3.11]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.11
322
+ [3.3.10]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.10
308
323
  [3.3.9]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.9
309
324
  [3.3.8]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.8
310
325
  [3.3.7]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.7
@@ -35,6 +35,8 @@ 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` includes a Notes line: `Agents: run \`myapp docs skill\` to learn how to use this app`.
39
+
38
40
  ## Configuration
39
41
 
40
42
  | Field | Default | Purpose |
package/docs/install.md CHANGED
@@ -11,7 +11,7 @@ myapp install --all --yes
11
11
  # Refresh after upgrading (re-copy running binary + refresh installed artifacts)
12
12
  myapp install --reinstall
13
13
 
14
- # Download latest release (when install.updateGetLatest is configured)
14
+ # Upgrade to latest release (when install.updateGetLatest is configured)
15
15
  myapp install --update
16
16
 
17
17
  # See what is installed
@@ -99,7 +99,7 @@ Environment:
99
99
 
100
100
  | Flag | Description |
101
101
  | --- | --- |
102
- | `--yes` | Skip confirmation (required for non-TTY unless `--json` / `--reinstall`) |
102
+ | `--yes` | Skip confirmation (required for non-TTY unless `--json`, `--reinstall`, or `--update`) |
103
103
  | `--dry` | Preview changes; per-step messages on stderr with `[dry run]` |
104
104
  | `--json` | Machine-readable output on stdout (implies `--yes`) |
105
105
  | `--quiet` | Suppress summaries and per-step messages (requires `--yes`) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "argsbarg",
3
- "version": "3.3.9",
3
+ "version": "3.3.11",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "bun": ">=1.3"
package/plan.md CHANGED
@@ -2,13 +2,13 @@
2
2
 
3
3
  ## Current Status
4
4
 
5
- **Overall**: Core CLI, MCP, install, docs, and update built-ins are complete. Public API is stable at **3.x**.
5
+ **Overall**: Core CLI, MCP, install, docs, and `install --update` are complete. Public API is stable at **3.x**.
6
6
 
7
7
  ### Shipped
8
8
 
9
9
  - Schema-driven parsing, help, completions, subcommand routing, fallback commands
10
10
  - MCP server (`mcpServer: { enabled: true }`), `ctx.invocation`, `cliInvoke`
11
- - `install` / `update` built-ins, agent skills, bundled `docs` (topics, schema, api, skill, mcp)
11
+ - `install` built-in (`install --update` when `updateGetLatest` is set), agent skills, bundled `docs` (topics, schema, api, skill, mcp)
12
12
  - Headless helpers and `ghReleaseUpdateGetLatest` for GitHub release consumers
13
13
 
14
14
  ### Consumers
@@ -37,6 +37,25 @@ describe("builtins help copy", () => {
37
37
  expect(names).not.toContain("mcp");
38
38
  });
39
39
 
40
+ test("install omits --update when updateGetLatest unset", () => {
41
+ const install = cliBuiltinInstallCommand(fixture);
42
+ expect(installBuiltinOptions(fixture).map((o) => o.name)).not.toContain("update");
43
+ expect(install.notes).not.toContain("Upgrade to latest release");
44
+ expect(install.notes).toContain("Refresh after upgrading");
45
+ });
46
+
47
+ test("install notes include upgrade section when updateGetLatest is set", () => {
48
+ const withUpdate: CliProgram = {
49
+ ...fixture,
50
+ install: { updateGetLatest: async () => ({ path: process.execPath }) },
51
+ };
52
+ const install = cliBuiltinInstallCommand(withUpdate);
53
+ const notes = install.notes ?? "";
54
+ expect(installBuiltinOptions(withUpdate).map((o) => o.name)).toContain("update");
55
+ expect(notes).toContain("Upgrade to latest release");
56
+ expect(notes.indexOf("install --reinstall")).toBeLessThan(notes.indexOf("install --update"));
57
+ });
58
+
40
59
  test("mcp builtin description is user-facing", () => {
41
60
  const mcp = cliBuiltinMcpCommand();
42
61
  expect(mcp.description).toContain("MCP server");
@@ -61,6 +80,15 @@ describe("presentation root", () => {
61
80
  const root = cliPresentationRoot(fixture);
62
81
  expect(root.commands?.map((c) => c.key)).toContain("version");
63
82
  });
83
+
84
+ test("root notes include agent hint when docs enabled", () => {
85
+ const withDocs: CliProgram = {
86
+ ...fixture,
87
+ docs: { enabled: true, topics: { readme: { text: "# r\n" } } },
88
+ };
89
+ const root = cliPresentationRoot(withDocs);
90
+ expect(root.notes).toContain("Agents: run `myapp docs skill` to learn how to use this app");
91
+ });
64
92
  });
65
93
 
66
94
  describe("completion emitters", () => {
@@ -67,7 +67,7 @@ export function installBuiltinOptions(root: CliProgram): CliOption[] {
67
67
  },
68
68
  {
69
69
  name: "quiet",
70
- description: "Suppress informational output (requires --yes).",
70
+ description: "Suppress informational output (requires --yes, --json, --reinstall, or --update).",
71
71
  kind: CliOptionKind.Presence,
72
72
  },
73
73
  ];
@@ -84,7 +84,7 @@ export function installBuiltinOptions(root: CliProgram): CliOption[] {
84
84
  const statusIdx = opts.findIndex((o) => o.name === "status");
85
85
  opts.splice(statusIdx, 0, {
86
86
  name: "update",
87
- description: "Download and install the latest release.",
87
+ description: "Download the latest release and reinstall installed artifacts.",
88
88
  kind: CliOptionKind.Presence,
89
89
  });
90
90
  }
@@ -34,11 +34,13 @@ export function presentationBuiltins(program: CliProgram, caps: CliCapabilities)
34
34
  export function cliPresentationRoot(program: CliProgram): CliRouter {
35
35
  const caps = resolveCapabilities(program);
36
36
  const builtins = presentationBuiltins(program, caps);
37
+ const notes = presentationRootNotes(program, caps);
37
38
 
38
39
  if (isCliLeaf(program)) {
39
40
  return {
40
41
  key: program.key,
41
42
  description: program.description,
43
+ notes,
42
44
  options: program.options,
43
45
  commands: builtins,
44
46
  };
@@ -47,7 +49,7 @@ export function cliPresentationRoot(program: CliProgram): CliRouter {
47
49
  return {
48
50
  key: program.key,
49
51
  description: program.description,
50
- notes: program.notes,
52
+ notes,
51
53
  options: program.options,
52
54
  fallbackCommand: program.fallbackCommand,
53
55
  fallbackMode: program.fallbackMode,
@@ -55,5 +57,22 @@ export function cliPresentationRoot(program: CliProgram): CliRouter {
55
57
  };
56
58
  }
57
59
 
60
+ /** Root help notes: consumer `program.notes` plus agent discovery when `docs` is enabled. */
61
+ export function presentationRootNotes(program: CliProgram, caps: CliCapabilities): string | undefined {
62
+ const parts: string[] = [];
63
+ if ((program.notes ?? "").trim().length > 0) {
64
+ parts.push(program.notes!.trim());
65
+ }
66
+ if (caps.docs) {
67
+ const cmd = `${program.key} docs skill`;
68
+ parts.push(`Agents: run \`${cmd}\` to learn how to use this app`);
69
+ }
70
+ if (parts.length === 0) {
71
+ return undefined;
72
+ }
73
+ return parts.join("\n\n");
74
+ }
75
+
58
76
  /** Presentation tree may include builtin leaf stubs. */
59
77
  export type CliPresentationNode = CliNode | CliLeaf;
78
+
@@ -64,6 +64,20 @@ test("generateApiGuide resolves program key in install notes", () => {
64
64
  const md = generateApiGuide(fixture);
65
65
  expect(md).not.toContain("{argsbarg:program}");
66
66
  expect(md).toContain("myapp install --all --yes");
67
+ expect(md).not.toContain("Upgrade to latest release");
68
+ });
69
+
70
+ test("generateApiGuide includes upgrade section when updateGetLatest is set", () => {
71
+ const fixture: CliProgram = {
72
+ key: "myapp",
73
+ version: "1.0.0",
74
+ description: "Demo app.",
75
+ install: { updateGetLatest: async () => ({ path: process.execPath }) },
76
+ commands: [{ key: "run", description: "Run.", handler: () => {} }],
77
+ };
78
+ const md = generateApiGuide(fixture);
79
+ expect(md).toContain("Upgrade to latest release");
80
+ expect(md).toContain("myapp install --update");
67
81
  });
68
82
 
69
83
  test("generateApiGuide resolves {argsbarg:program} in consumer notes", () => {
package/src/help.ts CHANGED
@@ -381,6 +381,21 @@ function rowsForSubcommands(cmds: CliNode[]): HelpRow[] {
381
381
 
382
382
  // ── Main Help Render ──────────────────────────────────────────────────────────
383
383
 
384
+ function appendNotesBox(
385
+ lines: string[],
386
+ notes: string | undefined,
387
+ appKey: string,
388
+ hw: number,
389
+ color: boolean,
390
+ ): void {
391
+ if ((notes ?? "").length === 0) {
392
+ return;
393
+ }
394
+ const resolved = cliResolveNotes(notes!, appKey);
395
+ lines.push("");
396
+ lines.push(renderTextBox("Notes", wrapText(resolved, hw - 4), hw, color).join("\n"));
397
+ }
398
+
384
399
  /**
385
400
  * Renders full help for the app root or a nested command, following `helpPath` from the root key.
386
401
  * `useStderr` is reserved for call-site consistency; width and color use stdout TTY.
@@ -416,6 +431,7 @@ export function cliHelpRender(schema: CliRouter, helpPath: string[], useStderr:
416
431
  renderTableBox("Commands", rowsForSubcommands(schema.commands ?? []), hw, color).join("\n"),
417
432
  );
418
433
  }
434
+ appendNotesBox(lines, schema.notes, schema.key, hw, color);
419
435
  return lines.join("\n") + "\n\n";
420
436
  }
421
437
 
@@ -479,11 +495,7 @@ export function cliHelpRender(schema: CliRouter, helpPath: string[], useStderr:
479
495
  }
480
496
 
481
497
  if ((node.notes ?? "").length > 0) {
482
- const resolved = cliResolveNotes(node.notes!, schema.key);
483
- lines.push("");
484
- lines.push(
485
- renderTextBox("Notes", wrapText(resolved, hw - 4), hw, color).join("\n"),
486
- );
498
+ appendNotesBox(lines, node.notes, schema.key, hw, color);
487
499
  }
488
500
 
489
501
  return lines.join("\n") + "\n\n";
package/src/index.test.ts CHANGED
@@ -678,6 +678,50 @@ test("root help omits legacy --schema flag", () => {
678
678
  expect(help).not.toContain("--schema");
679
679
  });
680
680
 
681
+ test("root help shows agent docs hint when docs enabled", () => {
682
+ const root = testProgram({
683
+ key: "myapp",
684
+ version: "1.0.0",
685
+ description: "demo",
686
+ docs: {
687
+ enabled: true,
688
+ topics: { readme: { text: "# readme\n" } },
689
+ },
690
+ commands: [{ key: "run", description: "Run.", handler: () => {} }],
691
+ });
692
+ const help = cliHelpRender(cliPresentationRoot(root), [], false);
693
+ expect(help).toContain("Agents: run `myapp docs skill` to learn how to use this app");
694
+ });
695
+
696
+ test("root help omits agent hint when docs disabled", () => {
697
+ const root = testProgram({
698
+ key: "myapp",
699
+ version: "1.0.0",
700
+ description: "demo",
701
+ commands: [{ key: "run", description: "Run.", handler: () => {} }],
702
+ });
703
+ const help = cliHelpRender(cliPresentationRoot(root), [], false);
704
+ expect(help).not.toContain("Agents:");
705
+ expect(help).not.toContain("docs skill");
706
+ });
707
+
708
+ test("root help includes program notes and agent hint", () => {
709
+ const root = testProgram({
710
+ key: "myapp",
711
+ version: "1.0.0",
712
+ description: "demo",
713
+ notes: "See `{argsbarg:program} docs readme` for the user guide.",
714
+ docs: {
715
+ enabled: true,
716
+ topics: { readme: { text: "# readme\n" } },
717
+ },
718
+ commands: [{ key: "run", description: "Run.", handler: () => {} }],
719
+ });
720
+ const help = cliHelpRender(cliPresentationRoot(root), [], false);
721
+ expect(help).toContain("See `myapp docs readme` for the user guide.");
722
+ expect(help).toContain("myapp docs skill");
723
+ });
724
+
681
725
  const nestedMcpFixture = testProgram({
682
726
  key: "nested.ts",
683
727
  description: "Nested groups demo.",
@@ -7,7 +7,7 @@ import { installErr } from "./status.ts";
7
7
  export async function cliUpdate(root: CliProgram): Promise<never> {
8
8
  const hook = root.install?.updateGetLatest;
9
9
  if (!hook) {
10
- installErr("update is not configured. Set install.updateGetLatest on the program root.");
10
+ installErr("install --update is not configured. Set install.updateGetLatest on the program root.");
11
11
  process.exit(1);
12
12
  }
13
13