padrone 1.3.0 → 1.5.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 (82) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/README.md +105 -284
  3. package/dist/{args-DFEI7_G_.mjs → args-D5PNDyNu.mjs} +46 -21
  4. package/dist/args-D5PNDyNu.mjs.map +1 -0
  5. package/dist/chunk-CjcI7cDX.mjs +15 -0
  6. package/dist/codegen/index.d.mts +28 -3
  7. package/dist/codegen/index.d.mts.map +1 -1
  8. package/dist/codegen/index.mjs +169 -19
  9. package/dist/codegen/index.mjs.map +1 -1
  10. package/dist/command-utils-B1D-HqCd.mjs +1117 -0
  11. package/dist/command-utils-B1D-HqCd.mjs.map +1 -0
  12. package/dist/completion.d.mts +1 -1
  13. package/dist/completion.d.mts.map +1 -1
  14. package/dist/completion.mjs +77 -29
  15. package/dist/completion.mjs.map +1 -1
  16. package/dist/docs/index.d.mts +22 -2
  17. package/dist/docs/index.d.mts.map +1 -1
  18. package/dist/docs/index.mjs +94 -7
  19. package/dist/docs/index.mjs.map +1 -1
  20. package/dist/errors-BiVrBgi6.mjs +114 -0
  21. package/dist/errors-BiVrBgi6.mjs.map +1 -0
  22. package/dist/{formatter-XroimS3Q.d.mts → formatter-DtHzbP22.d.mts} +35 -5
  23. package/dist/formatter-DtHzbP22.d.mts.map +1 -0
  24. package/dist/help-bbmu9-qd.mjs +735 -0
  25. package/dist/help-bbmu9-qd.mjs.map +1 -0
  26. package/dist/index.d.mts +32 -3
  27. package/dist/index.d.mts.map +1 -1
  28. package/dist/index.mjs +495 -267
  29. package/dist/index.mjs.map +1 -1
  30. package/dist/mcp-mLWIdUIu.mjs +379 -0
  31. package/dist/mcp-mLWIdUIu.mjs.map +1 -0
  32. package/dist/serve-B0u43DK7.mjs +404 -0
  33. package/dist/serve-B0u43DK7.mjs.map +1 -0
  34. package/dist/stream-BcC146Ud.mjs +56 -0
  35. package/dist/stream-BcC146Ud.mjs.map +1 -0
  36. package/dist/test.d.mts +1 -1
  37. package/dist/test.mjs +4 -15
  38. package/dist/test.mjs.map +1 -1
  39. package/dist/{types-BS7RP5Ls.d.mts → types-Ch8Mk6Qb.d.mts} +311 -63
  40. package/dist/types-Ch8Mk6Qb.d.mts.map +1 -0
  41. package/dist/{update-check-EbNDkzyV.mjs → update-check-CFX1FV3v.mjs} +2 -2
  42. package/dist/{update-check-EbNDkzyV.mjs.map → update-check-CFX1FV3v.mjs.map} +1 -1
  43. package/dist/zod.d.mts +32 -0
  44. package/dist/zod.d.mts.map +1 -0
  45. package/dist/zod.mjs +50 -0
  46. package/dist/zod.mjs.map +1 -0
  47. package/package.json +10 -2
  48. package/src/args.ts +76 -44
  49. package/src/cli/docs.ts +1 -7
  50. package/src/cli/doctor.ts +195 -10
  51. package/src/cli/index.ts +1 -1
  52. package/src/cli/init.ts +2 -3
  53. package/src/cli/link.ts +2 -2
  54. package/src/codegen/discovery.ts +80 -28
  55. package/src/codegen/index.ts +2 -1
  56. package/src/codegen/parsers/bash.ts +179 -0
  57. package/src/codegen/schema-to-code.ts +2 -1
  58. package/src/colorizer.ts +126 -13
  59. package/src/command-utils.ts +401 -23
  60. package/src/completion.ts +120 -47
  61. package/src/create.ts +483 -130
  62. package/src/docs/index.ts +122 -8
  63. package/src/formatter.ts +173 -125
  64. package/src/help.ts +46 -12
  65. package/src/index.ts +29 -1
  66. package/src/interactive.ts +45 -4
  67. package/src/mcp.ts +390 -0
  68. package/src/repl-loop.ts +16 -3
  69. package/src/runtime.ts +195 -2
  70. package/src/serve.ts +442 -0
  71. package/src/stream.ts +75 -0
  72. package/src/test.ts +7 -16
  73. package/src/type-utils.ts +28 -4
  74. package/src/types.ts +212 -30
  75. package/src/wrap.ts +23 -25
  76. package/src/zod.ts +50 -0
  77. package/dist/args-DFEI7_G_.mjs.map +0 -1
  78. package/dist/chunk-y_GBKt04.mjs +0 -5
  79. package/dist/formatter-XroimS3Q.d.mts.map +0 -1
  80. package/dist/help-CgGP7hQU.mjs +0 -1229
  81. package/dist/help-CgGP7hQU.mjs.map +0 -1
  82. package/dist/types-BS7RP5Ls.d.mts.map +0 -1
