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
@@ -0,0 +1,157 @@
1
+ import type { StandardSchemaV1 } from '@standard-schema/spec';
2
+ import type { PadroneRuntime, ResolvedPadroneRuntime } from '../core/runtime.ts';
3
+ import type { FullCommandName } from '../util/type-utils.ts';
4
+ import type { PadroneArgsSchemaMeta } from './args-meta.ts';
5
+ import type { AnyPadroneProgram } from './builder.ts';
6
+ import type { RegisteredInterceptor } from './interceptor.ts';
7
+ import type { PadroneSchema } from './schema.ts';
8
+
9
+ type UnknownRecord = Record<string, unknown>;
10
+ type DefaultArgs = UnknownRecord | void;
11
+
12
+ /**
13
+ * Read-only metadata about a Padrone program.
14
+ * Returned by `program.info` to expose program-level properties without leaking internal command internals.
15
+ */
16
+ export type PadroneProgramMeta<TName extends string = string> = {
17
+ /** The program name (CLI binary name). */
18
+ name: TName;
19
+ /** Display title shown in help output. */
20
+ title?: string;
21
+ /** Program description. */
22
+ description?: string;
23
+ /** Program version string. */
24
+ version?: string;
25
+ /** Usage examples shown in help output. */
26
+ examples?: string[];
27
+ /** Whether the program is deprecated. */
28
+ deprecated?: boolean | string;
29
+ /** Names of registered subcommands. */
30
+ commands: string[];
31
+ };
32
+
33
+ /**
34
+ * Context object passed as the second argument to command action handlers.
35
+ * Contains the resolved runtime, the executing command, and the program instance.
36
+ */
37
+ export type PadroneActionContext<TContext = unknown> = {
38
+ /** The resolved runtime for this command (I/O, env, config, etc.). */
39
+ runtime: ResolvedPadroneRuntime;
40
+ /** The command being executed. */
41
+ command: AnyPadroneCommand;
42
+ /** The root program instance. */
43
+ program: AnyPadroneProgram;
44
+ /**
45
+ * Cancellation signal that fires when the process receives SIGINT, SIGTERM, or SIGHUP.
46
+ * Use with `fetch()`, child processes, or any API that accepts `AbortSignal`.
47
+ * Check `signal.aborted` to test if cancellation was requested.
48
+ * The `signal.reason` is a `PadroneSignal` string ('SIGINT', 'SIGTERM', or 'SIGHUP').
49
+ */
50
+ signal: AbortSignal;
51
+ /** User-defined context object. Set via `.context()` on the builder and provided at `cli()`/`eval()` time. */
52
+ context: TContext;
53
+ /** Which API entry point triggered this execution. */
54
+ caller: 'cli' | 'eval' | 'run' | 'repl' | 'serve' | 'mcp' | 'tool';
55
+ };
56
+
57
+ /**
58
+ * Configuration for a command.
59
+ */
60
+ export type PadroneCommandConfig = {
61
+ /** A short title for the command, displayed in help. */
62
+ title?: string;
63
+ /** A longer description of what the command does. */
64
+ description?: string;
65
+ /** The version of the command. */
66
+ version?: string;
67
+ /** Whether the command is deprecated, or a message explaining the deprecation. */
68
+ deprecated?: boolean | string;
69
+ /** Whether the command should be hidden from help output. */
70
+ hidden?: boolean;
71
+ /** Group name for organizing this command under a labeled section in help output. */
72
+ group?: string;
73
+ /** Usage examples shown in help output. Each entry is a command-line invocation string. */
74
+ examples?: string[];
75
+ /**
76
+ * Whether this command performs a mutation (create, update, delete).
77
+ * - In `serve()`: mutation commands accept POST only; non-mutation commands accept GET and POST.
78
+ * - In `mcp()`: sets `annotations.destructiveHint` on the tool definition.
79
+ * - In `tool()`: defaults `needsApproval` to `true` when not explicitly set.
80
+ */
81
+ mutation?: boolean;
82
+ };
83
+
84
+ export type PadroneCommand<
85
+ TName extends string = string,
86
+ TParentName extends string = '',
87
+ TArgs extends PadroneSchema = PadroneSchema<DefaultArgs>,
88
+ TRes = void,
89
+ TCommands extends [...AnyPadroneCommand[]] = [],
90
+ TAliases extends string[] = string[],
91
+ TAsync extends boolean = false,
92
+ TContext = unknown,
93
+ TContextProvided = unknown,
94
+ > = {
95
+ name: TName;
96
+ path: FullCommandName<TName, TParentName>;
97
+ title?: string;
98
+ description?: string;
99
+ version?: string;
100
+ /** Alternative names that can be used to invoke this command. Derived from the names passed to command(). */
101
+ aliases?: TAliases;
102
+ deprecated?: boolean | string;
103
+ hidden?: boolean;
104
+ /** Group name for organizing this command under a labeled section in help output. */
105
+ group?: string;
106
+ /** Whether this command performs a mutation (create, update, delete). Affects HTTP method in serve (POST-only) and MCP tool annotations (destructiveHint). */
107
+ mutation?: boolean;
108
+ needsApproval?: boolean | ((args: TArgs) => Promise<boolean> | boolean);
109
+ /** Usage examples shown in help output. Each entry is a command-line invocation string. */
110
+ examples?: string[];
111
+ argsSchema?: TArgs;
112
+ meta?: GetArgsMeta<TArgs>;
113
+ action?: (args: StandardSchemaV1.InferOutput<TArgs>, ctx: PadroneActionContext<TContext & TContextProvided>) => TRes;
114
+ /** Runtime flag indicating this command uses async validation. Set by `.async()` or `asyncSchema()`. */
115
+ isAsync?: boolean;
116
+ /** Runtime configuration for I/O abstraction. */
117
+ runtime?: PadroneRuntime;
118
+
119
+ /** Transform function that maps parent context to this command's context. Set by `.context(transform)`. */
120
+ contextTransform?: (ctx: unknown) => unknown;
121
+
122
+ /** Interceptors registered on this command. Collected from the parent chain at execution time. */
123
+ interceptors?: RegisteredInterceptor[];
124
+
125
+ parent?: AnyPadroneCommand;
126
+ commands?: TCommands;
127
+
128
+ /** @deprecated Internal use only */
129
+ '~types': {
130
+ name: TName;
131
+ parentName: TParentName;
132
+ path: FullCommandName<TName, TParentName>;
133
+ aliases: TAliases;
134
+ argsSchema: TArgs;
135
+ argsInput: StandardSchemaV1.InferInput<TArgs>;
136
+ argsOutput: StandardSchemaV1.InferOutput<TArgs>;
137
+ result: TRes;
138
+ commands: TCommands;
139
+ async: TAsync;
140
+ context: TContext;
141
+ contextProvided: TContextProvided;
142
+ };
143
+ };
144
+
145
+ export type AnyPadroneCommand = PadroneCommand<string, any, any, any, [...AnyPadroneCommand[]], string[], any, any, any>;
146
+
147
+ /**
148
+ * Base type for extracting command information from builder or program.
149
+ * Both PadroneBuilder and PadroneProgram share this structure.
150
+ */
151
+ export type CommandTypesBase = {
152
+ '~types': {
153
+ command: AnyPadroneCommand;
154
+ };
155
+ };
156
+
157
+ export type GetArgsMeta<TArgs extends PadroneSchema> = PadroneArgsSchemaMeta<NonNullable<StandardSchemaV1.InferInput<TArgs>>>;
@@ -0,0 +1,59 @@
1
+ export type { PadroneArgsSchemaMeta, PadroneFieldMeta, SingleChar, StdinConfig } from './args-meta.ts';
2
+ export type {
3
+ AnyPadroneBuilder,
4
+ AnyPadroneProgram,
5
+ PadroneBuilder,
6
+ PadroneBuilderMethods,
7
+ PadroneExtension,
8
+ PadroneProgram,
9
+ } from './builder.ts';
10
+ export type {
11
+ AnyPadroneCommand,
12
+ CommandTypesBase,
13
+ GetArgsMeta,
14
+ PadroneActionContext,
15
+ PadroneCommand,
16
+ PadroneCommandConfig,
17
+ PadroneProgramMeta,
18
+ } from './command.ts';
19
+ export type {
20
+ ExtractInterceptorContext,
21
+ ExtractInterceptorRequires,
22
+ InterceptorBaseContext,
23
+ InterceptorDefBuilder,
24
+ InterceptorErrorContext,
25
+ InterceptorErrorResult,
26
+ InterceptorExecuteContext,
27
+ InterceptorExecuteResult,
28
+ InterceptorFactory,
29
+ InterceptorMeta,
30
+ InterceptorNextOverrides,
31
+ InterceptorParseContext,
32
+ InterceptorParseResult,
33
+ InterceptorPhases,
34
+ InterceptorShutdownContext,
35
+ InterceptorStartContext,
36
+ InterceptorValidateContext,
37
+ InterceptorValidateResult,
38
+ PadroneContextInterceptor,
39
+ PadroneInterceptor,
40
+ PadroneInterceptorFn,
41
+ RegisteredInterceptor,
42
+ ResolvedInterceptor,
43
+ } from './interceptor.ts';
44
+ export type {
45
+ PadroneCliPreferences,
46
+ PadroneEvalPreferences,
47
+ PadroneReplPreferences,
48
+ PadroneReplSpacing,
49
+ } from './preferences.ts';
50
+ export type {
51
+ GetArguments,
52
+ GetResults,
53
+ MaybePromiseCommandResult,
54
+ PadroneAPI,
55
+ PadroneCommandResult,
56
+ PadroneDrainResult,
57
+ PadroneParseResult,
58
+ } from './result.ts';
59
+ export type { AsyncPadroneSchema, PadroneSchema } from './schema.ts';
@@ -0,0 +1,296 @@
1
+ import type { StandardSchemaV1 } from '@standard-schema/spec';
2
+ import type { ResolvedPadroneRuntime } from '../core/runtime.ts';
3
+ import type { AnyPadroneProgram } from './builder.ts';
4
+ import type { AnyPadroneCommand, PadroneActionContext } from './command.ts';
5
+
6
+ // ---------------------------------------------------------------------------
7
+ // Interceptor system
8
+ // ---------------------------------------------------------------------------
9
+
10
+ /** Base context shared across all interceptor phases within a single execution. */
11
+ export type InterceptorBaseContext<TContext = unknown> = {
12
+ /** The resolved command for this execution. In the parse phase, this is the root program. */
13
+ command: AnyPadroneCommand;
14
+ /** The raw CLI input string (undefined when invoked without input). */
15
+ input: string | undefined;
16
+ /** Cancellation signal that fires when the process receives a termination signal. */
17
+ signal: AbortSignal;
18
+ /** User-defined context object, resolved through the command's parent chain. */
19
+ context: TContext;
20
+ /** The resolved runtime for this execution. Interceptors can override this before calling `next()`. */
21
+ runtime: ResolvedPadroneRuntime;
22
+ /** The program instance. Available for extensions that need program-level methods. */
23
+ program: AnyPadroneProgram;
24
+ /** The invocation method that triggered this execution (e.g. 'cli', 'eval', 'run'). */
25
+ caller: PadroneActionContext['caller'];
26
+ };
27
+
28
+ /** Context for the parse phase. */
29
+ export type InterceptorParseContext<TContext = unknown> = InterceptorBaseContext<TContext>;
30
+
31
+ /** Result returned by the parse phase's `next()`. */
32
+ export type InterceptorParseResult = {
33
+ command: AnyPadroneCommand;
34
+ rawArgs: Record<string, unknown>;
35
+ positionalArgs: string[];
36
+ };
37
+
38
+ /** Context for the validate phase. */
39
+ export type InterceptorValidateContext<TContext = unknown> = InterceptorBaseContext<TContext> & {
40
+ /** Raw named arguments extracted by the parser. Mutable — modify before `next()` to inject/override values. */
41
+ rawArgs: Record<string, unknown>;
42
+ /** Positional argument strings extracted by the parser. */
43
+ positionalArgs: string[];
44
+ /** Interactive mode override (set by the interactive extension when `--interactive` / `-i` flag is used). */
45
+ interactive?: boolean;
46
+ /** Interactive mode preference from eval/cli options. Available for the interactive extension. */
47
+ evalInteractive?: boolean;
48
+ };
49
+
50
+ /** Result returned by the validate phase's `next()`. */
51
+ export type InterceptorValidateResult<TArgs = unknown> = {
52
+ args: TArgs;
53
+ argsResult: StandardSchemaV1.Result<TArgs>;
54
+ };
55
+
56
+ /** Context for the execute phase. Includes validate context fields (rawArgs, positionalArgs). */
57
+ export type InterceptorExecuteContext<TArgs = unknown, TContext = unknown> = InterceptorValidateContext<TContext> & {
58
+ /** Validated arguments that will be passed to the action. Mutable — modify before `next()` to override. */
59
+ args: TArgs;
60
+ };
61
+
62
+ /** Result returned by the execute phase's `next()`. */
63
+ export type InterceptorExecuteResult<TResult = unknown> = {
64
+ result: TResult;
65
+ };
66
+
67
+ /** Context for the start phase. Runs before parsing, wraps the entire pipeline. */
68
+ export type InterceptorStartContext<TContext = unknown> = InterceptorBaseContext<TContext>;
69
+
70
+ /** Context for the error phase. Called when the pipeline throws. Includes pipeline state accumulated before the error. */
71
+ export type InterceptorErrorContext<TContext = unknown> = InterceptorBaseContext<TContext> & {
72
+ /** The error that was thrown. */
73
+ error: unknown;
74
+ /** Raw named arguments (available if parse completed). */
75
+ rawArgs?: Record<string, unknown>;
76
+ /** Positional argument strings (available if parse completed). */
77
+ positionalArgs?: string[];
78
+ /** Validated arguments (available if validate completed). */
79
+ args?: unknown;
80
+ };
81
+
82
+ /** Result returned by the error phase's `next()`. */
83
+ export type InterceptorErrorResult<TResult = unknown> = {
84
+ /** The error (possibly transformed). Set to `undefined` to suppress the error. */
85
+ error?: unknown;
86
+ /** A replacement result when suppressing the error. */
87
+ result?: TResult;
88
+ };
89
+
90
+ /** Context for the shutdown phase. Always runs after the pipeline (success or failure). Includes pipeline state accumulated before completion. */
91
+ export type InterceptorShutdownContext<TResult = unknown, TContext = unknown> = InterceptorBaseContext<TContext> & {
92
+ /** The error, if the pipeline failed (after error phase processing). */
93
+ error?: unknown;
94
+ /** The pipeline result, if it succeeded. */
95
+ result?: TResult;
96
+ /** Raw named arguments (available if parse completed). */
97
+ rawArgs?: Record<string, unknown>;
98
+ /** Positional argument strings (available if parse completed). */
99
+ positionalArgs?: string[];
100
+ /** Validated arguments (available if validate completed). */
101
+ args?: unknown;
102
+ };
103
+
104
+ /** Overrides passable to `next()`. Provides autocomplete for common fields; accepts any phase-specific fields. */
105
+ export type InterceptorNextOverrides = Partial<InterceptorBaseContext> & Record<string, unknown>;
106
+
107
+ /**
108
+ * A phase handler function for the interceptor middleware chain.
109
+ *
110
+ * - `TCtx` — the context object available to the handler.
111
+ * - `TNextResult` — the typed result returned by `next()`, giving the handler type-safe access to downstream output.
112
+ * - `TReturn` — the type the handler itself returns. Defaults to `TNextResult` but can be wider,
113
+ * allowing interceptors to transform or replace the result (e.g., error-recovery interceptors returning a different type).
114
+ */
115
+ type InterceptorPhaseHandler<TCtx, TNextResult, TReturn = TNextResult> = (
116
+ ctx: TCtx,
117
+ next: (overrides?: InterceptorNextOverrides) => TNextResult | Promise<TNextResult>,
118
+ ) => TReturn | Promise<TReturn>;
119
+
120
+ // ---------------------------------------------------------------------------
121
+ // Interceptor meta, phases, and factory
122
+ // ---------------------------------------------------------------------------
123
+
124
+ /** Static metadata for an interceptor. Always available at registration time without calling the factory. */
125
+ export type InterceptorMeta = {
126
+ /** Display name for this interceptor. Used for identification in logs and debugging. */
127
+ name: string;
128
+ /**
129
+ * Optional unique identifier for deduplication. When multiple interceptors share the same `id`,
130
+ * only the last one registered is kept. Useful for allowing downstream code to override
131
+ * an interceptor without accumulating duplicates.
132
+ */
133
+ id?: string;
134
+ /**
135
+ * Ordering hint. Lower values run as outer layers (earlier before `next()`, later after).
136
+ * Interceptors with the same order preserve registration order. Defaults to `0`.
137
+ */
138
+ order?: number;
139
+ /**
140
+ * When `true`, the interceptor is skipped during execution. Combined with `id`-based deduplication,
141
+ * this lets downstream code disable an interceptor by re-registering it with `disabled: true`.
142
+ */
143
+ disabled?: boolean;
144
+ /**
145
+ * When `false`, the interceptor applies only to the command it was registered on
146
+ * and is not inherited by subcommands. Defaults to `true`.
147
+ */
148
+ inherit?: boolean;
149
+ };
150
+
151
+ /**
152
+ * Phase handler definitions returned by an interceptor factory.
153
+ * The factory's closure provides typed, scoped cross-phase state.
154
+ *
155
+ * Type parameters:
156
+ * - `TArgs` — the validated arguments type (output of the args schema).
157
+ * - `TResult` — the command's return type.
158
+ */
159
+ export type InterceptorPhases<TArgs = unknown, TResult = unknown, TContext = unknown> = {
160
+ /**
161
+ * Runs before the pipeline (parse → validate → execute). `next()` proceeds to the pipeline.
162
+ * Root interceptors only. Use for startup tasks like telemetry, update checks, or global config loading.
163
+ */
164
+ start?: InterceptorPhaseHandler<InterceptorStartContext<TContext>, unknown>;
165
+ /** Intercepts command routing and raw argument extraction. */
166
+ parse?: InterceptorPhaseHandler<InterceptorParseContext<TContext>, InterceptorParseResult>;
167
+ /** Intercepts argument preprocessing and schema validation. Interactive prompting is handled by the interactive extension. */
168
+ validate?: InterceptorPhaseHandler<InterceptorValidateContext<TContext>, InterceptorValidateResult<TArgs>, InterceptorValidateResult>;
169
+ /** Intercepts handler execution. */
170
+ execute?: InterceptorPhaseHandler<
171
+ InterceptorExecuteContext<TArgs, TContext>,
172
+ InterceptorExecuteResult<TResult>,
173
+ InterceptorExecuteResult
174
+ >;
175
+ /**
176
+ * Called when the pipeline throws an error. `next()` passes to the next error handler
177
+ * (innermost returns `{ error }` unchanged). Return `{ result }` without `error` to suppress.
178
+ */
179
+ error?: InterceptorPhaseHandler<InterceptorErrorContext<TContext>, InterceptorErrorResult<TResult>, InterceptorErrorResult>;
180
+ /**
181
+ * Always runs after the pipeline completes (success or failure). `next()` calls the next shutdown handler.
182
+ * Use for cleanup: closing connections, flushing logs, etc.
183
+ */
184
+ shutdown?: InterceptorPhaseHandler<InterceptorShutdownContext<TResult, TContext>, void>;
185
+ };
186
+
187
+ /**
188
+ * Factory function that creates phase handlers for an interceptor.
189
+ * Called once per command execution — the closure provides typed, scoped cross-phase state across phases.
190
+ */
191
+ export type InterceptorFactory<TArgs = unknown, TResult = unknown, TContext = unknown> = () => InterceptorPhases<TArgs, TResult, TContext>;
192
+
193
+ /**
194
+ * A self-contained interceptor value: a factory function with static metadata as own properties.
195
+ * Created via `defineInterceptor(meta, factory)`. This is the distributable form — a single
196
+ * importable value that packages can export.
197
+ *
198
+ * Also accepted directly by `.intercept()` as the single-argument form.
199
+ * Call `.provides<T>()` to brand it as a context-providing interceptor.
200
+ */
201
+ export type PadroneInterceptorFn<TArgs = unknown, TResult = unknown, TContext = unknown> = InterceptorFactory<TArgs, TResult, TContext> &
202
+ InterceptorMeta & {
203
+ /** Brand this interceptor as providing additional context of type `TProvides`. No-op at runtime; purely a type-level cast. */
204
+ provides: <TProvides>() => PadroneContextInterceptor<TProvides, TArgs, TResult, TContext>;
205
+ /**
206
+ * Brand this interceptor as requiring context of type `TRequires` to be available.
207
+ * `.intercept()` will produce a compile error if the required context is not satisfied.
208
+ * Use optional properties for soft requirements: `.requires<{ db: DB; logger?: Logger }>()`.
209
+ * No-op at runtime; purely a type-level cast.
210
+ */
211
+ requires: <TRequires>() => PadroneInterceptorFn<TArgs, TResult, TContext> & InterceptorRequiresBrand<TRequires>;
212
+ };
213
+
214
+ /**
215
+ * A Padrone interceptor in its single-value distributable form.
216
+ * Alias for `PadroneInterceptorFn` — this is the primary public type.
217
+ *
218
+ * Create with `defineInterceptor(meta, factory)` or pass `(meta, factory)` directly to `.intercept()`.
219
+ */
220
+ export type PadroneInterceptor<TArgs = unknown, TResult = unknown, TContext = unknown> = PadroneInterceptorFn<TArgs, TResult, TContext>;
221
+
222
+ /**
223
+ * A context-providing interceptor. Carries a phantom `'~context'` brand declaring what it adds
224
+ * to the command context. When registered via `.intercept()`, the builder's context type is
225
+ * intersected with `TProvides`.
226
+ *
227
+ * Created by calling `.provides<T>()` on a `PadroneInterceptorFn`.
228
+ * Chain `.requires<T>()` to also declare context dependencies.
229
+ */
230
+ export type PadroneContextInterceptor<TProvides = unknown, TArgs = unknown, TResult = unknown, TContext = unknown> = Omit<
231
+ PadroneInterceptorFn<TArgs, TResult, TContext>,
232
+ 'requires'
233
+ > &
234
+ InterceptorFactory<TArgs, TResult, TContext> & {
235
+ /** Phantom brand — declares the context type this interceptor provides. */
236
+ '~context': TProvides;
237
+ /** Like `.requires()` on `PadroneInterceptorFn` but preserves the `'~context'` brand. */
238
+ requires: <TRequires>() => PadroneContextInterceptor<TProvides, TArgs, TResult, TContext> & InterceptorRequiresBrand<TRequires>;
239
+ };
240
+
241
+ /**
242
+ * Phantom brand for context requirements. Uses a contravariant function type so that
243
+ * `.intercept()` overloads can check `TAvailableContext extends TRequires` via assignability.
244
+ */
245
+ type InterceptorRequiresBrand<TRequires> = { '~contextRequires': (ctx: TRequires) => void };
246
+
247
+ /** Extracts the provided context type from a context-providing interceptor, or `unknown` if not branded. */
248
+ export type ExtractInterceptorContext<T> = T extends { '~context': infer C } ? C : unknown;
249
+
250
+ /** Extracts the required context type from an interceptor, or `unknown` if no requirements. */
251
+ export type ExtractInterceptorRequires<T> = T extends { '~contextRequires': (ctx: infer R) => void } ? R : unknown;
252
+
253
+ /**
254
+ * Checks whether an interceptor's context requirements are satisfied by the available context.
255
+ * Returns `true` if the interceptor has no requirements or if the available context extends the requirements.
256
+ */
257
+ export type InterceptorRequiresCheck<TInterceptor, TAvailableContext> = TInterceptor extends {
258
+ '~contextRequires': (ctx: infer TReq) => void;
259
+ }
260
+ ? TAvailableContext extends TReq
261
+ ? true
262
+ : false
263
+ : true;
264
+
265
+ /** Error brand returned by `.intercept()` when the interceptor's required context is not satisfied. */
266
+ export type InterceptorRequiresError = {
267
+ readonly '~error': 'Required context not satisfied. Ensure required interceptors are registered before this one.';
268
+ };
269
+
270
+ /**
271
+ * Builder returned by `defineInterceptor(meta)` (single-arg form).
272
+ * Call `.requires<T>()` to declare (and type) the context dependency, then `.factory()` to provide the phase handlers.
273
+ */
274
+ export type InterceptorDefBuilder<TContext = unknown, TBrand = unknown> = {
275
+ /** Declare the context type this interceptor requires. Sets `TContext` for phase handler typing. */
276
+ requires: <TRequires>() => InterceptorDefBuilder<TRequires, InterceptorRequiresBrand<TRequires>>;
277
+ /** Provide the interceptor factory. Phase handlers receive the typed `TContext` from `.requires()`. */
278
+ factory: <TArgs = unknown, TResult = unknown>(
279
+ factory: InterceptorFactory<TArgs, TResult, TContext>,
280
+ ) => PadroneInterceptorFn<TArgs, TResult, TContext> & TBrand;
281
+ };
282
+
283
+ /**
284
+ * Internal stored form on command objects. Separates static metadata from the factory
285
+ * so that deduplication and sorting can happen without calling the factory.
286
+ */
287
+ export type RegisteredInterceptor = {
288
+ meta: InterceptorMeta;
289
+ factory: InterceptorFactory<any, any, any>;
290
+ };
291
+
292
+ /**
293
+ * Resolved interceptor: metadata merged with phase handlers after the factory has been called.
294
+ * Used internally by the runtime chain runner.
295
+ */
296
+ export type ResolvedInterceptor = InterceptorMeta & InterceptorPhases<any, any, any>;
@@ -0,0 +1,83 @@
1
+ import type { PadroneRuntime } from '../core/runtime.ts';
2
+ import type { PadroneActionContext } from './command.ts';
3
+
4
+ /**
5
+ * Options for `repl()` to customize the REPL session.
6
+ */
7
+ /** A single spacing value: blank line (`true`), separator string, or an array of these for multiple lines. */
8
+ export type PadroneReplSpacing = boolean | string | (boolean | string)[];
9
+
10
+ export type PadroneReplPreferences<TScope extends string = string> = {
11
+ /** The prompt string displayed before each input, or a function returning it. Defaults to `"<programName>> "`. */
12
+ prompt?: string | (() => string);
13
+ /**
14
+ * A greeting message displayed when the REPL starts.
15
+ * When not provided, defaults to `"Welcome to <name> v<version>"` (or just `"Welcome to <name>"` if no version).
16
+ * Set to `false` to suppress the default greeting entirely.
17
+ */
18
+ greeting?: string | false;
19
+ /**
20
+ * A hint message displayed below the greeting in dimmed text.
21
+ * When not provided, defaults to `'Type ".help" for more information, ".exit" to quit.'`.
22
+ * Set to `false` to suppress the hint.
23
+ */
24
+ hint?: string | false;
25
+ /** Initial history entries (most recent last). Arrow keys navigate history in the terminal. */
26
+ history?: string[];
27
+ /** Set to `false` to disable tab completion. Defaults to `true`. */
28
+ completion?: boolean;
29
+ /**
30
+ * Add spacing/separators around each command's output.
31
+ * A spacing value can be:
32
+ * - `true` — blank line
33
+ * - A string — separator line (single char like `'─'` repeats to terminal width, multi-char prints as-is)
34
+ * - An array of the above — multiple lines in order (e.g. `[true, '─']` for blank line then separator)
35
+ *
36
+ * Shorthand applies to both before and after. Use `{ before?, after? }` for independent control.
37
+ */
38
+ spacing?: PadroneReplSpacing | { before?: PadroneReplSpacing; after?: PadroneReplSpacing };
39
+ /** Prefix each line of command output/error with this string (e.g. `'│ '`, `' '`, `'▎ '`). */
40
+ outputPrefix?: string;
41
+ /**
42
+ * Start the REPL scoped to a command subtree. The scope path is a space-separated command path
43
+ * (e.g. `'db'` or `'db migrate'`). Commands are resolved relative to the scoped command.
44
+ * Users can change scope at runtime with `.scope <subcommand>` and `.scope ..`/`..`.
45
+ */
46
+ scope?: TScope;
47
+ };
48
+
49
+ /**
50
+ * Options that can be passed to `eval()` to control execution behavior.
51
+ */
52
+ export type PadroneEvalPreferences = {
53
+ /**
54
+ * Controls interactive prompting for this execution.
55
+ * Overrides the runtime's `interactive` setting, but is itself overridden by `--interactive` / `-i` flags.
56
+ *
57
+ * - `undefined`: inherit from runtime (default).
58
+ * - `true`: force prompting for all configured interactive fields, even if values are already provided.
59
+ * - `false`: suppress all interactive prompts.
60
+ */
61
+ interactive?: boolean;
62
+
63
+ /**
64
+ * Override the runtime for this execution.
65
+ * Partial — only the provided fields replace the command's resolved runtime.
66
+ * Useful for capturing output, injecting test doubles, or running in non-terminal contexts (e.g. AI tool calls).
67
+ */
68
+ runtime?: PadroneRuntime;
69
+
70
+ /**
71
+ * User-defined context object passed to command action handlers via `ctx.context`.
72
+ * The context flows through the command tree and can be transformed by subcommands via `.context(transform)`.
73
+ */
74
+ context?: unknown;
75
+
76
+ /** @internal Which API entry point triggered this execution. */
77
+ caller?: PadroneActionContext['caller'];
78
+ };
79
+
80
+ /**
81
+ * Options that can be passed to `cli()` to control execution behavior.
82
+ */
83
+ export type PadroneCliPreferences = PadroneEvalPreferences;
@@ -0,0 +1,71 @@
1
+ import type { StandardSchemaV1 } from '@standard-schema/spec';
2
+ import type { PadroneSignal } from '../core/runtime.ts';
3
+ import type { Drained, IsGeneric, MaybePromise } from '../util/type-utils.ts';
4
+ import type { AnyPadroneCommand } from './command.ts';
5
+
6
+ type EmptyRecord = Record<string, never>;
7
+
8
+ type NormalizeArguments<TArgs> = IsGeneric<TArgs> extends true ? void | EmptyRecord : TArgs;
9
+ export type GetArguments<TDir extends 'in' | 'out', TCommand extends AnyPadroneCommand> = TDir extends 'in'
10
+ ? NormalizeArguments<TCommand['~types']['argsInput']>
11
+ : NormalizeArguments<TCommand['~types']['argsOutput']>;
12
+
13
+ export type GetResults<TCommand extends AnyPadroneCommand> = ReturnType<NonNullable<TCommand['action']>>;
14
+
15
+ /**
16
+ * Result of `drain()` — a discriminated union that never throws.
17
+ * On success, `value` holds the fully resolved/collected result; on failure, `error` holds the error.
18
+ */
19
+ export type PadroneDrainResult<TResult> = { value: Drained<TResult>; error?: never } | { error: unknown; value?: never };
20
+
21
+ /**
22
+ * Result returned by `eval()`, `cli()`, and `run()`. Never thrown — errors are captured in the `error` field.
23
+ * Discriminated union: check `error` to distinguish success from failure.
24
+ *
25
+ * On success: `command`, `args`, `argsResult`, `result` are populated; `error` is absent.
26
+ * On failure: `error` is populated; `command` may be present if routing succeeded.
27
+ */
28
+ export type PadroneCommandResult<TCommand extends AnyPadroneCommand = AnyPadroneCommand> =
29
+ | (PadroneParseResult<TCommand> & {
30
+ result: GetResults<TCommand>;
31
+ error?: never;
32
+ /** The signal that caused cancellation, if any. */
33
+ signal?: PadroneSignal;
34
+ /** Suggested exit code (e.g. 130 for SIGINT). Present when a signal caused termination. */
35
+ exitCode?: number;
36
+ /** Flattens the result: awaits Promises, collects iterables, catches errors. Never throws. */
37
+ drain: () => Promise<PadroneDrainResult<GetResults<TCommand>>>;
38
+ })
39
+ | {
40
+ command?: TCommand;
41
+ args?: GetArguments<'out', TCommand>;
42
+ argsResult?: StandardSchemaV1.Result<GetArguments<'out', TCommand>>;
43
+ error: unknown;
44
+ result?: never;
45
+ /** The signal that caused cancellation, if any. */
46
+ signal?: PadroneSignal;
47
+ /** Suggested exit code (e.g. 130 for SIGINT). Present when a signal caused termination. */
48
+ exitCode?: number;
49
+ /** Returns `{ error }` since there is no result to drain. */
50
+ drain: () => Promise<PadroneDrainResult<GetResults<TCommand>>>;
51
+ };
52
+
53
+ /**
54
+ * Like `MaybePromise<PadroneCommandResult<TCommand>, TAsync>` but ensures `drain()` is available
55
+ * at the outer level in all cases — both sync (Thenable) and async (Promise).
56
+ */
57
+ export type MaybePromiseCommandResult<TCommand extends AnyPadroneCommand, TAsync> = MaybePromise<PadroneCommandResult<TCommand>, TAsync> & {
58
+ drain: () => Promise<PadroneDrainResult<GetResults<TCommand>>>;
59
+ };
60
+
61
+ export type PadroneParseResult<TCommand extends AnyPadroneCommand = AnyPadroneCommand> = {
62
+ command: TCommand;
63
+ args?: GetArguments<'out', TCommand>;
64
+ argsResult?: StandardSchemaV1.Result<GetArguments<'out', TCommand>>;
65
+ };
66
+
67
+ export type PadroneAPI<TCommand extends AnyPadroneCommand> = PadroneAPICommand<TCommand> & {
68
+ [K in TCommand['~types']['commands'][number] as K['name']]: PadroneAPI<K>;
69
+ };
70
+
71
+ type PadroneAPICommand<TCommand extends AnyPadroneCommand> = (args: GetArguments<'in', TCommand>) => GetResults<TCommand>;