argsbarg 7.0.6 → 7.0.8

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.
Files changed (42) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/README.md +4 -4
  3. package/docs/README.md +1 -1
  4. package/docs/ai-skills.md +14 -62
  5. package/docs/bundled-docs.md +9 -13
  6. package/docs/cli-program.md +10 -11
  7. package/docs/configure.md +9 -11
  8. package/docs/output-schema.md +1 -1
  9. package/examples/full-example/AGENTS.md +3 -3
  10. package/examples/full-example/docs/README.md +1 -1
  11. package/examples/full-example/justfile +0 -1
  12. package/examples/full-example/skills/full-example/SKILL.md +0 -2
  13. package/examples/full-example/src/program.ts +0 -1
  14. package/examples/full-example-json/AGENTS.md +3 -3
  15. package/examples/full-example-json/docs/README.md +1 -1
  16. package/examples/full-example-json/justfile +0 -1
  17. package/examples/full-example-json/skills/full-example-json/SKILL.md +0 -2
  18. package/examples/full-example-json/src/program.ts +0 -1
  19. package/index.d.ts +5 -3
  20. package/package.json +1 -1
  21. package/src/builtins/builtins.test.ts +1 -22
  22. package/src/builtins/configure-copy.ts +4 -11
  23. package/src/builtins/presentation.ts +2 -5
  24. package/src/configure/artifacts/status.test.ts +5 -5
  25. package/src/configure/artifacts/target-effective.ts +1 -2
  26. package/src/configure/artifacts/target-skill.ts +9 -15
  27. package/src/configure/artifacts/targets/skill.ts +8 -1
  28. package/src/configure/artifacts/targets.test.ts +5 -5
  29. package/src/configure/configure.test.ts +4 -4
  30. package/src/core/parse.test.ts +1 -112
  31. package/src/core/types.ts +5 -3
  32. package/src/core/validate.ts +4 -2
  33. package/src/docs/builtin.ts +5 -3
  34. package/src/docs/cli-guide.ts +3 -3
  35. package/src/docs/docs.test.ts +3 -47
  36. package/src/docs/resolve.ts +5 -5
  37. package/src/docs/save.ts +9 -20
  38. package/src/help.test.ts +250 -8
  39. package/src/help.ts +397 -46
  40. package/src/skill/generate.ts +30 -156
  41. package/src/skill/hint.ts +2 -15
  42. package/src/skill/install.ts +3 -35
package/src/docs/save.ts CHANGED
@@ -1,20 +1,19 @@
1
1
  /*
2
2
  This module persists bundled documentation topics to disk when `--save` is passed.
3
- It writes documentation under `./docs/` and agent skills under `./skills/<app-name>/SKILL.md`.
3
+ It writes documentation under `./docs/`.
4
4
  */
5
5
 
6
- import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
6
+ import { mkdirSync, writeFileSync } from "node:fs";
7
7
  import { dirname, join } from "node:path";
8
8
  import type { CliProgram } from "../core/types.ts";
9
9
  import { generatedFileHtmlComment, insertGeneratedHint } from "../skill/hint.ts";
10
- import { skillDirName } from "../skill/naming.ts";
11
10
  import { docsTopicContent } from "./resolve.ts";
12
11
 
13
12
  /** Relative output directory for `docs --save`. */
14
13
  export const DOCS_SAVE_DIR = "docs";
15
14
 
16
15
  /** Builtin docs topics generated by argsbarg (not consumer `docs.topics`). */
17
- export const DOCS_GENERATED_SAVE_TOPICS = ["mcp", "cli", "skill", "http"] as const;
16
+ export const DOCS_GENERATED_SAVE_TOPICS = ["mcp", "cli", "http"] as const;
18
17
 
19
18
  /** Whether `--save` should prepend a generated-file hint (argsbarg writers only). */