package/src/help.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { StandardJSONSchemaV1 } from '@standard-schema/spec';
2
- import { extractSchemaMetadata, type PadroneArgsSchemaMeta, parsePositionalConfig, parseStdinConfig } from './args.ts';
2
+ import { extractSchemaMetadata, JSON_SCHEMA_OPTS, type PadroneArgsSchemaMeta, parsePositionalConfig, parseStdinConfig } from './args.ts';
3
+ import type { ColorConfig, ColorTheme } from './colorizer.ts';
3
4
  import { findCommandByName } from './command-utils.ts';
4
5
  import {
5
6
  createFormatter,
@@ -16,6 +17,9 @@ import { getRootCommand } from './utils.ts';
16
17
  export type HelpPreferences = {
17
18
  format?: HelpFormat | 'auto';
18
19
  detail?: HelpDetail;
20
+ theme?: ColorTheme | ColorConfig;
21
+ /** Show all global commands and flags in full detail */
22
+ all?: boolean;
19
23
  };
20
24
 
21
25
  /**
@@ -35,7 +39,7 @@ function extractPositionalArgsInfo(
35
39
  const positionalConfig = parsePositionalConfig(meta.positional);
36
40
 
37
41
  try {
38
- const jsonSchema = schema['~standard'].jsonSchema.input({ target: 'draft-2020-12' }) as Record<string, any>;
42
+ const jsonSchema = schema['~standard'].jsonSchema.input(JSON_SCHEMA_OPTS) as Record<string, any>;
39
43
 
40
44
  if (jsonSchema.type === 'object' && jsonSchema.properties) {
41
45
  const properties = jsonSchema.properties as Record<string, any>;
@@ -54,6 +58,7 @@ function extractPositionalArgsInfo(
54
58
  optional: !required.includes(name),
55
59
  default: prop.default,
56
60
  type: variadic ? `array<${prop.items?.type || 'string'}>` : prop.type,
61
+ enum: (prop.enum ?? prop.items?.enum) as string[] | undefined,
57
62
  });
58
63
  }
59
64
  }
@@ -74,7 +79,7 @@ function extractArgsInfo(schema: StandardJSONSchemaV1, meta?: PadroneArgsSchemaM
74
79
  const argsMeta = meta?.fields;
75
80
 
76
81
  try {
77
- const jsonSchema = schema['~standard'].jsonSchema.input({ target: 'draft-2020-12' }) as Record<string, any>;
82
+ const jsonSchema = schema['~standard'].jsonSchema.input(JSON_SCHEMA_OPTS) as Record<string, any>;
78
83
 
79
84
  // Handle object: z.object({ key: z.string(), ... })
80
85
  if (jsonSchema.type === 'object' && jsonSchema.properties) {
@@ -133,6 +138,7 @@ function extractArgsInfo(schema: StandardJSONSchemaV1, meta?: PadroneArgsSchemaM
133
138
  examples: optMeta?.examples ?? prop?.examples,
134
139
  variadic: propType === 'array',
135
140
  negatable: isNegatable,
141
+ group: optMeta?.group,
136
142
  });
137
143
  }
138
144
  }
@@ -153,7 +159,7 @@ function extractArgsInfo(schema: StandardJSONSchemaV1, meta?: PadroneArgsSchemaM
153
159
  * @param cmd - The command to build help info for
154
160
  * @param detail - The level of detail ('minimal', 'standard', or 'full')
155
161
  */
156
- export function getHelpInfo(cmd: AnyPadroneCommand, detail: HelpPreferences['detail'] = 'standard'): HelpInfo {
162
+ export function getHelpInfo(cmd: AnyPadroneCommand, detail: HelpPreferences['detail'] = 'standard', all?: boolean): HelpInfo {
157
163
  const rootCmd = getRootCommand(cmd);
158
164
  // A command is a "default" command if its name is '' or it has '' as an alias
159
165
  const isDefaultCommand = cmd.parent && (!cmd.name || cmd.aliases?.includes(''));
@@ -175,6 +181,7 @@ export function getHelpInfo(cmd: AnyPadroneCommand, detail: HelpPreferences['det
175
181
  name: commandName,
176
182
  title: cmd.title,
177
183
  description: cmd.description,
184
+ examples: cmd.examples,
178
185
  aliases: displayAliases,
179
186
  deprecated: cmd.deprecated,
180
187
  hidden: cmd.hidden,
@@ -183,7 +190,7 @@ export function getHelpInfo(cmd: AnyPadroneCommand, detail: HelpPreferences['det
183
190
  hasSubcommands: !!(cmd.commands && cmd.commands.length > 0),
184
191
  hasPositionals,
185
192
  hasArguments: false, // updated below after extracting arguments
186
- stdinField: cmd.meta?.stdin ? parseStdinConfig(cmd.meta.stdin).field : undefined,
193
+ stdinField: cmd.meta?.stdin ? parseStdinConfig(cmd.meta.stdin) : undefined,
187
194
  },
188
195
  };
189
196
 
@@ -221,6 +228,7 @@ export function getHelpInfo(cmd: AnyPadroneCommand, detail: HelpPreferences['det
221
228
  aliases: displayAliases?.length ? displayAliases : undefined,
222
229
  deprecated: c.deprecated,
223
230
  hidden: c.hidden,
231
+ group: c.group,
224
232
  },
225
233
  {
226
234
  name: displayName,
@@ -229,6 +237,7 @@ export function getHelpInfo(cmd: AnyPadroneCommand, detail: HelpPreferences['det
229
237
  deprecated: c.deprecated,
230
238
  hidden: c.hidden,
231
239
  hasSubcommands: true,
240
+ group: c.group,
232
241
  },
233
242
  ];
234
243
  }
@@ -242,6 +251,7 @@ export function getHelpInfo(cmd: AnyPadroneCommand, detail: HelpPreferences['det
242
251
  deprecated: c.deprecated,
243
252
  hidden: c.hidden,
244
253
  hasSubcommands,
254
+ group: c.group,
245
255
  },
246
256
  ];
247
257
  }),
@@ -284,40 +294,64 @@ export function getHelpInfo(cmd: AnyPadroneCommand, detail: HelpPreferences['det
284
294
  }
285
295
  }
286
296
 
287
- // Add built-in commands/flags for root command only
288
- if (!cmd.parent) {
297
+ // Add global commands/flags (root command by default, all commands when --all is passed)
298
+ if (!cmd.parent || all) {
289
299
  const builtins: HelpInfo['builtins'] = [];
290
300
 
291
- if (!findCommandByName('help', cmd.commands)) {
301
+ if (!findCommandByName('help', rootCmd.commands)) {
292
302
  builtins.push({
293
303
  name: 'help [command], -h, --help',
294
304
  description: 'Show help for a command',
295
305
  sub: [
306
+ { name: '--all', description: 'Show all global commands and flags' },
296
307
  { name: '--detail <level>', description: 'Detail level (minimal, standard, full)' },
297
308
  { name: '--format <format>', description: 'Output format (text, ansi, json, markdown, html)' },
298
309
  ],
299
310
  });
300
311
  }
301
312
 
302
- if (!findCommandByName('version', cmd.commands)) {
313
+ if (!findCommandByName('version', rootCmd.commands)) {
303
314
  builtins.push({
304
315
  name: 'version, -v, --version',
305
316
  description: 'Show version information',
306
317
  });
307
318
  }
308
319
 
309
- if (!findCommandByName('completion', cmd.commands)) {
320
+ if (!findCommandByName('completion', rootCmd.commands)) {
310
321
  builtins.push({
311
322
  name: 'completion [shell]',
312
323
  description: 'Generate shell completions (bash, zsh, fish, powershell)',
313
324
  });
314
325
  }
315
326
 
327
+ if (!findCommandByName('man', rootCmd.commands)) {
328
+ builtins.push({
329
+ name: 'man',
330
+ description: 'Show or install man pages (--setup to install, --remove to uninstall) (experimental)',
331
+ });
332
+ }
333
+
316
334
  builtins.push({
317
335
  name: '[command] --repl',
318
336
  description: 'Start interactive REPL scoped to a command',
319
337
  });
320
338
 
339
+ if (!findCommandByName('mcp', rootCmd.commands)) {
340
+ builtins.push({
341
+ name: 'mcp [http|stdio]',
342
+ description: 'Start a Model Context Protocol server to expose commands as AI tools (experimental)',
343
+ sub: [
344
+ { name: '--port <port>', description: 'HTTP port (default: 3000)' },
345
+ { name: '--host <host>', description: 'HTTP host (default: 127.0.0.1)' },
346
+ ],
347
+ });
348
+ }
349
+
350
+ builtins.push({
351
+ name: '--color [theme], --no-color',
352
+ description: 'Set color theme (default, ocean, warm, monochrome) or disable colors',
353
+ });
354
+
321
355
  if (builtins.length > 0) {
322
356
  helpInfo.builtins = builtins;
323
357
  }
@@ -331,7 +365,7 @@ export function getHelpInfo(cmd: AnyPadroneCommand, detail: HelpPreferences['det
331
365
  // ============================================================================
332
366
 
333
367
  export function generateHelp(rootCommand: AnyPadroneCommand, commandObj: AnyPadroneCommand = rootCommand, prefs?: HelpPreferences): string {
334
- const helpInfo = getHelpInfo(commandObj, prefs?.detail);
335
- const formatter = createFormatter(prefs?.format ?? 'auto', prefs?.detail);
368
+ const helpInfo = getHelpInfo(commandObj, prefs?.detail, prefs?.all);
369
+ const formatter = createFormatter(prefs?.format ?? 'auto', prefs?.detail, prefs?.theme, prefs?.all);
336
370
  return formatter.format(helpInfo);
337
371
  }
package/src/index.ts CHANGED
@@ -1,10 +1,24 @@
1
+ export type { AnsiStyle, ColorConfig, ColorTheme } from './colorizer.ts';
2
+ export { colorThemes } from './colorizer.ts';
1
3
  export { asyncSchema, buildReplCompleter, createPadrone } from './create.ts';
2
4
  export type { PadroneErrorOptions } from './errors.ts';
3
5
  export { ActionError, ConfigError, PadroneError, RoutingError, ValidationError } from './errors.ts';
4
6
  export type { HelpInfo } from './formatter.ts';
5
- export type { InteractiveMode, InteractivePromptConfig, PadroneRuntime } from './runtime.ts';
7
+ export type { PadroneMcpPreferences } from './mcp.ts';
8
+ export type {
9
+ InteractiveMode,
10
+ InteractivePromptConfig,
11
+ PadroneProgressIndicator,
12
+ PadroneProgressOptions,
13
+ PadroneRuntime,
14
+ PadroneSpinnerConfig,
15
+ PadroneSpinnerPreset,
16
+ } from './runtime.ts';
6
17
  export { REPL_SIGINT } from './runtime.ts';
18
+ export type { AsyncStreamMeta } from './stream.ts';
19
+ export { asyncStream } from './stream.ts';
7
20
  export type { InferArgsInput, InferArgsOutput, InferCommand } from './type-helpers.ts';
21
+ export type { Drained } from './type-utils.ts';
8
22
  export type {
9
23
  AnyPadroneBuilder,
10
24
  AnyPadroneCommand,
@@ -14,10 +28,24 @@ export type {
14
28
  PadroneBuilder,
15
29
  PadroneCommand,
16
30
  PadroneCommandResult,
31
+ PadroneDrainResult,
17
32
  PadroneParseResult,
18
33
  PadronePlugin,
19
34
  PadroneProgram,
35
+ PadroneProgressMessage,
36
+ PadroneProgressPrefs as PadroneProgressConfig,
20
37
  PadroneSchema,
38
+ PluginBaseContext,
39
+ PluginErrorContext,
40
+ PluginErrorResult,
41
+ PluginExecuteContext,
42
+ PluginExecuteResult,
43
+ PluginParseContext,
44
+ PluginParseResult,
45
+ PluginShutdownContext,
46
+ PluginStartContext,
47
+ PluginValidateContext,
48
+ PluginValidateResult,
21
49
  } from './types.ts';
22
50
  export type { UpdateCheckConfig } from './update-check.ts';
23
51
  export type { WrapConfig, WrapResult } from './wrap.ts';
@@ -1,3 +1,4 @@
1
+ import { JSON_SCHEMA_OPTS } from './args.ts';
1
2
  import type { InteractivePromptConfig, ResolvedPadroneRuntime } from './runtime.ts';
2
3
  import type { AnyPadroneCommand } from './types.ts';
3
4
 
@@ -44,6 +45,46 @@ export function detectPromptConfig(
44
45
  return { name, message, type: 'input', default: propSchema.default };
45
46
  }
46
47
 
48
+ /**
49
+ * Prompt a single field and validate it against the command's schema.
50
+ * Re-prompts with a warning until the user provides a valid value.
51
+ */
52
+ async function promptWithValidation(
53
+ field: string,
54
+ config: InteractivePromptConfig,
55
+ currentData: Record<string, unknown>,
56
+ command: AnyPadroneCommand,
57
+ runtime: ResolvedPadroneRuntime,
58
+ ): Promise<unknown> {
59
+ let promptConfig = config;
60
+
61
+ // eslint-disable-next-line no-constant-condition
62
+ while (true) {
63
+ const value = await runtime.prompt!(promptConfig);
64
+
65
+ if (!command.argsSchema) return value;
66
+
67
+ // Validate the full object with the new value to catch field-level issues
68
+ const testData = { ...currentData, [field]: value };
69
+ const validated = await command.argsSchema['~standard'].validate(testData);
70
+
71
+ if (!validated.issues) return value;
72
+
73
+ // Only keep issues whose path starts with this field
74
+ const fieldIssues = validated.issues.filter((issue: { path?: ReadonlyArray<PropertyKey> }) => {
75
+ const rootKey = issue.path?.[0];
76
+ return rootKey !== undefined && String(rootKey) === field;
77
+ });
78
+
79
+ if (fieldIssues.length === 0) return value;
80
+
81
+ // Warn the user and re-prompt with the invalid value as default
82
+ const messages = fieldIssues.map((i: { message: string }) => i.message).join('; ');
83
+ runtime.error(`Invalid value for "${field}": ${messages}`);
84
+ promptConfig = { ...config, default: value };
85
+ }
86
+ }
87
+
47
88
  /**
48
89
  * Prompt for missing interactive fields.
49
90
  * Runs after env/config preprocessing and before schema validation.
@@ -69,7 +110,7 @@ export async function promptInteractiveFields(
69
110
  let requiredFields: Set<string> = new Set();
70
111
  if (command.argsSchema) {
71
112
  try {
72
- const jsonSchema = command.argsSchema['~standard'].jsonSchema.input({ target: 'draft-2020-12' }) as Record<string, any>;
113
+ const jsonSchema = command.argsSchema['~standard'].jsonSchema.input(JSON_SCHEMA_OPTS) as Record<string, any>;
73
114
  if (jsonSchema.type === 'object' && jsonSchema.properties) {
74
115
  jsonProperties = jsonSchema.properties;
75
116
  }
@@ -108,14 +149,14 @@ export async function promptInteractiveFields(
108
149
  }
109
150
  }
110
151
 
111
- // Prompt each required interactive field
152
+ // Prompt each required interactive field with per-field validation
112
153
  for (const field of fieldsToPrompt) {
113
154
  const config = detectPromptConfig(field, jsonProperties[field], fieldDescriptions[field]);
114
155
  // When forced, use the current value as the default
115
156
  if (force && result[field] !== undefined) {
116
157
  config.default = result[field];
117
158
  }
118
- result[field] = await runtime.prompt(config);
159
+ result[field] = await promptWithValidation(field, config, result, command, runtime);
119
160
  }
120
161
 
121
162
  // Determine optional interactive fields
@@ -160,7 +201,7 @@ export async function promptInteractiveFields(
160
201
  if (force && result[field] !== undefined) {
161
202
  config.default = result[field];
162
203
  }
163
- result[field] = await runtime.prompt(config);
204
+ result[field] = await promptWithValidation(field, config, result, command, runtime);
164
205
  }
165
206
  }
166
207
  }