padrone 1.4.0 → 1.6.0

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 (141) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/README.md +108 -283
  3. package/dist/args-Cnq0nwSM.mjs +272 -0
  4. package/dist/args-Cnq0nwSM.mjs.map +1 -0
  5. package/dist/codegen/index.d.mts +28 -3
  6. package/dist/codegen/index.d.mts.map +1 -1
  7. package/dist/codegen/index.mjs +169 -19
  8. package/dist/codegen/index.mjs.map +1 -1
  9. package/dist/commands-B_gufyR9.mjs +514 -0
  10. package/dist/commands-B_gufyR9.mjs.map +1 -0
  11. package/dist/{completion.mjs → completion-BEuflbDO.mjs} +86 -108
  12. package/dist/completion-BEuflbDO.mjs.map +1 -0
  13. package/dist/docs/index.d.mts +22 -2
  14. package/dist/docs/index.d.mts.map +1 -1
  15. package/dist/docs/index.mjs +92 -7
  16. package/dist/docs/index.mjs.map +1 -1
  17. package/dist/errors-CL63UOzt.mjs +137 -0
  18. package/dist/errors-CL63UOzt.mjs.map +1 -0
  19. package/dist/{formatter-ClUK5hcQ.d.mts → formatter-DrvhDMrq.d.mts} +35 -6
  20. package/dist/formatter-DrvhDMrq.d.mts.map +1 -0
  21. package/dist/help-B5Kk83of.mjs +849 -0
  22. package/dist/help-B5Kk83of.mjs.map +1 -0
  23. package/dist/index-BaU3X6dY.d.mts +1178 -0
  24. package/dist/index-BaU3X6dY.d.mts.map +1 -0
  25. package/dist/index.d.mts +763 -36
  26. package/dist/index.d.mts.map +1 -1
  27. package/dist/index.mjs +3608 -1534
  28. package/dist/index.mjs.map +1 -1
  29. package/dist/mcp-BM-d0nZi.mjs +377 -0
  30. package/dist/mcp-BM-d0nZi.mjs.map +1 -0
  31. package/dist/serve-Bk0JUlCj.mjs +402 -0
  32. package/dist/serve-Bk0JUlCj.mjs.map +1 -0
  33. package/dist/stream-DC4H8YTx.mjs +77 -0
  34. package/dist/stream-DC4H8YTx.mjs.map +1 -0
  35. package/dist/test.d.mts +5 -8
  36. package/dist/test.d.mts.map +1 -1
  37. package/dist/test.mjs +5 -27
  38. package/dist/test.mjs.map +1 -1
  39. package/dist/{update-check-EbNDkzyV.mjs → update-check-CZ2VqjnV.mjs} +16 -17
  40. package/dist/update-check-CZ2VqjnV.mjs.map +1 -0
  41. package/dist/zod.d.mts +32 -0
  42. package/dist/zod.d.mts.map +1 -0
  43. package/dist/zod.mjs +50 -0
  44. package/dist/zod.mjs.map +1 -0
  45. package/package.json +20 -9
  46. package/src/cli/completions.ts +14 -11
  47. package/src/cli/docs.ts +13 -16
  48. package/src/cli/doctor.ts +213 -24
  49. package/src/cli/index.ts +28 -82
  50. package/src/cli/init.ts +12 -10
  51. package/src/cli/link.ts +22 -18
  52. package/src/cli/wrap.ts +14 -11
  53. package/src/codegen/discovery.ts +80 -28
  54. package/src/codegen/index.ts +2 -1
  55. package/src/codegen/parsers/bash.ts +179 -0
  56. package/src/codegen/schema-to-code.ts +2 -1
  57. package/src/core/args.ts +296 -0
  58. package/src/core/commands.ts +373 -0
  59. package/src/core/create.ts +268 -0
  60. package/src/{runtime.ts → core/default-runtime.ts} +70 -135
  61. package/src/{errors.ts → core/errors.ts} +22 -0
  62. package/src/core/exec.ts +259 -0
  63. package/src/core/interceptors.ts +302 -0
  64. package/src/{parse.ts → core/parse.ts} +36 -89
  65. package/src/core/program-methods.ts +301 -0
  66. package/src/core/results.ts +229 -0
  67. package/src/core/runtime.ts +246 -0
  68. package/src/core/validate.ts +247 -0
  69. package/src/docs/index.ts +124 -11
  70. package/src/extension/auto-output.ts +95 -0
  71. package/src/extension/color.ts +38 -0
  72. package/src/extension/completion.ts +49 -0
  73. package/src/extension/config.ts +262 -0
  74. package/src/extension/env.ts +101 -0
  75. package/src/extension/help.ts +192 -0
  76. package/src/extension/index.ts +43 -0
  77. package/src/extension/ink.ts +93 -0
  78. package/src/extension/interactive.ts +106 -0
  79. package/src/extension/logger.ts +214 -0
  80. package/src/extension/man.ts +51 -0
  81. package/src/extension/mcp.ts +52 -0
  82. package/src/extension/progress-renderer.ts +338 -0
  83. package/src/extension/progress.ts +299 -0
  84. package/src/extension/repl.ts +94 -0
  85. package/src/extension/serve.ts +48 -0
  86. package/src/extension/signal.ts +87 -0
  87. package/src/extension/stdin.ts +62 -0
  88. package/src/extension/suggestions.ts +114 -0
  89. package/src/extension/timing.ts +81 -0
  90. package/src/extension/tracing.ts +175 -0
  91. package/src/extension/update-check.ts +77 -0
  92. package/src/extension/utils.ts +51 -0
  93. package/src/extension/version.ts +63 -0
  94. package/src/{completion.ts → feature/completion.ts} +130 -57
  95. package/src/{interactive.ts → feature/interactive.ts} +47 -6
  96. package/src/feature/mcp.ts +387 -0
  97. package/src/{repl-loop.ts → feature/repl-loop.ts} +26 -16
  98. package/src/feature/serve.ts +438 -0
  99. package/src/feature/test.ts +262 -0
  100. package/src/{update-check.ts → feature/update-check.ts} +16 -16
  101. package/src/{wrap.ts → feature/wrap.ts} +27 -27
  102. package/src/index.ts +120 -11
  103. package/src/output/colorizer.ts +154 -0
  104. package/src/{formatter.ts → output/formatter.ts} +281 -135
  105. package/src/{help.ts → output/help.ts} +62 -15
  106. package/src/{zod.d.ts → schema/zod.d.ts} +1 -1
  107. package/src/schema/zod.ts +50 -0
  108. package/src/test.ts +2 -285
  109. package/src/types/args-meta.ts +151 -0
  110. package/src/types/builder.ts +697 -0
  111. package/src/types/command.ts +157 -0
  112. package/src/types/index.ts +59 -0
  113. package/src/types/interceptor.ts +296 -0
  114. package/src/types/preferences.ts +83 -0
  115. package/src/types/result.ts +71 -0
  116. package/src/types/schema.ts +19 -0
  117. package/src/util/dotenv.ts +244 -0
  118. package/src/{shell-utils.ts → util/shell-utils.ts} +26 -9
  119. package/src/util/stream.ts +101 -0
  120. package/src/{type-helpers.ts → util/type-helpers.ts} +23 -16
  121. package/src/{type-utils.ts → util/type-utils.ts} +99 -37
  122. package/src/util/utils.ts +51 -0
  123. package/src/zod.ts +1 -0
  124. package/dist/args-CVDbyyzG.mjs +0 -199
  125. package/dist/args-CVDbyyzG.mjs.map +0 -1
  126. package/dist/chunk-y_GBKt04.mjs +0 -5
  127. package/dist/completion.d.mts +0 -64
  128. package/dist/completion.d.mts.map +0 -1
  129. package/dist/completion.mjs.map +0 -1
  130. package/dist/formatter-ClUK5hcQ.d.mts.map +0 -1
  131. package/dist/help-CcBe91bV.mjs +0 -1254
  132. package/dist/help-CcBe91bV.mjs.map +0 -1
  133. package/dist/types-DjIdJN5G.d.mts +0 -1059
  134. package/dist/types-DjIdJN5G.d.mts.map +0 -1
  135. package/dist/update-check-EbNDkzyV.mjs.map +0 -1
  136. package/src/args.ts +0 -461
  137. package/src/colorizer.ts +0 -41
  138. package/src/command-utils.ts +0 -532
  139. package/src/create.ts +0 -1477
  140. package/src/types.ts +0 -1109
  141. package/src/utils.ts +0 -140