20
19
  export function docsTopicIsGeneratedByArgsbarg(
@@ -34,7 +33,7 @@ export function docsSaveGeneratedHint(
34
33
  return generatedFileHtmlComment(`${program.key} docs ${topic} --save`);
35
34
  }
36
35
 
37
- /** Inserts save hint without breaking YAML frontmatter (`docs skill`). */
36
+ /** Inserts save hint into markdown content. */
38
37
  export function applySaveGeneratedHint(
39
38
  /** Program definition. */
40
39
  program: CliProgram,
@@ -47,7 +46,7 @@ export function applySaveGeneratedHint(
47
46
  return content;
48
47
  }
49
48
  const hint = docsSaveGeneratedHint(program, topic);
50
- return insertGeneratedHint(content, hint, topic === "skill" ? { afterFrontmatter: true } : undefined);
49
+ return insertGeneratedHint(content, hint);
51
50
  }
52
51
 
53
52
  /** File body for `--save` (hint on argsbarg-generated markdown only). */
@@ -74,20 +73,17 @@ export function docsSaveFilename(
74
73
  return `${topic}.md`;
75
74
  }
76
75
 
77
- /** Relative path under cwd for a saved docs topic (or skills/<key>/SKILL.md for skill). */
76
+ /** Relative path under cwd for a saved docs topic. */
78
77
  export function docsSaveRelativePath(
79
78
  /** Topic identifier. */
80
79
  topic: string,
81
- /** Program root for resolving app-specific paths like skill directories. */
82
- program?: CliProgram,
80
+ /** Program root for resolving app-specific paths. */
81
+ _program?: CliProgram,
83
82
  ): string {
84
- if (topic === "skill" && program) {
85
- return join("skills", skillDirName(program.key), "SKILL.md");
86
- }
87
83
  return join(DOCS_SAVE_DIR, docsSaveFilename(topic));
88
84
  }
89
85
 
90
- /** Writes one docs topic under `./docs/` or `./skills/<key>/`; returns relative path written. */
86
+ /** Writes one docs topic under `./docs/`; returns relative path written. */
91
87
  export function saveDocsTopic(
92
88
  /** Program definition root. */
93
89
  program: CliProgram,
@@ -98,13 +94,6 @@ export function saveDocsTopic(
98
94
  const abs = join(process.cwd(), rel);
99
95
  mkdirSync(dirname(abs), { recursive: true });
100
96
 
101
- if (topic === "skill") {
102
- const legacyDoc = join(process.cwd(), DOCS_SAVE_DIR, "skill.md");
103
- if (existsSync(legacyDoc)) {
104
- rmSync(legacyDoc, { force: true });
105
- }
106
- }
107
-
108
97
  writeFileSync(abs, docsTopicContentForSave(program, topic), "utf8");
109
98
  return rel;
110
99
  }
package/src/help.test.ts CHANGED
@@ -5,7 +5,14 @@ Help rendering and label formatting tests.
5
5
  import { describe, expect, test } from "bun:test";
6
6
  import { cliPresentationRoot } from "./builtins/presentation.ts";
7
7
  import { type CliOption, CliOptionKind, type CliPositional } from "./core/types.ts";
8
- import { CLI_NOTES_PROGRAM, cliHelpRender, cliOptionLabel, cliPositionalLabel, cliResolveNotes } from "./help.ts";
8
+ import {
9
+ CLI_NOTES_PROGRAM,
10
+ cliHelpRender,
11
+ cliOptionLabel,
12
+ cliPositionalLabel,
13
+ cliResolveNotes,
14
+ schemaToYamlLines,
15
+ } from "./help.ts";
9
16
  import { testProgram } from "./test/fixtures.ts";
10
17
 
11
18
  describe("cliOptionLabel", () => {
@@ -64,7 +71,7 @@ describe("cliResolveNotes", () => {
64
71
  });
65
72
 
66
73
  describe("cliHelpRender", () => {
67
- test("docs help lists schema, cli, and skill subcommands", () => {
74
+ test("docs help lists schema and cli subcommands", () => {
68
75
  const root = testProgram({
69
76
  key: "app",
70
77
  version: "1.0.0",
@@ -85,8 +92,7 @@ describe("cliHelpRender", () => {
85
92
  expect(help).toContain("Print the full CLI command tree as JSON.");
86
93
  expect(help).toContain("cli");
87
94
  expect(help).toContain("markdown");
88
- expect(help).toContain("skill");
89
- expect(help).toContain("reference agent SKILL");
95
+ expect(help).not.toContain("skill");
90
96
  });
91
97
 
92
98
  test("root help omits legacy --schema flag", () => {
@@ -106,7 +112,7 @@ describe("cliHelpRender", () => {
106
112
  expect(help).not.toContain("--schema");
107
113
  });
108
114
 
109
- test("root help shows agent docs hint when docs enabled", () => {
115
+ test("root help omits agent docs hint when docs enabled", () => {
110
116
  const root = testProgram({
111
117
  key: "myapp",
112
118
  version: "1.0.0",
@@ -117,7 +123,7 @@ describe("cliHelpRender", () => {
117
123
  commands: [{ key: "run", description: "Run.", handler: () => {} }],
118
124
  });
119
125
  const help = cliHelpRender(cliPresentationRoot(root), [], false);
120
- expect(help).toContain("For AI agents: `myapp docs skill`.");
126
+ expect(help).not.toContain("docs skill");
121
127
  expect(help).not.toContain("install --skill");
122
128
  });
123
129
 
@@ -134,7 +140,7 @@ describe("cliHelpRender", () => {
134
140
  expect(help).not.toContain("docs skill");
135
141
  });
136
142
 
137
- test("root help includes program notes and agent hint", () => {
143
+ test("root help includes program notes", () => {
138
144
  const root = testProgram({
139
145
  key: "myapp",
140
146
  version: "1.0.0",
@@ -147,6 +153,242 @@ describe("cliHelpRender", () => {
147
153
  });
148
154
  const help = cliHelpRender(cliPresentationRoot(root), [], false);
149
155
  expect(help).toContain("See `myapp docs readme` for the user guide.");
150
- expect(help).toContain("myapp docs skill");
156
+ expect(help).not.toContain("docs skill");
157
+ });
158
+
159
+ /** Tests that non-TTY help strips box characters and renders plain text. */
160
+ test("non-TTY help strips boxes and renders clean plain text", () => {
161
+ const root = testProgram({
162
+ key: "myapp",
163
+ version: "1.0.0",
164
+ description: "Test application.",
165
+ commands: [
166
+ {
167
+ key: "status",
168
+ description: "Show status.",
169
+ options: [
170
+ {
171
+ name: "verbose",
172
+ shortName: "v",
173
+ description: "Verbose output.",
174
+ kind: CliOptionKind.Presence,
175
+ },
176
+ ],
177
+ handler: () => {},
178
+ },
179
+ ],
180
+ });
181
+ const help = cliHelpRender(cliPresentationRoot(root), ["status"], false, { isTTY: false });
182
+ expect(help).not.toContain("╭─");
183
+ expect(help).not.toContain("╰─");
184
+ expect(help).not.toContain("│");
185
+ expect(help).toContain("Usage:\n myapp status [OPTIONS]");
186
+ expect(help).toContain("Options:\n --help, -h Show help for this command.\n --verbose, -v Verbose output.");
187
+ });
188
+
189
+ /** Tests that TTY help renders rounded UTF-8 boxes. */
190
+ test("TTY help renders rounded UTF-8 boxes and omits output schema by default", () => {
191
+ const root = testProgram({
192
+ key: "myapp",
193
+ version: "1.0.0",
194
+ description: "Test application.",
195
+ commands: [
196
+ {
197
+ key: "status",
198
+ description: "Show status.",
199
+ outputSchema: {
200
+ type: "object",
201
+ properties: {
202
+ version: { type: "string", description: "App version." },
203
+ },
204
+ required: ["version"],
205
+ },
206
+ handler: () => {},
207
+ },
208
+ ],
209
+ });
210
+ const help = cliHelpRender(cliPresentationRoot(root), ["status"], false, { isTTY: true });
211
+ expect(help).toContain("╭");
212
+ expect(help).toContain("╰");
213
+ expect(help).toContain("│");
214
+ expect(help).not.toContain("Output Schema");
215
+ });
216
+
217
+ /** Tests that non-TTY help automatically includes output schema in YAML by default. */
218
+ test("non-TTY help automatically includes output schema in YAML by default", () => {
219
+ const root = testProgram({
220
+ key: "myapp",
221
+ version: "1.0.0",
222
+ description: "Test application.",
223
+ commands: [
224
+ {
225
+ key: "status",
226
+ description: "Show status.",
227
+ outputSchema: {
228
+ type: "object",
229
+ properties: {
230
+ version: { type: "string", description: "App version." },
231
+ },
232
+ required: ["version"],
233
+ },
234
+ handler: () => {},
235
+ },
236
+ ],
237
+ });
238
+ const help = cliHelpRender(cliPresentationRoot(root), ["status"], false, { isTTY: false });
239
+ expect(help).toContain("Output Schema (with --json):");
240
+ expect(help).toContain("# App version.");
241
+ expect(help).toContain("version: string");
242
+ });
243
+
244
+ /** Tests that json leaf commands render Output Schema (JSON) and Input Schema. */
245
+ test("json leaf renders Output Schema (JSON) and Input Schema in non-TTY mode", () => {
246
+ const root = testProgram({
247
+ key: "myapp",
248
+ version: "1.0.0",
249
+ description: "Test application.",
250
+ commands: [
251
+ {
252
+ key: "create",
253
+ kind: "json",
254
+ description: "Create resource.",
255
+ inputSchema: {
256
+ type: "object",
257
+ properties: {
258
+ name: { type: "string", description: "Resource name." },
259
+ },
260
+ required: ["name"],
261
+ },
262
+ outputSchema: {
263
+ type: "object",
264
+ properties: {
265
+ id: { type: "string", description: "Generated ID." },
266
+ },
267
+ required: ["id"],
268
+ },
269
+ handler: () => {},
270
+ },
271
+ ],
272
+ });
273
+ const help = cliHelpRender(cliPresentationRoot(root), ["create"], false, { isTTY: false });
274
+ expect(help).toContain("Input Schema:");
275
+ expect(help).toContain("# Resource name.");
276
+ expect(help).toContain("name: string");
277
+ expect(help).toContain("Output Schema (JSON):");
278
+ expect(help).toContain("# Generated ID.");
279
+ expect(help).toContain("id: string");
280
+ });
281
+ });
282
+
283
+ /** Tests for converting JSON Schema to human- and agent-friendly YAML lines. */
284
+ describe("schemaToYamlLines", () => {
285
+ /** Tests primitive properties with required and optional keys and comments. */
286
+ test("formats primitive properties with descriptions and optionality", () => {
287
+ const schema = {
288
+ type: "object",
289
+ properties: {
290
+ documentId: {
291
+ type: "string",
292
+ description: "Unique document identifier.",
293
+ },
294
+ index: {
295
+ type: "integer",
296
+ },
297
+ },
298
+ required: ["documentId"],
299
+ };
300
+ const lines = schemaToYamlLines(schema, 0);
301
+ expect(lines).toEqual(["# Unique document identifier.", "documentId: string", "index?: integer"]);
302
+ });
303
+
304
+ /** Tests enums, string formats, and union types. */
305
+ test("formats enums, string formats, and union types", () => {
306
+ const schema = {
307
+ type: "object",
308
+ properties: {
309
+ format: {
310
+ type: "string",
311
+ enum: ["pdf", "html"],
312
+ },
313
+ createdAt: {
314
+ type: "string",
315
+ format: "date-time",
316
+ },
317
+ status: {
318
+ anyOf: [{ type: "string" }, { type: "number" }],
319
+ },
320
+ },
321
+ };
322
+ const lines = schemaToYamlLines(schema, 0);
323
+ expect(lines).toEqual(['format?: "pdf" | "html"', "createdAt?: string (date-time)", "status?: string | number"]);
324
+ });
325
+
326
+ /** Tests nested objects and arrays of objects with definitions. */
327
+ test("formats nested objects and arrays of objects with definition resolution", () => {
328
+ const schema = {
329
+ type: "object",
330
+ properties: {
331
+ tab: {
332
+ type: "object",
333
+ description: "Active tab metadata.",
334
+ properties: {
335
+ tabId: { type: "string" },
336
+ title: { type: "string" },
337
+ },
338
+ required: ["tabId", "title"],
339
+ },
340
+ tabs: {
341
+ type: "array",
342
+ items: {
343
+ $ref: "#/definitions/TabItem",
344
+ },
345
+ },
346
+ },
347
+ definitions: {
348
+ TabItem: {
349
+ type: "object",
350
+ properties: {
351
+ tabId: { type: "string" },
352
+ title: { type: "string" },
353
+ index: { type: "integer" },
354
+ },
355
+ required: ["tabId", "title", "index"],
356
+ },
357
+ },
358
+ required: ["tab"],
359
+ };
360
+ const lines = schemaToYamlLines(schema, 0);
361
+ expect(lines).toEqual([
362
+ "# Active tab metadata.",
363
+ "tab:",
364
+ " tabId: string",
365
+ " title: string",
366
+ "tabs?:",
367
+ " - tabId: string",
368
+ " title: string",
369
+ " index: integer",
370
+ ]);
371
+ });
372
+
373
+ /** Tests recursive references handle cycles gracefully without infinite loop. */
374
+ test("handles recursive definition references without infinite loop", () => {
375
+ const schema = {
376
+ type: "object",
377
+ properties: {
378
+ name: { type: "string" },
379
+ parent: { $ref: "#/definitions/TreeNode" },
380
+ },
381
+ definitions: {
382
+ TreeNode: {
383
+ type: "object",
384
+ properties: {
385
+ name: { type: "string" },
386
+ parent: { $ref: "#/definitions/TreeNode" },
387
+ },
388
+ },
389
+ },
390
+ };
391
+ const lines = schemaToYamlLines(schema, 0);
392
+ expect(lines).toEqual(["name?: string", "parent?:", " name?: string", " parent?: TreeNode"]);
151
393
  });
152
394
  });