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.
- package/CHANGELOG.md +115 -0
- package/README.md +108 -283
- package/dist/args-Cnq0nwSM.mjs +272 -0
- package/dist/args-Cnq0nwSM.mjs.map +1 -0
- package/dist/codegen/index.d.mts +28 -3
- package/dist/codegen/index.d.mts.map +1 -1
- package/dist/codegen/index.mjs +169 -19
- package/dist/codegen/index.mjs.map +1 -1
- package/dist/commands-B_gufyR9.mjs +514 -0
- package/dist/commands-B_gufyR9.mjs.map +1 -0
- package/dist/{completion.mjs → completion-BEuflbDO.mjs} +86 -108
- package/dist/completion-BEuflbDO.mjs.map +1 -0
- package/dist/docs/index.d.mts +22 -2
- package/dist/docs/index.d.mts.map +1 -1
- package/dist/docs/index.mjs +92 -7
- package/dist/docs/index.mjs.map +1 -1
- package/dist/errors-CL63UOzt.mjs +137 -0
- package/dist/errors-CL63UOzt.mjs.map +1 -0
- package/dist/{formatter-ClUK5hcQ.d.mts → formatter-DrvhDMrq.d.mts} +35 -6
- package/dist/formatter-DrvhDMrq.d.mts.map +1 -0
- package/dist/help-B5Kk83of.mjs +849 -0
- package/dist/help-B5Kk83of.mjs.map +1 -0
- package/dist/index-BaU3X6dY.d.mts +1178 -0
- package/dist/index-BaU3X6dY.d.mts.map +1 -0
- package/dist/index.d.mts +763 -36
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +3608 -1534
- package/dist/index.mjs.map +1 -1
- package/dist/mcp-BM-d0nZi.mjs +377 -0
- package/dist/mcp-BM-d0nZi.mjs.map +1 -0
- package/dist/serve-Bk0JUlCj.mjs +402 -0
- package/dist/serve-Bk0JUlCj.mjs.map +1 -0
- package/dist/stream-DC4H8YTx.mjs +77 -0
- package/dist/stream-DC4H8YTx.mjs.map +1 -0
- package/dist/test.d.mts +5 -8
- package/dist/test.d.mts.map +1 -1
- package/dist/test.mjs +5 -27
- package/dist/test.mjs.map +1 -1
- package/dist/{update-check-EbNDkzyV.mjs → update-check-CZ2VqjnV.mjs} +16 -17
- package/dist/update-check-CZ2VqjnV.mjs.map +1 -0
- package/dist/zod.d.mts +32 -0
- package/dist/zod.d.mts.map +1 -0
- package/dist/zod.mjs +50 -0
- package/dist/zod.mjs.map +1 -0
- package/package.json +20 -9
- package/src/cli/completions.ts +14 -11
- package/src/cli/docs.ts +13 -16
- package/src/cli/doctor.ts +213 -24
- package/src/cli/index.ts +28 -82
- package/src/cli/init.ts +12 -10
- package/src/cli/link.ts +22 -18
- package/src/cli/wrap.ts +14 -11
- package/src/codegen/discovery.ts +80 -28
- package/src/codegen/index.ts +2 -1
- package/src/codegen/parsers/bash.ts +179 -0
- package/src/codegen/schema-to-code.ts +2 -1
- package/src/core/args.ts +296 -0
- package/src/core/commands.ts +373 -0
- package/src/core/create.ts +268 -0
- package/src/{runtime.ts → core/default-runtime.ts} +70 -135
- package/src/{errors.ts → core/errors.ts} +22 -0
- package/src/core/exec.ts +259 -0
- package/src/core/interceptors.ts +302 -0
- package/src/{parse.ts → core/parse.ts} +36 -89
- package/src/core/program-methods.ts +301 -0
- package/src/core/results.ts +229 -0
- package/src/core/runtime.ts +246 -0
- package/src/core/validate.ts +247 -0
- package/src/docs/index.ts +124 -11
- package/src/extension/auto-output.ts +95 -0
- package/src/extension/color.ts +38 -0
- package/src/extension/completion.ts +49 -0
- package/src/extension/config.ts +262 -0
- package/src/extension/env.ts +101 -0
- package/src/extension/help.ts +192 -0
- package/src/extension/index.ts +43 -0
- package/src/extension/ink.ts +93 -0
- package/src/extension/interactive.ts +106 -0
- package/src/extension/logger.ts +214 -0
- package/src/extension/man.ts +51 -0
- package/src/extension/mcp.ts +52 -0
- package/src/extension/progress-renderer.ts +338 -0
- package/src/extension/progress.ts +299 -0
- package/src/extension/repl.ts +94 -0
- package/src/extension/serve.ts +48 -0
- package/src/extension/signal.ts +87 -0
- package/src/extension/stdin.ts +62 -0
- package/src/extension/suggestions.ts +114 -0
- package/src/extension/timing.ts +81 -0
- package/src/extension/tracing.ts +175 -0
- package/src/extension/update-check.ts +77 -0
- package/src/extension/utils.ts +51 -0
- package/src/extension/version.ts +63 -0
- package/src/{completion.ts → feature/completion.ts} +130 -57
- package/src/{interactive.ts → feature/interactive.ts} +47 -6
- package/src/feature/mcp.ts +387 -0
- package/src/{repl-loop.ts → feature/repl-loop.ts} +26 -16
- package/src/feature/serve.ts +438 -0
- package/src/feature/test.ts +262 -0
- package/src/{update-check.ts → feature/update-check.ts} +16 -16
- package/src/{wrap.ts → feature/wrap.ts} +27 -27
- package/src/index.ts +120 -11
- package/src/output/colorizer.ts +154 -0
- package/src/{formatter.ts → output/formatter.ts} +281 -135
- package/src/{help.ts → output/help.ts} +62 -15
- package/src/{zod.d.ts → schema/zod.d.ts} +1 -1
- package/src/schema/zod.ts +50 -0
- package/src/test.ts +2 -285
- package/src/types/args-meta.ts +151 -0
- package/src/types/builder.ts +697 -0
- package/src/types/command.ts +157 -0
- package/src/types/index.ts +59 -0
- package/src/types/interceptor.ts +296 -0
- package/src/types/preferences.ts +83 -0
- package/src/types/result.ts +71 -0
- package/src/types/schema.ts +19 -0
- package/src/util/dotenv.ts +244 -0
- package/src/{shell-utils.ts → util/shell-utils.ts} +26 -9
- package/src/util/stream.ts +101 -0
- package/src/{type-helpers.ts → util/type-helpers.ts} +23 -16
- package/src/{type-utils.ts → util/type-utils.ts} +99 -37
- package/src/util/utils.ts +51 -0
- package/src/zod.ts +1 -0
- package/dist/args-CVDbyyzG.mjs +0 -199
- package/dist/args-CVDbyyzG.mjs.map +0 -1
- package/dist/chunk-y_GBKt04.mjs +0 -5
- package/dist/completion.d.mts +0 -64
- package/dist/completion.d.mts.map +0 -1
- package/dist/completion.mjs.map +0 -1
- package/dist/formatter-ClUK5hcQ.d.mts.map +0 -1
- package/dist/help-CcBe91bV.mjs +0 -1254
- package/dist/help-CcBe91bV.mjs.map +0 -1
- package/dist/types-DjIdJN5G.d.mts +0 -1059
- package/dist/types-DjIdJN5G.d.mts.map +0 -1
- package/dist/update-check-EbNDkzyV.mjs.map +0 -1
- package/src/args.ts +0 -461
- package/src/colorizer.ts +0 -41
- package/src/command-utils.ts +0 -532
- package/src/create.ts +0 -1477
- package/src/types.ts +0 -1109
- package/src/utils.ts +0 -140
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { extractSchemaMetadata } from '
|
|
2
|
-
import
|
|
3
|
-
import type
|
|
1
|
+
import { extractSchemaMetadata, getJsonSchema } from '../core/args.ts';
|
|
2
|
+
import type { AnyPadroneCommand } from '../types/index.ts';
|
|
3
|
+
import { detectShell, getRcFile, type ShellType, writeToRcFile } from '../util/shell-utils.ts';
|
|
4
4
|
|
|
5
|
-
export { detectShell, escapeRegExp, getRcFile, type ShellType, writeToRcFile } from '
|
|
5
|
+
export { detectShell, escapeRegExp, getRcFile, type ShellType, writeToRcFile } from '../util/shell-utils.ts';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Collects all commands from a program recursively.
|
|
@@ -22,11 +22,19 @@ function collectAllCommands(cmd: AnyPadroneCommand): AnyPadroneCommand[] {
|
|
|
22
22
|
return result;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
interface ExtractedArg {
|
|
26
|
+
name: string;
|
|
27
|
+
alias?: string;
|
|
28
|
+
isBoolean: boolean;
|
|
29
|
+
enum?: string[];
|
|
30
|
+
description?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
25
33
|
/**
|
|
26
34
|
* Extracts all argument names from a command's schema.
|
|
27
35
|
*/
|
|
28
|
-
function extractArguments(cmd: AnyPadroneCommand):
|
|
29
|
-
const argList:
|
|
36
|
+
function extractArguments(cmd: AnyPadroneCommand): ExtractedArg[] {
|
|
37
|
+
const argList: ExtractedArg[] = [];
|
|
30
38
|
|
|
31
39
|
if (!cmd.argsSchema) return argList;
|
|
32
40
|
|
|
@@ -34,21 +42,24 @@ function extractArguments(cmd: AnyPadroneCommand): { name: string; alias?: strin
|
|
|
34
42
|
const argsMeta = cmd.meta?.fields;
|
|
35
43
|
const { aliases } = extractSchemaMetadata(cmd.argsSchema, argsMeta, cmd.meta?.autoAlias);
|
|
36
44
|
|
|
37
|
-
//
|
|
38
|
-
const
|
|
39
|
-
for (const [
|
|
40
|
-
|
|
45
|
+
// Build reverse map: argName → aliasName
|
|
46
|
+
const argToAlias: Record<string, string> = {};
|
|
47
|
+
for (const [aliasName, argName] of Object.entries(aliases)) {
|
|
48
|
+
if (!argToAlias[argName]) argToAlias[argName] = aliasName;
|
|
41
49
|
}
|
|
42
50
|
|
|
43
|
-
const jsonSchema = cmd.argsSchema
|
|
51
|
+
const jsonSchema = getJsonSchema(cmd.argsSchema) as Record<string, any>;
|
|
44
52
|
|
|
45
53
|
if (jsonSchema.type === 'object' && jsonSchema.properties) {
|
|
46
54
|
for (const [key, prop] of Object.entries(jsonSchema.properties as Record<string, any>)) {
|
|
47
|
-
const
|
|
55
|
+
const enumValues = (prop.enum ?? prop.items?.enum) as string[] | undefined;
|
|
56
|
+
const optMeta = argsMeta?.[key];
|
|
48
57
|
argList.push({
|
|
49
58
|
name: key,
|
|
50
|
-
alias:
|
|
59
|
+
alias: argToAlias[key],
|
|
51
60
|
isBoolean: prop?.type === 'boolean',
|
|
61
|
+
enum: enumValues,
|
|
62
|
+
description: optMeta?.description ?? prop.description,
|
|
52
63
|
});
|
|
53
64
|
}
|
|
54
65
|
}
|
|
@@ -59,6 +70,23 @@ function extractArguments(cmd: AnyPadroneCommand): { name: string; alias?: strin
|
|
|
59
70
|
return argList;
|
|
60
71
|
}
|
|
61
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Collects unique args across all commands, preserving first-seen enum values.
|
|
75
|
+
*/
|
|
76
|
+
function collectUniqueArgs(program: AnyPadroneCommand, commands: AnyPadroneCommand[]): Map<string, ExtractedArg> {
|
|
77
|
+
const seen = new Map<string, ExtractedArg>();
|
|
78
|
+
|
|
79
|
+
for (const cmd of [program, ...commands]) {
|
|
80
|
+
for (const arg of extractArguments(cmd)) {
|
|
81
|
+
if (!seen.has(arg.name)) {
|
|
82
|
+
seen.set(arg.name, arg);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return seen;
|
|
88
|
+
}
|
|
89
|
+
|
|
62
90
|
/**
|
|
63
91
|
* Generates a Bash completion script for the program.
|
|
64
92
|
*/
|
|
@@ -66,21 +94,41 @@ export function generateBashCompletion(program: AnyPadroneCommand): string {
|
|
|
66
94
|
const programName = program.name;
|
|
67
95
|
const commands = collectAllCommands(program);
|
|
68
96
|
const commandNames = commands.map((c) => c.name).join(' ');
|
|
97
|
+
const uniqueArgs = collectUniqueArgs(program, commands);
|
|
69
98
|
|
|
70
|
-
// Collect all
|
|
99
|
+
// Collect all option names
|
|
71
100
|
const allArguments = new Set<string>();
|
|
72
101
|
allArguments.add('--help');
|
|
73
102
|
allArguments.add('--version');
|
|
74
103
|
|
|
75
|
-
for (const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (arg.alias) allArguments.add(`-${arg.alias}`);
|
|
79
|
-
}
|
|
104
|
+
for (const arg of uniqueArgs.values()) {
|
|
105
|
+
allArguments.add(`--${arg.name}`);
|
|
106
|
+
if (arg.alias) allArguments.add(`--${arg.alias}`);
|
|
80
107
|
}
|
|
81
108
|
|
|
82
109
|
const argsList = Array.from(allArguments).join(' ');
|
|
83
110
|
|
|
111
|
+
// Build case branches for options with enum values
|
|
112
|
+
const enumCases: string[] = [];
|
|
113
|
+
for (const arg of uniqueArgs.values()) {
|
|
114
|
+
if (!arg.enum || arg.enum.length === 0) continue;
|
|
115
|
+
const values = arg.enum.join(' ');
|
|
116
|
+
const patterns = [`--${arg.name}`];
|
|
117
|
+
if (arg.alias) patterns.push(`--${arg.alias}`);
|
|
118
|
+
enumCases.push(` ${patterns.join('|')}) COMPREPLY=($(compgen -W "${values}" -- "$cur")); return 0 ;;`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const enumBlock =
|
|
122
|
+
enumCases.length > 0
|
|
123
|
+
? `
|
|
124
|
+
# Complete option values
|
|
125
|
+
case "$prev" in
|
|
126
|
+
${enumCases.join('\n')}
|
|
127
|
+
esac
|
|
128
|
+
|
|
129
|
+
`
|
|
130
|
+
: '\n';
|
|
131
|
+
|
|
84
132
|
return `###-begin-${programName}-completion-###
|
|
85
133
|
#
|
|
86
134
|
# ${programName} command completion script
|
|
@@ -104,8 +152,7 @@ if type complete &>/dev/null; then
|
|
|
104
152
|
|
|
105
153
|
local commands="${commandNames}"
|
|
106
154
|
local args="${argsList}"
|
|
107
|
-
|
|
108
|
-
# Complete args when current word starts with -
|
|
155
|
+
${enumBlock} # Complete args when current word starts with -
|
|
109
156
|
if [[ "$cur" == -* ]]; then
|
|
110
157
|
COMPREPLY=($(compgen -W "$args" -- "$cur"))
|
|
111
158
|
return 0
|
|
@@ -161,26 +208,24 @@ export function generateZshCompletion(program: AnyPadroneCommand): string {
|
|
|
161
208
|
})
|
|
162
209
|
.join('\n');
|
|
163
210
|
|
|
164
|
-
// Collect all args with descriptions
|
|
211
|
+
// Collect all args with descriptions and enum values
|
|
165
212
|
const argumentCompletions: string[] = [];
|
|
166
213
|
argumentCompletions.push(" '--help[Show help information]'");
|
|
167
214
|
argumentCompletions.push(" '--version[Show version number]'");
|
|
168
215
|
|
|
169
|
-
const
|
|
216
|
+
const uniqueArgs = collectUniqueArgs(program, commands);
|
|
170
217
|
|
|
171
|
-
for (const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
seenArgs.add(arg.name);
|
|
218
|
+
for (const arg of uniqueArgs.values()) {
|
|
219
|
+
const desc = arg.description || '';
|
|
220
|
+
const escapedDesc = desc.replace(/'/g, "'\\''").replace(/\[/g, '\\[').replace(/\]/g, '\\]');
|
|
175
221
|
|
|
176
|
-
|
|
177
|
-
|
|
222
|
+
// Zsh action spec for enum values: :label:(val1 val2 val3)
|
|
223
|
+
const valueAction = arg.enum?.length ? `: :(${arg.enum.join(' ')})` : '';
|
|
178
224
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
225
|
+
if (arg.alias) {
|
|
226
|
+
argumentCompletions.push(` {--${arg.alias},--${arg.name}}'[${escapedDesc}]${valueAction}'`);
|
|
227
|
+
} else {
|
|
228
|
+
argumentCompletions.push(` '--${arg.name}[${escapedDesc}]${valueAction}'`);
|
|
184
229
|
}
|
|
185
230
|
}
|
|
186
231
|
|
|
@@ -253,21 +298,18 @@ export function generateFishCompletion(program: AnyPadroneCommand): string {
|
|
|
253
298
|
lines.push(`complete -c ${programName} -l help -d 'Show help information'`);
|
|
254
299
|
lines.push(`complete -c ${programName} -l version -d 'Show version number'`);
|
|
255
300
|
|
|
256
|
-
const
|
|
301
|
+
const uniqueArgs = collectUniqueArgs(program, commands);
|
|
257
302
|
|
|
258
|
-
for (const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const desc = cmd.meta?.fields?.[arg.name]?.description || '';
|
|
264
|
-
const escapedDesc = desc.replace(/'/g, "\\'");
|
|
303
|
+
for (const arg of uniqueArgs.values()) {
|
|
304
|
+
const desc = arg.description || '';
|
|
305
|
+
const escapedDesc = desc.replace(/'/g, "\\'");
|
|
306
|
+
// Fish: -xa 'val1 val2' provides exclusive value completions
|
|
307
|
+
const valueFlag = arg.enum?.length ? ` -xa '${arg.enum.join(' ')}'` : '';
|
|
265
308
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
}
|
|
309
|
+
if (arg.alias) {
|
|
310
|
+
lines.push(`complete -c ${programName} -l ${arg.name} -s ${arg.alias} -d '${escapedDesc}'${valueFlag}`);
|
|
311
|
+
} else {
|
|
312
|
+
lines.push(`complete -c ${programName} -l ${arg.name} -d '${escapedDesc}'${valueFlag}`);
|
|
271
313
|
}
|
|
272
314
|
}
|
|
273
315
|
|
|
@@ -282,9 +324,41 @@ export function generateFishCompletion(program: AnyPadroneCommand): string {
|
|
|
282
324
|
export function generatePowerShellCompletion(program: AnyPadroneCommand): string {
|
|
283
325
|
const programName = program.name;
|
|
284
326
|
const commands = collectAllCommands(program);
|
|
327
|
+
const uniqueArgs = collectUniqueArgs(program, commands);
|
|
285
328
|
|
|
286
329
|
const commandNames = commands.map((c) => `'${c.name}'`).join(', ');
|
|
287
330
|
|
|
331
|
+
// Collect all option names
|
|
332
|
+
const argNames: string[] = ["'--help'", "'--version'"];
|
|
333
|
+
for (const arg of uniqueArgs.values()) {
|
|
334
|
+
argNames.push(`'--${arg.name}'`);
|
|
335
|
+
if (arg.alias) argNames.push(`'--${arg.alias}'`);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Build switch cases for option value completion
|
|
339
|
+
const enumCases: string[] = [];
|
|
340
|
+
for (const arg of uniqueArgs.values()) {
|
|
341
|
+
if (!arg.enum || arg.enum.length === 0) continue;
|
|
342
|
+
const values = arg.enum.map((v) => `'${v}'`).join(', ');
|
|
343
|
+
const patterns = [`'--${arg.name}'`];
|
|
344
|
+
if (arg.alias) patterns.push(`'--${arg.alias}'`);
|
|
345
|
+
enumCases.push(` ${patterns.join(', ')} { @(${values}) | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
346
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
347
|
+
}; return }`);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const enumBlock =
|
|
351
|
+
enumCases.length > 0
|
|
352
|
+
? `
|
|
353
|
+
# Complete option values
|
|
354
|
+
$prevWord = $commandAst.CommandElements | Select-Object -Last 2 | Select-Object -First 1
|
|
355
|
+
switch ($prevWord) {
|
|
356
|
+
${enumCases.join('\n')}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
`
|
|
360
|
+
: '\n';
|
|
361
|
+
|
|
288
362
|
return `###-begin-${programName}-completion-###
|
|
289
363
|
#
|
|
290
364
|
# ${programName} command completion script for PowerShell
|
|
@@ -296,9 +370,8 @@ Register-ArgumentCompleter -Native -CommandName ${programName} -ScriptBlock {
|
|
|
296
370
|
param($wordToComplete, $commandAst, $cursorPosition)
|
|
297
371
|
|
|
298
372
|
$commands = @(${commandNames})
|
|
299
|
-
$args = @('
|
|
300
|
-
|
|
301
|
-
if ($wordToComplete -like '-*') {
|
|
373
|
+
$args = @(${argNames.join(', ')})
|
|
374
|
+
${enumBlock} if ($wordToComplete -like '-*') {
|
|
302
375
|
$args | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
303
376
|
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
304
377
|
}
|
|
@@ -366,7 +439,7 @@ ${programName} completion powershell >> $PROFILE`;
|
|
|
366
439
|
* Generates the completion output with automatic shell detection.
|
|
367
440
|
* If shell is not specified, detects the current shell and provides instructions.
|
|
368
441
|
*/
|
|
369
|
-
export function generateCompletionOutput(program: AnyPadroneCommand, shell?: ShellType): string {
|
|
442
|
+
export async function generateCompletionOutput(program: AnyPadroneCommand, shell?: ShellType): Promise<string> {
|
|
370
443
|
const programName = program.name;
|
|
371
444
|
|
|
372
445
|
if (shell) {
|
|
@@ -374,7 +447,7 @@ export function generateCompletionOutput(program: AnyPadroneCommand, shell?: She
|
|
|
374
447
|
}
|
|
375
448
|
|
|
376
449
|
// Auto-detect shell and provide instructions
|
|
377
|
-
const detectedShell = detectShell();
|
|
450
|
+
const detectedShell = await detectShell();
|
|
378
451
|
|
|
379
452
|
if (detectedShell) {
|
|
380
453
|
const instructions = getCompletionInstallInstructions(programName, detectedShell);
|
|
@@ -419,10 +492,10 @@ export interface SetupCompletionsResult {
|
|
|
419
492
|
* Sets up shell completions by writing an eval snippet to the appropriate shell config file.
|
|
420
493
|
* Uses marker comments for idempotency — re-running replaces the existing block.
|
|
421
494
|
*/
|
|
422
|
-
export function setupCompletions(programName: string, shell: ShellType): SetupCompletionsResult {
|
|
423
|
-
const { existsSync, mkdirSync, writeFileSync } =
|
|
424
|
-
const { join } =
|
|
425
|
-
const { homedir } =
|
|
495
|
+
export async function setupCompletions(programName: string, shell: ShellType): Promise<SetupCompletionsResult> {
|
|
496
|
+
const { existsSync, mkdirSync, writeFileSync } = await import('node:fs');
|
|
497
|
+
const { join } = await import('node:path');
|
|
498
|
+
const { homedir } = await import('node:os');
|
|
426
499
|
|
|
427
500
|
const beginMarker = `###-begin-${programName}-completion-###`;
|
|
428
501
|
const endMarker = `###-end-${programName}-completion-###`;
|
|
@@ -437,7 +510,7 @@ export function setupCompletions(programName: string, shell: ShellType): SetupCo
|
|
|
437
510
|
return { file: filePath, updated: existed };
|
|
438
511
|
}
|
|
439
512
|
|
|
440
|
-
const rcFile = getRcFile(shell);
|
|
513
|
+
const rcFile = await getRcFile(shell);
|
|
441
514
|
if (!rcFile) {
|
|
442
515
|
throw new Error(`Could not determine config file for ${shell}.`);
|
|
443
516
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type {
|
|
1
|
+
import { getJsonSchema } from '../core/args.ts';
|
|
2
|
+
import type { InteractivePromptConfig, ResolvedPadroneRuntime } from '../core/runtime.ts';
|
|
3
|
+
import type { AnyPadroneCommand } from '../types/index.ts';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Auto-detect the prompt type for a field based on its JSON schema property definition.
|
|
@@ -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
|
|
113
|
+
const jsonSchema = getJsonSchema(command.argsSchema) 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
|
|
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
|
|
204
|
+
result[field] = await promptWithValidation(field, config, result, command, runtime);
|
|
164
205
|
}
|
|
165
206
|
}
|
|
166
207
|
}
|