@@ -1,75 +1,5 @@
1
- import { t as __require } from "./chunk-y_GBKt04.mjs";
2
- import { i as extractSchemaMetadata } from "./args-CVDbyyzG.mjs";
3
- //#region src/shell-utils.ts
4
- /**
5
- * Detects the current shell from environment variables and process info.
6
- * @returns The detected shell type, or undefined if unknown
7
- */
8
- function detectShell() {
9
- if (typeof process === "undefined") return void 0;
10
- const shellEnv = process.env.SHELL || "";
11
- if (shellEnv.includes("zsh")) return "zsh";
12
- if (shellEnv.includes("bash")) return "bash";
13
- if (shellEnv.includes("fish")) return "fish";
14
- if (process.env.PSModulePath || process.env.POWERSHELL_DISTRIBUTION_CHANNEL) return "powershell";
15
- try {
16
- const ppid = process.ppid;
17
- if (ppid) {
18
- const { execSync } = __require("node:child_process");
19
- const processName = execSync(`ps -p ${ppid} -o comm=`, {
20
- encoding: "utf-8",
21
- stdio: [
22
- "pipe",
23
- "pipe",
24
- "ignore"
25
- ]
26
- }).trim();
27
- if (processName.includes("zsh")) return "zsh";
28
- if (processName.includes("bash")) return "bash";
29
- if (processName.includes("fish")) return "fish";
30
- }
31
- } catch {}
32
- }
33
- function getRcFile(shell, home) {
34
- const { homedir } = __require("node:os");
35
- const { join } = __require("node:path");
36
- const h = home ?? homedir();
37
- switch (shell) {
38
- case "bash": return join(h, ".bashrc");
39
- case "zsh": return join(h, ".zshrc");
40
- case "fish": return join(h, ".config", "fish", "config.fish");
41
- case "powershell": return process.env.PROFILE || join(h, "Documents", "PowerShell", "Microsoft.PowerShell_profile.ps1");
42
- default: return null;
43
- }
44
- }
45
- function escapeRegExp(str) {
46
- return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
47
- }
48
- /**
49
- * Writes a snippet to a shell config file using begin/end markers for idempotency.
50
- * If a block with the same begin marker exists, it is replaced. Otherwise the snippet is appended.
51
- */
52
- function writeToRcFile(rcFile, snippet, beginMarker, endMarker) {
53
- const { existsSync, mkdirSync, readFileSync, writeFileSync } = __require("node:fs");
54
- const { dirname } = __require("node:path");
55
- const existing = existsSync(rcFile) ? readFileSync(rcFile, "utf-8") : "";
56
- if (existing.includes(beginMarker)) {
57
- const pattern = new RegExp(`${escapeRegExp(beginMarker)}[\\s\\S]*?${escapeRegExp(endMarker)}`);
58
- writeFileSync(rcFile, existing.replace(pattern, snippet));
59
- return {
60
- file: rcFile,
61
- updated: true
62
- };
63
- }
64
- mkdirSync(dirname(rcFile), { recursive: true });
65
- writeFileSync(rcFile, `${existing}${existing.length > 0 && !existing.endsWith("\n") ? "\n" : ""}\n${snippet}\n`);
66
- return {
67
- file: rcFile,
68
- updated: false
69
- };
70
- }
71
- //#endregion
72
- //#region src/completion.ts
1
+ import { a as getJsonSchema, d as detectShell, i as extractSchemaMetadata, m as writeToRcFile, p as getRcFile } from "./args-Cnq0nwSM.mjs";
2
+ //#region src/feature/completion.ts
73
3
  /**
74
4
  * Collects all commands from a program recursively.
75
5
  */
@@ -92,35 +22,55 @@ function extractArguments(cmd) {
92
22
  try {
93
23
  const argsMeta = cmd.meta?.fields;
94
24
  const { aliases } = extractSchemaMetadata(cmd.argsSchema, argsMeta, cmd.meta?.autoAlias);
95
- const aliasToArgument = {};
96
- for (const [arg, alias] of Object.entries(aliases)) aliasToArgument[alias] = arg;
97
- const jsonSchema = cmd.argsSchema["~standard"].jsonSchema.input({ target: "draft-2020-12" });
25
+ const argToAlias = {};
26
+ for (const [aliasName, argName] of Object.entries(aliases)) if (!argToAlias[argName]) argToAlias[argName] = aliasName;
27
+ const jsonSchema = getJsonSchema(cmd.argsSchema);
98
28
  if (jsonSchema.type === "object" && jsonSchema.properties) for (const [key, prop] of Object.entries(jsonSchema.properties)) {
99
- const alias = Object.entries(aliases).find(([arg]) => arg === key)?.[1];
29
+ const enumValues = prop.enum ?? prop.items?.enum;
30
+ const optMeta = argsMeta?.[key];
100
31
  argList.push({
101
32
  name: key,
102
- alias,
103
- isBoolean: prop?.type === "boolean"
33
+ alias: argToAlias[key],
34
+ isBoolean: prop?.type === "boolean",
35
+ enum: enumValues,
36
+ description: optMeta?.description ?? prop.description
104
37
  });
105
38
  }
106
39
  } catch {}
107
40
  return argList;
108
41
  }
