padrone 1.5.0 → 1.7.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 +44 -0
  2. package/README.md +15 -11
  3. package/dist/{args-D5PNDyNu.mjs → args-Cnq0nwSM.mjs} +91 -41
  4. package/dist/args-Cnq0nwSM.mjs.map +1 -0
  5. package/dist/codegen/index.mjs +4 -4
  6. package/dist/codegen/index.mjs.map +1 -1
  7. package/dist/commands-B_gufyR9.mjs +514 -0
  8. package/dist/commands-B_gufyR9.mjs.map +1 -0
  9. package/dist/{completion.mjs → completion-BEuflbDO.mjs} +12 -82
  10. package/dist/completion-BEuflbDO.mjs.map +1 -0
  11. package/dist/docs/index.d.mts +4 -4
  12. package/dist/docs/index.d.mts.map +1 -1
  13. package/dist/docs/index.mjs +10 -12
  14. package/dist/docs/index.mjs.map +1 -1
  15. package/dist/{errors-BiVrBgi6.mjs → errors-DA4KzK1M.mjs} +26 -3
  16. package/dist/errors-DA4KzK1M.mjs.map +1 -0
  17. package/dist/{formatter-DtHzbP22.d.mts → formatter-DrvhDMrq.d.mts} +3 -3
  18. package/dist/formatter-DrvhDMrq.d.mts.map +1 -0
  19. package/dist/{help-bbmu9-qd.mjs → help-BtxLgrF_.mjs} +190 -43
  20. package/dist/help-BtxLgrF_.mjs.map +1 -0
  21. package/dist/{types-Ch8Mk6Qb.d.mts → index-D6-7dz0l.d.mts} +634 -745
  22. package/dist/index-D6-7dz0l.d.mts.map +1 -0
  23. package/dist/index.d.mts +869 -36
  24. package/dist/index.d.mts.map +1 -1
  25. package/dist/index.mjs +3884 -1699
  26. package/dist/index.mjs.map +1 -1
  27. package/dist/{mcp-mLWIdUIu.mjs → mcp-6-Jw4Bpq.mjs} +13 -15
  28. package/dist/mcp-6-Jw4Bpq.mjs.map +1 -0
  29. package/dist/{serve-B0u43DK7.mjs → serve-YVTPzBCl.mjs} +12 -14
  30. package/dist/serve-YVTPzBCl.mjs.map +1 -0
  31. package/dist/{stream-BcC146Ud.mjs → stream-DC4H8YTx.mjs} +24 -3
  32. package/dist/stream-DC4H8YTx.mjs.map +1 -0
  33. package/dist/test.d.mts +5 -8
  34. package/dist/test.d.mts.map +1 -1
  35. package/dist/test.mjs +2 -13
  36. package/dist/test.mjs.map +1 -1
  37. package/dist/{update-check-CFX1FV3v.mjs → update-check-CZ2VqjnV.mjs} +16 -17
  38. package/dist/update-check-CZ2VqjnV.mjs.map +1 -0
  39. package/dist/zod.d.mts +2 -2
  40. package/dist/zod.d.mts.map +1 -1
  41. package/dist/zod.mjs +2 -2
  42. package/dist/zod.mjs.map +1 -1
  43. package/package.json +15 -12
  44. package/src/cli/completions.ts +14 -11
  45. package/src/cli/docs.ts +13 -10
  46. package/src/cli/doctor.ts +22 -18
  47. package/src/cli/index.ts +28 -82
  48. package/src/cli/init.ts +10 -7
  49. package/src/cli/link.ts +20 -16
  50. package/src/cli/wrap.ts +14 -11
  51. package/src/codegen/schema-to-code.ts +2 -2
  52. package/src/{args.ts → core/args.ts} +32 -225
  53. package/src/core/commands.ts +373 -0
  54. package/src/core/create.ts +301 -0
  55. package/src/core/default-runtime.ts +239 -0
  56. package/src/{errors.ts → core/errors.ts} +22 -0
  57. package/src/core/exec.ts +259 -0
  58. package/src/core/interceptors.ts +302 -0
  59. package/src/{parse.ts → core/parse.ts} +36 -89
  60. package/src/core/program-methods.ts +301 -0
  61. package/src/core/results.ts +229 -0
  62. package/src/core/runtime.ts +246 -0
  63. package/src/core/validate.ts +247 -0
  64. package/src/docs/index.ts +12 -13
  65. package/src/extension/auto-output.ts +146 -0
  66. package/src/extension/color.ts +38 -0
  67. package/src/extension/completion.ts +49 -0
  68. package/src/extension/config.ts +262 -0
  69. package/src/extension/env.ts +101 -0
  70. package/src/extension/help.ts +192 -0
  71. package/src/extension/index.ts +44 -0
  72. package/src/extension/ink.ts +93 -0
  73. package/src/extension/interactive.ts +106 -0
  74. package/src/extension/logger.ts +262 -0
  75. package/src/extension/man.ts +51 -0
  76. package/src/extension/mcp.ts +52 -0
  77. package/src/extension/progress-renderer.ts +338 -0
  78. package/src/extension/progress.ts +299 -0
  79. package/src/extension/repl.ts +94 -0
  80. package/src/extension/serve.ts +48 -0
  81. package/src/extension/signal.ts +87 -0
  82. package/src/extension/stdin.ts +62 -0
  83. package/src/extension/suggestions.ts +114 -0
  84. package/src/extension/timing.ts +81 -0
  85. package/src/extension/tracing.ts +175 -0
  86. package/src/extension/update-check.ts +77 -0
  87. package/src/extension/utils.ts +51 -0
  88. package/src/extension/version.ts +63 -0
  89. package/src/{completion.ts → feature/completion.ts} +12 -12
  90. package/src/{interactive.ts → feature/interactive.ts} +4 -4
  91. package/src/{mcp.ts → feature/mcp.ts} +12 -15
  92. package/src/{repl-loop.ts → feature/repl-loop.ts} +10 -13
  93. package/src/{serve.ts → feature/serve.ts} +11 -15
  94. package/src/feature/test.ts +262 -0
  95. package/src/{update-check.ts → feature/update-check.ts} +16 -16
  96. package/src/{wrap.ts → feature/wrap.ts} +10 -8
  97. package/src/index.ts +115 -30
  98. package/src/{formatter.ts → output/formatter.ts} +124 -176
  99. package/src/{help.ts → output/help.ts} +22 -8
  100. package/src/output/output-indicator.ts +87 -0
  101. package/src/output/primitives.ts +335 -0
  102. package/src/output/styling.ts +221 -0
  103. package/src/{zod.d.ts → schema/zod.d.ts} +1 -1
  104. package/src/schema/zod.ts +50 -0
  105. package/src/test.ts +2 -276
  106. package/src/types/args-meta.ts +151 -0
  107. package/src/types/builder.ts +718 -0
  108. package/src/types/command.ts +157 -0
  109. package/src/types/index.ts +60 -0
  110. package/src/types/interceptor.ts +296 -0
  111. package/src/types/preferences.ts +83 -0
  112. package/src/types/result.ts +71 -0
  113. package/src/types/schema.ts +19 -0
  114. package/src/util/dotenv.ts +244 -0
  115. package/src/{shell-utils.ts → util/shell-utils.ts} +26 -9
  116. package/src/{stream.ts → util/stream.ts} +27 -1
  117. package/src/{type-helpers.ts → util/type-helpers.ts} +23 -16
  118. package/src/{type-utils.ts → util/type-utils.ts} +71 -33
  119. package/src/util/utils.ts +51 -0
  120. package/src/zod.ts +1 -50
  121. package/dist/args-D5PNDyNu.mjs.map +0 -1
  122. package/dist/chunk-CjcI7cDX.mjs +0 -15
  123. package/dist/command-utils-B1D-HqCd.mjs +0 -1117
  124. package/dist/command-utils-B1D-HqCd.mjs.map +0 -1
  125. package/dist/completion.d.mts +0 -64
  126. package/dist/completion.d.mts.map +0 -1
  127. package/dist/completion.mjs.map +0 -1
  128. package/dist/errors-BiVrBgi6.mjs.map +0 -1
  129. package/dist/formatter-DtHzbP22.d.mts.map +0 -1
  130. package/dist/help-bbmu9-qd.mjs.map +0 -1
  131. package/dist/mcp-mLWIdUIu.mjs.map +0 -1
  132. package/dist/serve-B0u43DK7.mjs.map +0 -1
  133. package/dist/stream-BcC146Ud.mjs.map +0 -1
  134. package/dist/types-Ch8Mk6Qb.d.mts.map +0 -1
  135. package/dist/update-check-CFX1FV3v.mjs.map +0 -1
  136. package/src/command-utils.ts +0 -882
  137. package/src/create.ts +0 -1829
  138. package/src/runtime.ts +0 -497
  139. package/src/types.ts +0 -1291
  140. package/src/utils.ts +0 -140
  141. /package/src/{colorizer.ts → output/colorizer.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "padrone",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "description": "Create type-safe, interactive CLI apps with Zod schemas",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -89,18 +89,10 @@
89
89
  },