109
42
  /**
43
+ * Collects unique args across all commands, preserving first-seen enum values.
44
+ */
45
+ function collectUniqueArgs(program, commands) {
46
+ const seen = /* @__PURE__ */ new Map();
47
+ for (const cmd of [program, ...commands]) for (const arg of extractArguments(cmd)) if (!seen.has(arg.name)) seen.set(arg.name, arg);
48
+ return seen;
49
+ }
50
+ /**
110
51
  * Generates a Bash completion script for the program.
111
52
  */
112
53
  function generateBashCompletion(program) {
113
54
  const programName = program.name;
114
55
  const commands = collectAllCommands(program);
115
56
  const commandNames = commands.map((c) => c.name).join(" ");
57
+ const uniqueArgs = collectUniqueArgs(program, commands);
116
58
  const allArguments = /* @__PURE__ */ new Set();
117
59
  allArguments.add("--help");
118
60
  allArguments.add("--version");
119
- for (const cmd of [program, ...commands]) for (const arg of extractArguments(cmd)) {
61
+ for (const arg of uniqueArgs.values()) {
120
62
  allArguments.add(`--${arg.name}`);
121
- if (arg.alias) allArguments.add(`-${arg.alias}`);
63
+ if (arg.alias) allArguments.add(`--${arg.alias}`);
122
64
  }
123
65
  const argsList = Array.from(allArguments).join(" ");
66
+ const enumCases = [];
67
+ for (const arg of uniqueArgs.values()) {
68
+ if (!arg.enum || arg.enum.length === 0) continue;
69
+ const values = arg.enum.join(" ");
70
+ const patterns = [`--${arg.name}`];
71
+ if (arg.alias) patterns.push(`--${arg.alias}`);
72
+ enumCases.push(` ${patterns.join("|")}) COMPREPLY=($(compgen -W "${values}" -- "$cur")); return 0 ;;`);
73
+ }
124
74
  return `###-begin-${programName}-completion-###
125
75
  #
126
76
  # ${programName} command completion script
@@ -144,8 +94,13 @@ if type complete &>/dev/null; then
144
94
 
145
95
  local commands="${commandNames}"
146
96
  local args="${argsList}"
97
+ ${enumCases.length > 0 ? `
98
+ # Complete option values
99
+ case "$prev" in
100
+ ${enumCases.join("\n")}
101
+ esac
147
102
 
148
- # Complete args when current word starts with -
103
+ ` : "\n"} # Complete args when current word starts with -
149
104
  if [[ "$cur" == -* ]]; then
150
105
  COMPREPLY=($(compgen -W "$args" -- "$cur"))
151
106
  return 0
@@ -197,13 +152,12 @@ function generateZshCompletion(program) {
197
152
  const argumentCompletions = [];
198
153
  argumentCompletions.push(" '--help[Show help information]'");
199
154
  argumentCompletions.push(" '--version[Show version number]'");
200
- const seenArgs = new Set(["help", "version"]);
201
- for (const cmd of [program, ...commands]) for (const arg of extractArguments(cmd)) {
202
- if (seenArgs.has(arg.name)) continue;
203
- seenArgs.add(arg.name);
204
- const escapedDesc = (cmd.meta?.fields?.[arg.name]?.description || "").replace(/'/g, "'\\''").replace(/\[/g, "\\[").replace(/\]/g, "\\]");
205
- if (arg.alias) argumentCompletions.push(` {-${arg.alias},--${arg.name}}'[${escapedDesc}]'`);
206
- else argumentCompletions.push(` '--${arg.name}[${escapedDesc}]'`);
155
+ const uniqueArgs = collectUniqueArgs(program, commands);
156
+ for (const arg of uniqueArgs.values()) {
157
+ const escapedDesc = (arg.description || "").replace(/'/g, "'\\''").replace(/\[/g, "\\[").replace(/\]/g, "\\]");
158
+ const valueAction = arg.enum?.length ? `: :(${arg.enum.join(" ")})` : "";
159
+ if (arg.alias) argumentCompletions.push(` {--${arg.alias},--${arg.name}}'[${escapedDesc}]${valueAction}'`);
160
+ else argumentCompletions.push(` '--${arg.name}[${escapedDesc}]${valueAction}'`);
207
161
  }
208
162
  return `#compdef ${programName}
209
163
  ###-begin-${programName}-completion-###
@@ -268,13 +222,12 @@ function generateFishCompletion(program) {
268
222
  lines.push("# Global arguments");
269
223
  lines.push(`complete -c ${programName} -l help -d 'Show help information'`);
270
224
  lines.push(`complete -c ${programName} -l version -d 'Show version number'`);
271
- const seenArgs = new Set(["help", "version"]);
272
- for (const cmd of [program, ...commands]) for (const arg of extractArguments(cmd)) {
273
- if (seenArgs.has(arg.name)) continue;
274
- seenArgs.add(arg.name);
275
- const escapedDesc = (cmd.meta?.fields?.[arg.name]?.description || "").replace(/'/g, "\\'");
276
- if (arg.alias) lines.push(`complete -c ${programName} -s ${arg.alias} -l ${arg.name} -d '${escapedDesc}'`);
277
- else lines.push(`complete -c ${programName} -l ${arg.name} -d '${escapedDesc}'`);
225
+ const uniqueArgs = collectUniqueArgs(program, commands);
226
+ for (const arg of uniqueArgs.values()) {
227
+ const escapedDesc = (arg.description || "").replace(/'/g, "\\'");
228
+ const valueFlag = arg.enum?.length ? ` -xa '${arg.enum.join(" ")}'` : "";
229
+ if (arg.alias) lines.push(`complete -c ${programName} -l ${arg.name} -s ${arg.alias} -d '${escapedDesc}'${valueFlag}`);
230
+ else lines.push(`complete -c ${programName} -l ${arg.name} -d '${escapedDesc}'${valueFlag}`);
278
231
  }
279
232
  lines.push(`###-end-${programName}-completion-###`);
280
233
  return lines.join("\n");
@@ -284,6 +237,32 @@ function generateFishCompletion(program) {
284
237
  */
285
238
  function generatePowerShellCompletion(program) {
286
239
  const programName = program.name;
240
+ const commands = collectAllCommands(program);
241
+ const uniqueArgs = collectUniqueArgs(program, commands);
242
+ const commandNames = commands.map((c) => `'${c.name}'`).join(", ");
243
+ const argNames = ["'--help'", "'--version'"];
244
+ for (const arg of uniqueArgs.values()) {
245
+ argNames.push(`'--${arg.name}'`);
246
+ if (arg.alias) argNames.push(`'--${arg.alias}'`);
247
+ }
248
+ const enumCases = [];
249
+ for (const arg of uniqueArgs.values()) {
250
+ if (!arg.enum || arg.enum.length === 0) continue;
251
+ const values = arg.enum.map((v) => `'${v}'`).join(", ");
252
+ const patterns = [`'--${arg.name}'`];
253
+ if (arg.alias) patterns.push(`'--${arg.alias}'`);
254
+ enumCases.push(` ${patterns.join(", ")} { @(${values}) | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
255
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
256
+ }; return }`);
257
+ }
258
+ const enumBlock = enumCases.length > 0 ? `
259
+ # Complete option values
260
+ $prevWord = $commandAst.CommandElements | Select-Object -Last 2 | Select-Object -First 1
261
+ switch ($prevWord) {
262
+ ${enumCases.join("\n")}
263
+ }
264
+
265
+ ` : "\n";
287
266
  return `###-begin-${programName}-completion-###
288
267
  #
289
268
  # ${programName} command completion script for PowerShell
@@ -294,10 +273,9 @@ function generatePowerShellCompletion(program) {
294
273
  Register-ArgumentCompleter -Native -CommandName ${programName} -ScriptBlock {
295
274
  param($wordToComplete, $commandAst, $cursorPosition)
296
275
 
297
- $commands = @(${collectAllCommands(program).map((c) => `'${c.name}'`).join(", ")})
298
- $args = @('--help', '--version')
299
-
300
- if ($wordToComplete -like '-*') {
276
+ $commands = @(${commandNames})
277
+ $args = @(${argNames.join(", ")})
278
+ ${enumBlock} if ($wordToComplete -like '-*') {
301
279
  $args | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
302
280
  [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
303
281
  }
@@ -348,10 +326,10 @@ ${programName} completion powershell >> $PROFILE`;
348
326
  * Generates the completion output with automatic shell detection.
349
327
  * If shell is not specified, detects the current shell and provides instructions.
350
328
  */
351
- function generateCompletionOutput(program, shell) {
329
+ async function generateCompletionOutput(program, shell) {
352
330
  const programName = program.name;
353
331
  if (shell) return generateCompletion(program, shell);
354
- const detectedShell = detectShell();
332
+ const detectedShell = await detectShell();
355
333
  if (detectedShell) return `# Detected shell: ${detectedShell}
356
334
  #
357
335
  ${getCompletionInstallInstructions(programName, detectedShell)}
@@ -380,10 +358,10 @@ ${generateCompletion(program, detectedShell)}`;
380
358
  * Sets up shell completions by writing an eval snippet to the appropriate shell config file.
381
359
  * Uses marker comments for idempotency — re-running replaces the existing block.
382
360
  */
383
- function setupCompletions(programName, shell) {
384
- const { existsSync, mkdirSync, writeFileSync } = __require("node:fs");
385
- const { join } = __require("node:path");
386
- const { homedir } = __require("node:os");
361
+ async function setupCompletions(programName, shell) {
362
+ const { existsSync, mkdirSync, writeFileSync } = await import("node:fs");
363
+ const { join } = await import("node:path");
364
+ const { homedir } = await import("node:os");
387
365
  const beginMarker = `###-begin-${programName}-completion-###`;
388
366
  const endMarker = `###-end-${programName}-completion-###`;
389
367
  const snippet = buildSetupSnippet(programName, shell, beginMarker, endMarker);
@@ -398,7 +376,7 @@ function setupCompletions(programName, shell) {
398
376
  updated: existed
399
377
  };
400
378
  }
401
- const rcFile = getRcFile(shell);
379
+ const rcFile = await getRcFile(shell);
402
380
  if (!rcFile) throw new Error(`Could not determine config file for ${shell}.`);
403
381
  return writeToRcFile(rcFile, snippet, beginMarker, endMarker);
404
382
  }
@@ -412,6 +390,6 @@ function buildSetupSnippet(programName, shell, beginMarker, endMarker) {
412
390
  }
413
391
  }
414
392
  //#endregion
415
- export { detectShell, escapeRegExp, generateBashCompletion, generateCompletion, generateCompletionOutput, generateFishCompletion, generatePowerShellCompletion, generateZshCompletion, getCompletionInstallInstructions, getRcFile, setupCompletions, writeToRcFile };
393
+ export { detectShell, generateCompletionOutput, setupCompletions };
416
394
 
417
- //# sourceMappingURL=completion.mjs.map
395
+ //# sourceMappingURL=completion-BEuflbDO.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"completion-BEuflbDO.mjs","names":[],"sources":["../src/feature/completion.ts"],"sourcesContent":["import { extractSchemaMetadata, getJsonSchema } from '../core/args.ts';\nimport type { AnyPadroneCommand } from '../types/index.ts';\nimport { detectShell, getRcFile, type ShellType, writeToRcFile } from '../util/shell-utils.ts';\n\nexport { detectShell, escapeRegExp, getRcFile, type ShellType, writeToRcFile } from '../util/shell-utils.ts';\n\n/**\n * Collects all commands from a program recursively.\n */\nfunction collectAllCommands(cmd: AnyPadroneCommand): AnyPadroneCommand[] {\n const result: AnyPadroneCommand[] = [];\n\n if (cmd.commands) {\n for (const subcmd of cmd.commands) {\n if (!subcmd.hidden) {\n result.push(subcmd);\n result.push(...collectAllCommands(subcmd));\n }\n }\n }\n\n return result;\n}\n\ninterface ExtractedArg {\n name: string;\n alias?: string;\n isBoolean: boolean;\n enum?: string[];\n description?: string;\n}\n\n/**\n * Extracts all argument names from a command's schema.\n */\nfunction extractArguments(cmd: AnyPadroneCommand): ExtractedArg[] {\n const argList: ExtractedArg[] = [];\n\n if (!cmd.argsSchema) return argList;\n\n try {\n const argsMeta = cmd.meta?.fields;\n const { aliases } = extractSchemaMetadata(cmd.argsSchema, argsMeta, cmd.meta?.autoAlias);\n\n // Build reverse map: argName → aliasName\n const argToAlias: Record<string, string> = {};\n for (const [aliasName, argName] of Object.entries(aliases)) {\n if (!argToAlias[argName]) argToAlias[argName] = aliasName;\n }\n\n const jsonSchema = getJsonSchema(cmd.argsSchema) as Record<string, any>;\n\n if (jsonSchema.type === 'object' && jsonSchema.properties) {\n for (const [key, prop] of Object.entries(jsonSchema.properties as Record<string, any>)) {\n const enumValues = (prop.enum ?? prop.items?.enum) as string[] | undefined;\n const optMeta = argsMeta?.[key];\n argList.push({\n name: key,\n alias: argToAlias[key],\n isBoolean: prop?.type === 'boolean',\n enum: enumValues,\n description: optMeta?.description ?? prop.description,\n });\n }\n }\n } catch {\n // Ignore schema parsing errors\n }\n\n return argList;\n}\n\n/**\n * Collects unique args across all commands, preserving first-seen enum values.\n */\nfunction collectUniqueArgs(program: AnyPadroneCommand, commands: AnyPadroneCommand[]): Map<string, ExtractedArg> {\n const seen = new Map<string, ExtractedArg>();\n\n for (const cmd of [program, ...commands]) {\n for (const arg of extractArguments(cmd)) {\n if (!seen.has(arg.name)) {\n seen.set(arg.name, arg);\n }\n }\n }\n\n return seen;\n}\n\n/**\n * Generates a Bash completion script for the program.\n */\nexport function generateBashCompletion(program: AnyPadroneCommand): string {\n const programName = program.name;\n const commands = collectAllCommands(program);\n const commandNames = commands.map((c) => c.name).join(' ');\n const uniqueArgs = collectUniqueArgs(program, commands);\n\n // Collect all option names\n const allArguments = new Set<string>();\n allArguments.add('--help');\n allArguments.add('--version');\n\n for (const arg of uniqueArgs.values()) {\n allArguments.add(`--${arg.name}`);\n if (arg.alias) allArguments.add(`--${arg.alias}`);\n }\n\n const argsList = Array.from(allArguments).join(' ');\n\n // Build case branches for options with enum values\n const enumCases: string[] = [];\n for (const arg of uniqueArgs.values()) {\n if (!arg.enum || arg.enum.length === 0) continue;\n const values = arg.enum.join(' ');\n const patterns = [`--${arg.name}`];\n if (arg.alias) patterns.push(`--${arg.alias}`);\n enumCases.push(` ${patterns.join('|')}) COMPREPLY=($(compgen -W \"${values}\" -- \"$cur\")); return 0 ;;`);\n }\n\n const enumBlock =\n enumCases.length > 0\n ? `\n # Complete option values\n case \"$prev\" in\n${enumCases.join('\\n')}\n esac\n\n`\n : '\\n';\n\n return `###-begin-${programName}-completion-###\n#\n# ${programName} command completion script\n#\n# Installation: ${programName} completion >> ~/.bashrc (or ~/.zshrc)\n# Or, maybe: ${programName} completion > /usr/local/etc/bash_completion.d/${programName}\n#\n\nif type complete &>/dev/null; then\n _${programName}_completion() {\n local cur prev words cword\n if type _get_comp_words_by_ref &>/dev/null; then\n _get_comp_words_by_ref -n = -n @ -n : -w words -i cword\n else\n cword=\"$COMP_CWORD\"\n words=(\"\\${COMP_WORDS[@]}\")\n fi\n\n cur=\"\\${words[cword]}\"\n prev=\"\\${words[cword-1]}\"\n\n local commands=\"${commandNames}\"\n local args=\"${argsList}\"\n${enumBlock} # Complete args when current word starts with -\n if [[ \"$cur\" == -* ]]; then\n COMPREPLY=($(compgen -W \"$args\" -- \"$cur\"))\n return 0\n fi\n\n # Complete commands\n COMPREPLY=($(compgen -W \"$commands\" -- \"$cur\"))\n }\n complete -o bashdefault -o default -o nospace -F _${programName}_completion ${programName}\nelif type compdef &>/dev/null; then\n _${programName}_completion() {\n local si=$IFS\n local commands=\"${commandNames}\"\n local args=\"${argsList}\"\n\n if [[ \"\\${words[CURRENT]}\" == -* ]]; then\n compadd -- \\${=args}\n else\n compadd -- \\${=commands}\n fi\n IFS=$si\n }\n compdef _${programName}_completion ${programName}\nelif type compctl &>/dev/null; then\n _${programName}_completion() {\n local commands=\"${commandNames}\"\n local args=\"${argsList}\"\n\n if [[ \"\\${words[CURRENT]}\" == -* ]]; then\n reply=(\\${=args})\n else\n reply=(\\${=commands})\n fi\n }\n compctl -K _${programName}_completion ${programName}\nfi\n###-end-${programName}-completion-###`;\n}\n\n/**\n * Generates a Zsh completion script for the program.\n */\nexport function generateZshCompletion(program: AnyPadroneCommand): string {\n const programName = program.name;\n const commands = collectAllCommands(program);\n\n // Generate command completions with descriptions\n const commandCompletions = commands\n .map((cmd) => {\n const desc = cmd.description || cmd.title || '';\n const escapedDesc = desc.replace(/'/g, \"'\\\\''\").replace(/:/g, '\\\\:');\n return ` '${cmd.name}:${escapedDesc}'`;\n })\n .join('\\n');\n\n // Collect all args with descriptions and enum values\n const argumentCompletions: string[] = [];\n argumentCompletions.push(\" '--help[Show help information]'\");\n argumentCompletions.push(\" '--version[Show version number]'\");\n\n const uniqueArgs = collectUniqueArgs(program, commands);\n\n for (const arg of uniqueArgs.values()) {\n const desc = arg.description || '';\n const escapedDesc = desc.replace(/'/g, \"'\\\\''\").replace(/\\[/g, '\\\\[').replace(/\\]/g, '\\\\]');\n\n // Zsh action spec for enum values: :label:(val1 val2 val3)\n const valueAction = arg.enum?.length ? `: :(${arg.enum.join(' ')})` : '';\n\n if (arg.alias) {\n argumentCompletions.push(` {--${arg.alias},--${arg.name}}'[${escapedDesc}]${valueAction}'`);\n } else {\n argumentCompletions.push(` '--${arg.name}[${escapedDesc}]${valueAction}'`);\n }\n }\n\n return `#compdef ${programName}\n###-begin-${programName}-completion-###\n#\n# ${programName} command completion script for Zsh\n#\n# Installation: ${programName} completion >> ~/.zshrc\n# Or: ${programName} completion > ~/.zsh/completions/_${programName}\n#\n\n_${programName}() {\n local -a commands\n local -a args\n\n commands=(\n${commandCompletions}\n )\n\n args=(\n${argumentCompletions.join('\\n')}\n )\n\n _arguments -s \\\\\n $args \\\\\n '1: :->command' \\\\\n '*::arg:->args'\n\n case \"$state\" in\n command)\n _describe 'command' commands\n ;;\n esac\n}\n\n_${programName}\n###-end-${programName}-completion-###`;\n}\n\n/**\n * Generates a Fish completion script for the program.\n */\nexport function generateFishCompletion(program: AnyPadroneCommand): string {\n const programName = program.name;\n const commands = collectAllCommands(program);\n\n const lines: string[] = [\n `###-begin-${programName}-completion-###`,\n '#',\n `# ${programName} command completion script for Fish`,\n '#',\n `# Installation: ${programName} completion > ~/.config/fish/completions/${programName}.fish`,\n '#',\n '',\n `# Clear existing completions`,\n `complete -c ${programName} -e`,\n '',\n '# Commands',\n ];\n\n for (const cmd of commands) {\n const desc = cmd.description || cmd.title || '';\n const escapedDesc = desc.replace(/'/g, \"\\\\'\");\n lines.push(`complete -c ${programName} -n \"__fish_use_subcommand\" -a \"${cmd.name}\" -d '${escapedDesc}'`);\n }\n\n lines.push('');\n lines.push('# Global arguments');\n lines.push(`complete -c ${programName} -l help -d 'Show help information'`);\n lines.push(`complete -c ${programName} -l version -d 'Show version number'`);\n\n const uniqueArgs = collectUniqueArgs(program, commands);\n\n for (const arg of uniqueArgs.values()) {\n const desc = arg.description || '';\n const escapedDesc = desc.replace(/'/g, \"\\\\'\");\n // Fish: -xa 'val1 val2' provides exclusive value completions\n const valueFlag = arg.enum?.length ? ` -xa '${arg.enum.join(' ')}'` : '';\n\n if (arg.alias) {\n lines.push(`complete -c ${programName} -l ${arg.name} -s ${arg.alias} -d '${escapedDesc}'${valueFlag}`);\n } else {\n lines.push(`complete -c ${programName} -l ${arg.name} -d '${escapedDesc}'${valueFlag}`);\n }\n }\n\n lines.push(`###-end-${programName}-completion-###`);\n\n return lines.join('\\n');\n}\n\n/**\n * Generates a PowerShell completion script for the program.\n */\nexport function generatePowerShellCompletion(program: AnyPadroneCommand): string {\n const programName = program.name;\n const commands = collectAllCommands(program);\n const uniqueArgs = collectUniqueArgs(program, commands);\n\n const commandNames = commands.map((c) => `'${c.name}'`).join(', ');\n\n // Collect all option names\n const argNames: string[] = [\"'--help'\", \"'--version'\"];\n for (const arg of uniqueArgs.values()) {\n argNames.push(`'--${arg.name}'`);\n if (arg.alias) argNames.push(`'--${arg.alias}'`);\n }\n\n // Build switch cases for option value completion\n const enumCases: string[] = [];\n for (const arg of uniqueArgs.values()) {\n if (!arg.enum || arg.enum.length === 0) continue;\n const values = arg.enum.map((v) => `'${v}'`).join(', ');\n const patterns = [`'--${arg.name}'`];\n if (arg.alias) patterns.push(`'--${arg.alias}'`);\n enumCases.push(` ${patterns.join(', ')} { @(${values}) | Where-Object { $_ -like \"$wordToComplete*\" } | ForEach-Object {\n [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)\n }; return }`);\n }\n\n const enumBlock =\n enumCases.length > 0\n ? `\n # Complete option values\n $prevWord = $commandAst.CommandElements | Select-Object -Last 2 | Select-Object -First 1\n switch ($prevWord) {\n${enumCases.join('\\n')}\n }\n\n`\n : '\\n';\n\n return `###-begin-${programName}-completion-###\n#\n# ${programName} command completion script for PowerShell\n#\n# Installation: ${programName} completion >> $PROFILE\n#\n\nRegister-ArgumentCompleter -Native -CommandName ${programName} -ScriptBlock {\n param($wordToComplete, $commandAst, $cursorPosition)\n\n $commands = @(${commandNames})\n $args = @(${argNames.join(', ')})\n${enumBlock} if ($wordToComplete -like '-*') {\n $args | Where-Object { $_ -like \"$wordToComplete*\" } | ForEach-Object {\n [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)\n }\n } else {\n $commands | Where-Object { $_ -like \"$wordToComplete*\" } | ForEach-Object {\n [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)\n }\n }\n}\n###-end-${programName}-completion-###`;\n}\n\n/**\n * Generates a completion script for the specified shell.\n */\nexport function generateCompletion(program: AnyPadroneCommand, shell: ShellType): string {\n switch (shell) {\n case 'bash':\n return generateBashCompletion(program);\n case 'zsh':\n return generateZshCompletion(program);\n case 'fish':\n return generateFishCompletion(program);\n case 'powershell':\n return generatePowerShellCompletion(program);\n default:\n throw new Error(`Unsupported shell: ${shell}`);\n }\n}\n\n/**\n * Gets the installation instructions for a shell completion script.\n */\nexport function getCompletionInstallInstructions(programName: string, shell: ShellType): string {\n switch (shell) {\n case 'bash':\n return `# Add to ~/.bashrc:\n${programName} completion bash >> ~/.bashrc\n\n# Or install system-wide:\n${programName} completion bash > /usr/local/etc/bash_completion.d/${programName}`;\n\n case 'zsh':\n return `# Add to ~/.zshrc:\n${programName} completion zsh >> ~/.zshrc\n\n# Or add to completions directory:\n${programName} completion zsh > ~/.zsh/completions/_${programName}`;\n\n case 'fish':\n return `# Install to Fish completions:\n${programName} completion fish > ~/.config/fish/completions/${programName}.fish`;\n\n case 'powershell':\n return `# Add to PowerShell profile:\n${programName} completion powershell >> $PROFILE`;\n\n default:\n return `# Run: ${programName} completion <shell>\n# Supported shells: bash, zsh, fish, powershell`;\n }\n}\n\n/**\n * Generates the completion output with automatic shell detection.\n * If shell is not specified, detects the current shell and provides instructions.\n */\nexport async function generateCompletionOutput(program: AnyPadroneCommand, shell?: ShellType): Promise<string> {\n const programName = program.name;\n\n if (shell) {\n return generateCompletion(program, shell);\n }\n\n // Auto-detect shell and provide instructions\n const detectedShell = await detectShell();\n\n if (detectedShell) {\n const instructions = getCompletionInstallInstructions(programName, detectedShell);\n const script = generateCompletion(program, detectedShell);\n\n return `# Detected shell: ${detectedShell}\n#\n${instructions}\n#\n# Or evaluate directly (temporary, for current session only):\n# eval \"$(${programName} completion ${detectedShell})\"\n\n${script}`;\n }\n\n // Could not detect shell - provide usage info\n return `# Shell auto-detection failed.\n#\n# Usage: ${programName} completion <shell>\n#\n# Supported shells:\n# bash - Bash completion script\n# zsh - Zsh completion script\n# fish - Fish completion script\n# powershell - PowerShell completion script\n#\n# Example:\n# ${programName} completion bash >> ~/.bashrc\n# ${programName} completion zsh >> ~/.zshrc\n# ${programName} completion fish > ~/.config/fish/completions/${programName}.fish\n# ${programName} completion powershell >> $PROFILE`;\n}\n\nexport interface SetupCompletionsResult {\n /** The file that was written to. */\n file: string;\n /** Whether an existing completion block was replaced (true) or a new one was appended (false). */\n updated: boolean;\n}\n\n/**\n * Sets up shell completions by writing an eval snippet to the appropriate shell config file.\n * Uses marker comments for idempotency — re-running replaces the existing block.\n */\nexport async function setupCompletions(programName: string, shell: ShellType): Promise<SetupCompletionsResult> {\n const { existsSync, mkdirSync, writeFileSync } = await import('node:fs');\n const { join } = await import('node:path');\n const { homedir } = await import('node:os');\n\n const beginMarker = `###-begin-${programName}-completion-###`;\n const endMarker = `###-end-${programName}-completion-###`;\n const snippet = buildSetupSnippet(programName, shell, beginMarker, endMarker);\n\n if (shell === 'fish') {\n const completionsDir = join(homedir(), '.config', 'fish', 'completions');\n const filePath = join(completionsDir, `${programName}.fish`);\n mkdirSync(completionsDir, { recursive: true });\n const existed = existsSync(filePath);\n writeFileSync(filePath, `${snippet}\\n`);\n return { file: filePath, updated: existed };\n }\n\n const rcFile = await getRcFile(shell);\n if (!rcFile) {\n throw new Error(`Could not determine config file for ${shell}.`);\n }\n\n return writeToRcFile(rcFile, snippet, beginMarker, endMarker);\n}\n\nfunction buildSetupSnippet(programName: string, shell: ShellType, beginMarker: string, endMarker: string): string {\n const evalCmd = `${programName} completion ${shell}`;\n\n switch (shell) {\n case 'bash':\n case 'zsh':\n return `${beginMarker}\\neval \"$(${evalCmd})\"\\n${endMarker}`;\n case 'fish':\n return `${beginMarker}\\n${evalCmd} | source\\n${endMarker}`;\n case 'powershell':\n return `${beginMarker}\\n${evalCmd} | Invoke-Expression\\n${endMarker}`;\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { r as HelpInfo } from "../formatter-ClUK5hcQ.mjs";
1
+ import { r as HelpInfo } from "../formatter-DrvhDMrq.mjs";
2
2
 
3
3
  //#region src/docs/index.d.ts
4
4
  type DocsFormat = 'markdown' | 'html' | 'man' | 'json';
@@ -29,6 +29,26 @@ type DocsResult = {
29
29
  * Accepts either a PadroneProgram (from createPadrone()) or a raw AnyPadroneCommand.
30
30
  */
31
31
  declare function generateDocs(program: object, options?: DocsOptions): DocsResult;
32
+ type SetupManPagesResult = {
33
+ /** Directory where man pages were written. */dir: string; /** Man page files that were written. */
34
+ written: string[]; /** Whether existing pages were overwritten (true) or newly created (false). */
35
+ updated: boolean;
36
+ };
37
+ /**
38
+ * Installs man pages for a Padrone CLI program into the local man directory.
39
+ * Generates man pages for all commands and writes them to `~/.local/share/man/man1/`.
40
+ *
41
+ * After installation, `man <program>` and `man <program>-<subcommand>` should work
42
+ * (assuming `~/.local/share/man` is in `MANPATH` or `manpath` picks it up).
43
+ */
44
+ declare function setupManPages(program: object): Promise<SetupManPagesResult>;
45
+ /**
46
+ * Removes installed man pages for a Padrone CLI program.
47
+ */
48
+ declare function removeManPages(program: object): Promise<{
49
+ dir: string;
50
+ removed: string[];
51
+ }>;
32
52
  //#endregion
33
- export { DocsFormat, DocsOptions, DocsPage, DocsResult, generateDocs };
53
+ export { DocsFormat, DocsOptions, DocsPage, DocsResult, SetupManPagesResult, generateDocs, removeManPages, setupManPages };
34
54
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/docs/index.ts"],"mappings":";;;KAWY,UAAA;AAAA,KAEA,WAAA;EAFA,6CAIV,MAAA,GAAS,UAAA;EAET,MAAA,WANoB;EAQpB,aAAA,YANqB;EAQrB,WAAA,IAAe,IAAA,EAAM,QAAA,EAAU,KAAA,aAAkB,MAAA,mBANxC;EAQT,SAAA,YAFiD;EAIjD,MAAA;AAAA;AAAA,KAGU,QAAA;EAbD,8EAeT,IAAA,UAXA;EAaA,OAAA,UAXqB;EAarB,OAAA;AAAA;AAAA,KAGU,UAAA;EAdV,2BAgBA,KAAA,EAAO,QAAA,IAdD;EAgBN,OAAA,YAbU;EAeV,OAAA;EAEA,MAAA;IAAU,IAAA;IAAc,KAAA,EAAO,KAAA;EAAA;AAAA;;AARjC;;;iBA2egB,YAAA,CAAa,OAAA,UAAiB,OAAA,GAAS,WAAA,GAAmB,UAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/docs/index.ts"],"mappings":";;;KAWY,UAAA;AAAA,KAEA,WAAA;EAFA,6CAIV,MAAA,GAAS,UAAA;EAET,MAAA,WANoB;EAQpB,aAAA,YANqB;EAQrB,WAAA,IAAe,IAAA,EAAM,QAAA,EAAU,KAAA,aAAkB,MAAA,mBANxC;EAQT,SAAA,YAFiD;EAIjD,MAAA;AAAA;AAAA,KAGU,QAAA;EAbD,8EAeT,IAAA,UAXA;EAaA,OAAA,UAXqB;EAarB,OAAA;AAAA;AAAA,KAGU,UAAA;EAdV,2BAgBA,KAAA,EAAO,QAAA,IAdD;EAgBN,OAAA,YAbU;EAeV,OAAA;EAEA,MAAA;IAAU,IAAA;IAAc,KAAA,EAAO,KAAA;EAAA;AAAA;;AARjC;;;iBAigBgB,YAAA,CAAa,OAAA,UAAiB,OAAA,GAAS,WAAA,GAAmB,UAAA;AAAA,KAoF9D,mBAAA;EAnlBH,8CAqlBP,GAAA,UAjlBA;EAmlBA,OAAA,YAjlBU;EAmlBV,OAAA;AAAA;;;AA1FF;;;;;iBAqHsB,aAAA,CAAc,OAAA,WAAkB,OAAA,CAAQ,mBAAA;;;;iBA+BxC,cAAA,CAAe,OAAA,WAAkB,OAAA;EAAU,GAAA;EAAa,OAAA;AAAA"}
@@ -1,4 +1,5 @@
1
- import { a as commandSymbol, n as getHelpInfo } from "../help-CcBe91bV.mjs";
1
+ import { s as getCommand } from "../commands-B_gufyR9.mjs";
2
+ import { n as getHelpInfo } from "../help-B5Kk83of.mjs";
2
3
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
3
4
  import { dirname, join, resolve } from "node:path";
4
5
  //#region src/docs/index.ts
@@ -122,6 +123,14 @@ function generateMarkdownPage(info, depth, frontmatterFn) {
122
123
  lines.push(usageParts.join(" "));
123
124
  lines.push("```");
124
125
  lines.push("");
126
+ if (info.examples?.length) {
127
+ lines.push("## Examples");
128
+ lines.push("");
129
+ lines.push("```");
130
+ for (const ex of info.examples) lines.push(`$ ${ex}`);
131
+ lines.push("```");
132
+ lines.push("");
133
+ }
125
134
  if (info.subcommands?.length) {
126
135
  const visibleSubs = info.subcommands.filter((s) => !s.hidden);
127
136
  if (visibleSubs.length > 0) {
@@ -170,6 +179,10 @@ function generateHtmlPage(info, depth) {
170
179
  if (info.usage.hasArguments) usageParts.push("[options]");
171
180
  sections.push(" <h2>Usage</h2>");
172
181
  sections.push(` <pre><code>${escapeHtml(usageParts.join(" "))}</code></pre>`);
182
+ if (info.examples?.length) {
183
+ sections.push(" <h2>Examples</h2>");
184
+ sections.push(` <pre><code>${info.examples.map((ex) => `$ ${escapeHtml(ex)}`).join("\n")}</code></pre>`);
185
+ }
173
186
  if (info.subcommands?.length) {
174
187
  const visibleSubs = info.subcommands.filter((s) => !s.hidden);
175
188
  if (visibleSubs.length > 0) {
@@ -249,6 +262,13 @@ function generateManPage(info, _depth, programName) {
249
262
  lines.push(".SH DESCRIPTION");
250
263
  lines.push(escapeMan(info.description));
251
264
  }
265
+ if (info.examples?.length) {
266
+ lines.push(".SH EXAMPLES");
267
+ for (const ex of info.examples) {
268
+ lines.push(".PP");
269
+ lines.push(`.nf\n$ ${escapeMan(ex)}\n.fi`);
270
+ }
271
+ }
252
272
  if (info.subcommands?.length) {
253
273
  const visibleSubs = info.subcommands.filter((s) => !s.hidden);
254
274
  if (visibleSubs.length > 0) {
@@ -321,17 +341,13 @@ function generateMarkdownIndex(rootInfo, allInfos) {
321
341
  }
322
342
  return `${lines.join("\n").trimEnd()}\n`;
323
343
  }
324
- function resolveCommand(programOrCommand) {
325
- if (commandSymbol in programOrCommand) return programOrCommand[commandSymbol];
326
- return programOrCommand;
327
- }
328
344
  /**
329
345
  * Generate documentation for a Padrone CLI program or command tree.
330
346
  * Accepts either a PadroneProgram (from createPadrone()) or a raw AnyPadroneCommand.
331
347
  */
332
348
  function generateDocs(program, options = {}) {
333
349
  const { format = "markdown", output, includeHidden = false, frontmatter, overwrite = true, dryRun = false } = options;
334
- const cmd = resolveCommand(program);
350
+ const cmd = getCommand(program);
335
351
  const allInfos = collectAllHelpInfo(cmd, includeHidden);
336
352
  const rootInfo = allInfos[0];
337
353
  const programName = cmd.name || "program";
@@ -399,7 +415,76 @@ function generateDocs(program, options = {}) {
399
415
  }
400
416
  return result;
401
417
  }
418
+ /**
419
+ * Returns the local man page directory for the given section.
420
+ * Uses `~/.local/share/man/man<section>` (XDG convention).
421
+ */
422
+ async function getManPageDir(section = 1) {
423
+ const { homedir } = await import("node:os");
424
+ return join(process.env.XDG_DATA_HOME || join(homedir(), ".local", "share"), "man", `man${section}`);
425
+ }
426
+ /**
427
+ * Converts a command name to a man page filename.
428
+ * "myapp" → "myapp.1", "myapp deploy" → "myapp-deploy.1"
429
+ */
430
+ function manPageFilename(commandName, section = 1) {
431
+ return `${commandName.replace(/\s+/g, "-")}.${section}`;
432
+ }
433
+ /**
434
+ * Installs man pages for a Padrone CLI program into the local man directory.
435
+ * Generates man pages for all commands and writes them to `~/.local/share/man/man1/`.
436
+ *
437
+ * After installation, `man <program>` and `man <program>-<subcommand>` should work
438
+ * (assuming `~/.local/share/man` is in `MANPATH` or `manpath` picks it up).
439
+ */
440
+ async function setupManPages(program) {
441
+ const cmd = getCommand(program);
442
+ const allInfos = collectAllHelpInfo(cmd, false);
443
+ const programName = cmd.name || "program";
444
+ const manDir = await getManPageDir(1);
445
+ mkdirSync(manDir, { recursive: true });
446
+ const written = [];
447
+ let updated = false;
448
+ for (let i = 0; i < allInfos.length; i++) {
449
+ const info = allInfos[i];
450
+ const depth = i === 0 ? 0 : info.name.split(/\s+/).length;
451
+ const filename = manPageFilename(info.name === "<root>" || !info.name ? programName : info.name);
452
+ const fullPath = join(manDir, filename);
453
+ if (existsSync(fullPath)) updated = true;
454
+ writeFileSync(fullPath, generateManPage(info, depth, programName), "utf-8");
455
+ written.push(filename);
456
+ }
457
+ return {
458
+ dir: manDir,
459
+ written,
460
+ updated
461
+ };
462
+ }
463
+ /**
464
+ * Removes installed man pages for a Padrone CLI program.
465
+ */
466
+ async function removeManPages(program) {
467
+ const { unlinkSync } = await import("node:fs");
468
+ const cmd = getCommand(program);
469
+ const allInfos = collectAllHelpInfo(cmd, false);
470
+ const programName = cmd.name || "program";
471
+ const manDir = await getManPageDir(1);
472
+ const removed = [];
473
+ for (let i = 0; i < allInfos.length; i++) {
474
+ const info = allInfos[i];
475
+ const filename = manPageFilename(info.name === "<root>" || !info.name ? programName : info.name);
476
+ const fullPath = join(manDir, filename);
477
+ if (existsSync(fullPath)) {
478
+ unlinkSync(fullPath);
479
+ removed.push(filename);
480
+ }
481
+ }
482
+ return {
483
+ dir: manDir,
484
+ removed
485
+ };
486
+ }
402
487
  //#endregion
403
- export { generateDocs };
488
+ export { generateDocs, removeManPages, setupManPages };
404
489
 
405
490
  //# sourceMappingURL=index.mjs.map