90
90
  "source": "./src/zod.ts"
91
91
  },
92
- "./completion": {
93
- "padrone@dev": "./src/completion.ts",
94
- "import": {
95
- "types": "./dist/completion.d.mts",
96
- "default": "./dist/completion.mjs"
97
- },
98
- "source": "./src/completion.ts"
99
- },
100
92
  "./package.json": "./package.json"
101
93
  },
102
94
  "scripts": {
103
- "build": "tsdown src/index.ts src/test.ts src/zod.ts src/codegen/index.ts src/docs/index.ts src/completion.ts --dts --sourcemap",
95
+ "build": "tsdown src/index.ts src/test.ts src/zod.ts src/codegen/index.ts src/docs/index.ts --dts --sourcemap",
104
96
  "start": "bun --conditions=padrone@dev src/cli/index.ts",
105
97
  "dev": "bun --conditions=padrone@dev --watch src/cli/index.ts",
106
98
  "test": "bun test --conditions=padrone@dev",
@@ -113,18 +105,29 @@
113
105
  "enquirer": "^2.4.1"
114
106
  },
115
107
  "devDependencies": {
116
- "ai": "^6.0.116",
117
- "tsdown": "^0.21.4",
108
+ "@types/react": "^19.2.14",
109
+ "ai": "^6.0.141",
110
+ "ink": "^6.8.0",
111
+ "react": "^19.2.4",
112
+ "tsdown": "^0.21.6",
118
113
  "zod": "^4.3.6"
119
114
  },
120
115
  "peerDependencies": {
121
116
  "ai": "5 || 6",
117
+ "ink": "^6.0.0",
118
+ "react": ">=18.0.0",
122
119
  "zod": "^3.25.0 || ^4.0.0"
123
120
  },
124
121
  "peerDependenciesMeta": {
125
122
  "ai": {
126
123
  "optional": true
127
124
  },
125
+ "ink": {
126
+ "optional": true
127
+ },
128
+ "react": {
129
+ "optional": true
130
+ },
128
131
  "zod": {
129
132
  "optional": true
130
133
  }
@@ -1,16 +1,19 @@
1
- import { basename } from 'node:path';
2
- import { detectShell, getCompletionInstallInstructions, type ShellType, setupCompletions } from '../completion.ts';
3
- import type { PadroneActionContext } from '../types.ts';
1
+ import * as z from 'zod/v4';
2
+ import { detectShell, getCompletionInstallInstructions, setupCompletions } from '../feature/completion.ts';
3
+ import type { PadroneActionContext } from '../types/index.ts';
4
4
 
5
- interface CompletionsArgs {
6
- appPath?: string;
7
- for?: ShellType;
8
- setup?: boolean;
9
- }
5
+ export const completionsSchema = z.object({
6
+ appPath: z.string().optional().describe('Path or name of the CLI program (defaults to padrone)'),
7
+ for: z.enum(['bash', 'zsh', 'fish', 'powershell']).optional().describe('Target shell (auto-detected if omitted)'),
8
+ setup: z.boolean().optional().default(false).describe('Write completions to shell config file'),
9
+ });
10
+
11
+ type CompletionsArgs = z.infer<typeof completionsSchema>;
10
12
 
11
- export function runCompletions(args: CompletionsArgs, _ctx: PadroneActionContext) {
13
+ export async function runCompletions(args: CompletionsArgs, _ctx: PadroneActionContext) {
14
+ const { basename } = await import('node:path');
12
15
  const programName = args.appPath ? basename(args.appPath).replace(/\.[cm]?[jt]sx?$/, '') : 'padrone';
13
- const shell = args.for ?? detectShell();
16
+ const shell = args.for ?? (await detectShell());
14
17
 
15
18
  if (!shell) {
16
19
  console.error('Could not detect shell. Use --for to specify one: bash, zsh, fish, powershell');
@@ -18,7 +21,7 @@ export function runCompletions(args: CompletionsArgs, _ctx: PadroneActionContext
18
21
  }
19
22
 
20
23
  if (args.setup) {
21
- const result = setupCompletions(programName, shell);
24
+ const result = await setupCompletions(programName, shell);
22
25
  const verb = result.updated ? 'Updated' : 'Added';
23
26
  console.log(`${verb} ${programName} completions in ${result.file}`);
24
27
  return;
package/src/cli/docs.ts CHANGED
@@ -1,15 +1,18 @@
1
1
  import { resolve } from 'node:path';
2
- import { isPadroneProgram } from '../command-utils.ts';
3
- import { type DocsFormat, generateDocs } from '../docs/index.ts';
4
- import type { PadroneActionContext } from '../types.ts';
2
+ import * as z from 'zod/v4';
3
+ import { isPadroneProgram } from '../core/commands.ts';
4
+ import { generateDocs } from '../docs/index.ts';
5
+ import type { PadroneActionContext } from '../types/index.ts';
5
6
 
6
- interface DocsArgs {
7
- entry: string;
8
- output?: string;
9
- format?: DocsFormat;
10
- includeHidden?: boolean;
11
- dryRun?: boolean;
12
- }
7
+ export const docsSchema = z.object({
8
+ entry: z.string().describe('Entry file that exports a Padrone program'),
9
+ output: z.string().optional().default('./docs/cli').describe('Output directory'),
10
+ format: z.enum(['markdown', 'html', 'man', 'json']).optional().default('markdown').describe('Output format'),
11
+ includeHidden: z.boolean().optional().default(false).describe('Include hidden commands and options'),
12
+ dryRun: z.boolean().optional().default(false).describe('Print what would be generated without writing'),
13
+ });
14
+
15
+ type DocsArgs = z.infer<typeof docsSchema>;
13
16
 
14
17
  export async function runDocs(args: DocsArgs, _ctx: PadroneActionContext) {
15
18
  const entryPath = resolve(args.entry);
package/src/cli/doctor.ts CHANGED
@@ -1,12 +1,15 @@
1
1
  import { resolve } from 'node:path';
2
- import { JSON_SCHEMA_OPTS } from '../args.ts';
3
- import { getCommand, isPadroneProgram } from '../command-utils.ts';
4
- import type { AnyPadroneCommand, PadroneActionContext } from '../types.ts';
2
+ import * as z from 'zod/v4';
3
+ import { getJsonSchema } from '../core/args.ts';
4
+ import { getCommand, isPadroneProgram } from '../core/commands.ts';
5
+ import type { AnyPadroneCommand, PadroneActionContext } from '../types/index.ts';
5
6
  import { detectEntry } from './link.ts';
6
7
 
7
- interface DoctorArgs {
8
- entry?: string;
9
- }
8
+ export const doctorSchema = z.object({
9
+ entry: z.string().optional().describe('Entry file that exports a Padrone program (auto-detected from package.json if omitted)'),
10
+ });
11
+
12
+ type DoctorArgs = z.infer<typeof doctorSchema>;
10
13
 
11
14
  type Severity = 'error' | 'warning';
12
15
 
@@ -85,7 +88,7 @@ function collectDiagnostics(cmd: AnyPadroneCommand, diagnostics: Diagnostic[]) {
85
88
  checkCommandsWithoutActions(allCommands, diagnostics);
86
89
  checkSchemasWithoutDescriptions(allCommands, diagnostics);
87
90
  checkConflictingPositionals(allCommands, diagnostics);
88
- checkUnusedPlugins(allCommands, diagnostics);
91
+ checkUnusedInterceptors(allCommands, diagnostics);
89
92
  checkDuplicateOptionFlagsAndAliases(allCommands, diagnostics);
90
93
  checkUnreachableCommands(allCommands, diagnostics);
91
94
  checkMissingCommandDescriptions(allCommands, diagnostics);
@@ -107,10 +110,10 @@ function commandDisplayName(cmd: AnyPadroneCommand): string {
107
110
  return cmd.path || cmd.name || '<root>';
108
111
  }
109
112
 
110
- function getJsonSchema(cmd: AnyPadroneCommand): Record<string, any> | null {
113
+ function getCommandJsonSchema(cmd: AnyPadroneCommand): Record<string, any> | null {
111
114
  try {
112
115
  if (!cmd.argsSchema) return null;
113
- return cmd.argsSchema['~standard'].jsonSchema.input(JSON_SCHEMA_OPTS) as Record<string, any>;
116
+ return getJsonSchema(cmd.argsSchema) as Record<string, any>;
114
117
  } catch {
115
118
  return null;
116
119
  }
@@ -167,7 +170,7 @@ function checkShadowedOptionNames(commands: AnyPadroneCommand[], diagnostics: Di
167
170
  }
168
171
 
169
172
  // Check option names in schema
170
- const jsonSchema = getJsonSchema(cmd);
173
+ const jsonSchema = getCommandJsonSchema(cmd);
171
174
  if (!jsonSchema?.properties) continue;
172
175
 
173
176
  for (const propName of Object.keys(jsonSchema.properties)) {
@@ -240,7 +243,7 @@ function checkCommandsWithoutActions(commands: AnyPadroneCommand[], diagnostics:
240
243
  */
241
244
  function checkSchemasWithoutDescriptions(commands: AnyPadroneCommand[], diagnostics: Diagnostic[]) {
242
245
  for (const cmd of commands) {
243
- const jsonSchema = getJsonSchema(cmd);
246
+ const jsonSchema = getCommandJsonSchema(cmd);
244
247
  if (!jsonSchema?.properties) continue;
245
248
 
246
249
  for (const [propName, propSchema] of Object.entries(jsonSchema.properties as Record<string, any>)) {
@@ -295,7 +298,7 @@ function checkConflictingPositionals(commands: AnyPadroneCommand[], diagnostics:
295
298
  }
296
299
 
297
300
  // Check for positional names that don't exist in schema
298
- const jsonSchema = getJsonSchema(cmd);
301
+ const jsonSchema = getCommandJsonSchema(cmd);
299
302
  if (jsonSchema?.properties) {
300
303
  for (const p of positional) {
301
304
  const name = (p as string).replace(/^\.\.\./, '');
@@ -312,21 +315,22 @@ function checkConflictingPositionals(commands: AnyPadroneCommand[], diagnostics:
312
315
  }
313
316
 
314
317
  /**
315
- * Check for plugins that don't define any phase handlers.
318
+ * Check for interceptors that don't define any phase handlers.
316
319
  */
317
- function checkUnusedPlugins(commands: AnyPadroneCommand[], diagnostics: Diagnostic[]) {
320
+ function checkUnusedInterceptors(commands: AnyPadroneCommand[], diagnostics: Diagnostic[]) {
318
321
  const phases = ['start', 'parse', 'validate', 'execute', 'error', 'shutdown'] as const;
319
322
 
320
323
  for (const cmd of commands) {
321
- if (!cmd.plugins) continue;
324
+ if (!cmd.interceptors) continue;
322
325
 
323
- for (const plugin of cmd.plugins) {
324
- const hasHandler = phases.some((phase) => typeof (plugin as any)[phase] === 'function');
326
+ for (const interceptor of cmd.interceptors) {
327
+ const phases_obj = interceptor.factory();
328
+ const hasHandler = phases.some((phase) => typeof (phases_obj as any)[phase] === 'function');
325
329
  if (!hasHandler) {
326
330
  diagnostics.push({
327
331
  severity: 'warning',
328
332
  command: commandDisplayName(cmd),
329
- message: `Plugin "${plugin.name}" has no phase handlers.`,
333
+ message: `Interceptor "${interceptor.meta.name}" has no phase handlers.`,
330
334
  });
331
335
  }
332
336
  }
package/src/cli/index.ts CHANGED
@@ -1,12 +1,11 @@
1
1
  import { createPadrone } from 'padrone';
2
2
  import pkg from 'padrone/package.json' with { type: 'json' };
3
- import * as z from 'zod/v4';
4
- import { runCompletions } from './completions.ts';
5
- import { runDocs } from './docs.ts';
6
- import { runDoctor } from './doctor.ts';
7
- import { runInit } from './init.ts';
8
- import { runLink, runUnlink } from './link.ts';
9
- import { runWrap } from './wrap.ts';
3
+ import { completionsSchema, runCompletions } from './completions.ts';
4
+ import { docsSchema, runDocs } from './docs.ts';
5
+ import { doctorSchema, runDoctor } from './doctor.ts';
6
+ import { initSchema, runInit } from './init.ts';
7
+ import { linkSchema, runLink, runUnlink, unlinkSchema } from './link.ts';
8
+ import { runWrap, wrapSchema } from './wrap.ts';
10
9
 
11
10
  const PadroneCLI = createPadrone('padrone')
12
11
  .configure({
@@ -19,17 +18,9 @@ const PadroneCLI = createPadrone('padrone')
19
18
  .configure({
20
19
  description: 'Scaffold a new Padrone CLI project',
21
20
  })
22
- .arguments(
23
- z.object({
24
- name: z.string().optional().describe('Project name (defaults to directory name)'),
25
- description: z.string().optional().describe('Project description'),
26
- version: z.string().optional().default('0.1.0').describe('Initial version'),
27
- dir: z.string().optional().describe('Target directory (defaults to current directory)'),
28
- }),
29
- {
30
- positional: ['dir'],
31
- },
32
- )
21
+ .arguments(initSchema, {
22
+ positional: ['dir'],
23
+ })
33
24
  .async()
34
25
  .action(runInit),
35
26
  )
@@ -38,18 +29,9 @@ const PadroneCLI = createPadrone('padrone')
38
29
  .configure({
39
30
  description: 'Generate documentation for a Padrone CLI program',
40
31
  })
41
- .arguments(
42
- z.object({
43
- entry: z.string().describe('Entry file that exports a Padrone program'),
44
- output: z.string().optional().default('./docs/cli').describe('Output directory'),
45
- format: z.enum(['markdown', 'html', 'man', 'json']).optional().default('markdown').describe('Output format'),
46
- includeHidden: z.boolean().optional().default(false).describe('Include hidden commands and options'),
47
- dryRun: z.boolean().optional().default(false).describe('Print what would be generated without writing'),
48
- }),
49
- {
50
- positional: ['entry'],
51
- },
52
- )
32
+ .arguments(docsSchema, {
33
+ positional: ['entry'],
34
+ })
53
35
  .async()
54
36
  .action(runDocs),
55
37
  )
@@ -58,14 +40,9 @@ const PadroneCLI = createPadrone('padrone')
58
40
  .configure({
59
41
  description: 'Lint and validate a Padrone CLI program definition',
60
42
  })
61
- .arguments(
62
- z.object({
63
- entry: z.string().optional().describe('Entry file that exports a Padrone program (auto-detected from package.json if omitted)'),
64
- }),
65
- {
66
- positional: ['entry'],
67
- },
68
- )
43
+ .arguments(doctorSchema, {
44
+ positional: ['entry'],
45
+ })
69
46
  .async()
70
47
  .action(runDoctor),
71
48
  )
@@ -74,16 +51,9 @@ const PadroneCLI = createPadrone('padrone')
74
51
  .configure({
75
52
  description: 'Show shell completion install instructions for a Padrone CLI program',
76
53
  })
77
- .arguments(
78
- z.object({
79
- appPath: z.string().optional().describe('Path or name of the CLI program (defaults to padrone)'),
80
- for: z.enum(['bash', 'zsh', 'fish', 'powershell']).optional().describe('Target shell (auto-detected if omitted)'),
81
- setup: z.boolean().optional().default(false).describe('Write completions to shell config file'),
82
- }),
83
- {
84
- positional: ['appPath'],
85
- },
86
- )
54
+ .arguments(completionsSchema, {
55
+ positional: ['appPath'],
56
+ })
87
57
  .action(runCompletions),
88
58
  )
89
59
  .command('link', (cmd) =>
@@ -91,17 +61,9 @@ const PadroneCLI = createPadrone('padrone')
91
61
  .configure({
92
62
  description: 'Link a Padrone CLI program for global use during development',
93
63
  })
94
- .arguments(
95
- z.object({
96
- entry: z.string().optional().describe('Entry file (auto-detected from package.json bin field)'),
97
- name: z.string().optional().describe('Command name (auto-detected from package.json)'),
98
- list: z.boolean().optional().default(false).describe('List all linked programs'),
99
- setup: z.boolean().optional().default(false).describe('Add ~/.padrone/bin to PATH in shell config'),
100
- }),
101
- {
102
- positional: ['entry'],
103
- },
104
- )
64
+ .arguments(linkSchema, {
65
+ positional: ['entry'],
66
+ })
105
67
  .async()
106
68
  .action(runLink),
107
69
  )
@@ -110,14 +72,9 @@ const PadroneCLI = createPadrone('padrone')
110
72
  .configure({
111
73
  description: 'Remove a previously linked Padrone CLI program',
112
74
  })
113
- .arguments(
114
- z.object({
115
- name: z.string().optional().describe('Program name to unlink (auto-detected from current directory)'),
116
- }),
117
- {
118
- positional: ['name'],
119
- },
120
- )
75
+ .arguments(unlinkSchema, {
76
+ positional: ['name'],
77
+ })
121
78
  .async()
122
79
  .action(runUnlink),
123
80
  )
@@ -126,21 +83,10 @@ const PadroneCLI = createPadrone('padrone')
126
83
  .configure({
127
84
  description: 'Generate a Padrone wrapper for an existing CLI tool',
128
85
  })
129
- .arguments(
130
- z.object({
131
- command: z.string().describe('CLI command to wrap (e.g. gh, docker, kubectl)'),
132
- source: z.enum(['help', 'fish', 'zsh']).optional().default('help').describe('Parsing source (default: help)'),
133
- output: z.string().optional().describe('Output directory (default: ./src/<command>)'),
134
- depth: z.number().default(4).optional().describe('Max subcommand depth'),
135
- dryRun: z.boolean().optional().default(false).describe('Print what would be generated without writing'),
136
- overwrite: z.boolean().optional().default(false).describe('Overwrite existing files'),
137
- yes: z.boolean().optional().default(false).describe('Skip confirmation prompt'),
138
- }),
139
- {
140
- positional: ['command'],
141
- fields: { yes: { alias: 'y' } },
142
- },
143
- )
86
+ .arguments(wrapSchema, {
87
+ positional: ['command'],
88
+ fields: { yes: { alias: 'y' } },
89
+ })
144
90
  .async()
145
91
  .action(runWrap),
146
92
  );
package/src/cli/init.ts CHANGED
@@ -1,14 +1,17 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { basename, resolve } from 'node:path';
3
3
  import { createFileEmitter, template } from 'padrone/codegen';
4
- import type { PadroneActionContext } from '../types.ts';
4
+ import * as z from 'zod/v4';
5
+ import type { PadroneActionContext } from '../types/index.ts';
5
6
 
6
- interface InitArgs {
7
- name?: string;
8
- description?: string;
9
- version?: string;
10
- dir?: string;
11
- }
7
+ export const initSchema = z.object({
8
+ name: z.string().optional().describe('Project name (defaults to directory name)'),
9
+ description: z.string().optional().describe('Project description'),
10
+ version: z.string().optional().default('0.1.0').describe('Initial version'),
11
+ dir: z.string().optional().describe('Target directory (defaults to current directory)'),
12
+ });
13
+
14
+ type InitArgs = z.infer<typeof initSchema>;
12
15
 
13
16
  const packageJsonTemplate = template(`{
14
17
  "name": "{{name}}",
package/src/cli/link.ts CHANGED
@@ -1,19 +1,23 @@
1
1
  import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
2
2
  import { homedir } from 'node:os';
3
3
  import { basename, dirname, resolve } from 'node:path';
4
- import { detectShell, getRcFile, type ShellType, writeToRcFile } from '../shell-utils.ts';
5
- import type { PadroneActionContext } from '../types.ts';
6
-
7
- interface LinkArgs {
8
- entry?: string;
9
- name?: string;
10
- list?: boolean;
11
- setup?: boolean;
12
- }
4
+ import * as z from 'zod/v4';
5
+ import type { PadroneActionContext } from '../types/index.ts';
6
+ import { detectShell, getRcFile, type ShellType, writeToRcFile } from '../util/shell-utils.ts';
13
7
 
14
- interface UnlinkArgs {
15
- name?: string;
16
- }
8
+ export const linkSchema = z.object({
9
+ entry: z.string().optional().describe('Entry file (auto-detected from package.json bin field)'),
10
+ name: z.string().optional().describe('Command name (auto-detected from package.json)'),
11
+ list: z.boolean().optional().default(false).describe('List all linked programs'),
12
+ setup: z.boolean().optional().default(false).describe('Add ~/.padrone/bin to PATH in shell config'),
13
+ });
14
+
15
+ export const unlinkSchema = z.object({
16
+ name: z.string().optional().describe('Program name to unlink (auto-detected from current directory)'),
17
+ });
18
+
19
+ type LinkArgs = z.infer<typeof linkSchema>;
20
+ type UnlinkArgs = z.infer<typeof unlinkSchema>;
17
21
 
18
22
  interface LinkEntry {
19
23
  name: string;
@@ -143,8 +147,8 @@ function buildPathSnippet(shell: ShellType, binDir: string): string {
143
147
  }
144
148
  }
145
149
 
146
- function setupPath(shell: ShellType): { file: string; updated: boolean } {
147
- const rcFile = getRcFile(shell);
150
+ async function setupPath(shell: ShellType): Promise<{ file: string; updated: boolean }> {
151
+ const rcFile = await getRcFile(shell);
148
152
  if (!rcFile) {
149
153
  throw new Error(`Could not determine config file for ${shell}.`);
150
154
  }
@@ -268,13 +272,13 @@ export async function runLink(args: LinkArgs, ctx: PadroneActionContext) {
268
272
 
269
273
  if (!isInPath(BIN_DIR)) {
270
274
  if (args.setup) {
271
- const shell = detectShell();
275
+ const shell = await detectShell();
272
276
  if (!shell) {
273
277
  error('Could not detect shell. Add the PATH manually:');
274
278
  error(` export PATH="${BIN_DIR}:$PATH"`);
275
279
  return;
276
280
  }
277
- const result = setupPath(shell);
281
+ const result = await setupPath(shell);
278
282
  const verb = result.updated ? 'Updated' : 'Added';
279
283
  output(`${verb} PATH in ${result.file}`);
280
284
  output('Restart your shell or run:');
package/src/cli/wrap.ts CHANGED
@@ -1,20 +1,23 @@
1
1
  import { resolve } from 'node:path';
2
2
  import { createCodeBuilder, createFileEmitter, generateCommandTree } from 'padrone/codegen';
3
+ import * as z from 'zod/v4';
3
4
  import type { DiscoverySource } from '../codegen/discovery.ts';
4
5
  import { discoverCli } from '../codegen/discovery.ts';
5
6
  import { template } from '../codegen/template.ts';
6
7
  import type { GeneratorContext } from '../codegen/types.ts';
7
- import type { PadroneActionContext } from '../types.ts';
8
-
9
- interface WrapArgs {
10
- command: string;
11
- source?: DiscoverySource;
12
- output?: string;
13
- depth?: number;
14
- dryRun?: boolean;
15
- overwrite?: boolean;
16
- yes?: boolean;
17
- }
8
+ import type { PadroneActionContext } from '../types/index.ts';
9
+
10
+ export const wrapSchema = z.object({
11
+ command: z.string().describe('CLI command to wrap (e.g. gh, docker, kubectl)'),
12
+ source: z.enum(['help', 'fish', 'zsh']).optional().default('help').describe('Parsing source (default: help)'),
13
+ output: z.string().optional().describe('Output directory (default: ./src/<command>)'),
14
+ depth: z.number().default(4).optional().describe('Max subcommand depth'),
15
+ dryRun: z.boolean().optional().default(false).describe('Print what would be generated without writing'),
16
+ overwrite: z.boolean().optional().default(false).describe('Overwrite existing files'),
17
+ yes: z.boolean().optional().default(false).describe('Skip confirmation prompt'),
18
+ });
19
+
20
+ type WrapArgs = z.infer<typeof wrapSchema>;
18
21
 
19
22
  export async function runWrap(args: WrapArgs, ctx: PadroneActionContext) {
20
23
  const { output, error } = ctx.runtime;
@@ -1,5 +1,5 @@
1
1
  import type { StandardSchemaV1 } from '@standard-schema/spec';
2
- import { JSON_SCHEMA_OPTS } from '../args.ts';
2
+ import { getJsonSchema } from '../core/args.ts';
3
3
  import type { FieldMeta } from './types.ts';
4
4
 
5
5
  interface SchemaToCodeResult {
@@ -58,7 +58,7 @@ function jsonSchemaPropertyToZod(prop: Record<string, any>, required: boolean, a
58
58
  */
59
59
  export function schemaToCode(schema: StandardSchemaV1): SchemaToCodeResult {
60
60
  try {
61
- const jsonSchema = (schema as any)['~standard'].jsonSchema.input(JSON_SCHEMA_OPTS) as Record<string, any>;
61
+ const jsonSchema = getJsonSchema(schema as any) as Record<string, any>;
62
62
  return jsonSchemaToCode(jsonSchema);
63
63
  } catch {
64
64
  return { code: 'z.unknown()', imports: ['z'] };