@outfitter/cli 0.5.2 → 1.0.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/README.md +179 -60
- package/dist/actions.d.ts +5 -2
- package/dist/actions.js +2 -2
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +8 -1
- package/dist/colors/index.js +1 -17
- package/dist/command.d.ts +3 -43
- package/dist/command.js +241 -13
- package/dist/envelope.d.ts +5 -0
- package/dist/envelope.js +160 -0
- package/dist/flags.d.ts +5 -189
- package/dist/flags.js +5 -1
- package/dist/hints.d.ts +34 -0
- package/dist/hints.js +26 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -17
- package/dist/input.d.ts +3 -124
- package/dist/input.js +14 -359
- package/dist/internal/envelope-helpers.d.ts +4 -0
- package/dist/internal/envelope-helpers.js +24 -0
- package/dist/internal/envelope-types.d.ts +3 -0
- package/dist/internal/flag-builders.d.ts +3 -0
- package/dist/internal/flag-builders.js +155 -0
- package/dist/internal/flag-types.d.ts +3 -0
- package/dist/internal/flag-types.js +13 -0
- package/dist/internal/hint-action-graph.d.ts +5 -0
- package/dist/internal/hint-action-graph.js +11 -0
- package/dist/internal/hint-command-tree.d.ts +5 -0
- package/dist/internal/hint-command-tree.js +9 -0
- package/dist/internal/hint-error-recovery.d.ts +2 -0
- package/dist/internal/hint-error-recovery.js +7 -0
- package/dist/internal/hint-types.d.ts +4 -0
- package/dist/internal/hint-types.js +1 -0
- package/dist/internal/input-helpers.d.ts +18 -0
- package/dist/internal/input-helpers.js +11 -0
- package/dist/internal/input-normalization.d.ts +3 -0
- package/dist/internal/input-normalization.js +9 -0
- package/dist/internal/input-parsers.d.ts +3 -0
- package/dist/internal/input-parsers.js +19 -0
- package/dist/internal/input-security.d.ts +22 -0
- package/dist/internal/input-security.js +11 -0
- package/dist/internal/output-formatting.d.ts +3 -0
- package/dist/internal/output-formatting.js +21 -0
- package/dist/internal/presets.d.ts +3 -0
- package/dist/{shared/@outfitter/cli-pdb7znbq.js → internal/presets.js} +49 -223
- package/dist/internal/schema-commands.d.ts +3 -0
- package/dist/{shared/@outfitter/cli-0cjts94k.js → internal/schema-commands.js} +12 -100
- package/dist/internal/schema-formatting.d.ts +2 -0
- package/dist/internal/schema-formatting.js +7 -0
- package/dist/internal/schema-types.d.ts +2 -0
- package/dist/internal/schema-types.js +1 -0
- package/dist/output.d.ts +4 -3
- package/dist/output.js +10 -2
- package/dist/pagination.d.ts +1 -1
- package/dist/query.d.ts +84 -2
- package/dist/query.js +8 -45
- package/dist/schema-input.d.ts +80 -0
- package/dist/schema-input.js +15 -0
- package/dist/schema.d.ts +4 -1
- package/dist/schema.js +1 -1
- package/dist/shared/@outfitter/cli-10wxfc78.d.ts +45 -0
- package/dist/shared/@outfitter/cli-16wg5mka.d.ts +71 -0
- package/dist/shared/@outfitter/cli-1q5redaj.js +267 -0
- package/dist/shared/@outfitter/cli-2dfxs239.js +98 -0
- package/dist/shared/@outfitter/cli-30mt7c5w.d.ts +112 -0
- package/dist/shared/@outfitter/cli-3jta1h1h.js +134 -0
- package/dist/shared/@outfitter/cli-4h85mpth.js +76 -0
- package/dist/shared/@outfitter/cli-6shkwxdc.js +28 -0
- package/dist/shared/@outfitter/cli-89335n9a.js +16 -0
- package/dist/shared/@outfitter/cli-8999qjdd.js +3 -0
- package/dist/shared/@outfitter/cli-8cfxdady.js +60 -0
- package/dist/shared/@outfitter/cli-bcajqy33.d.ts +25 -0
- package/dist/shared/@outfitter/cli-c09332vm.d.ts +39 -0
- package/dist/shared/@outfitter/cli-cgha038c.d.ts +3 -0
- package/dist/shared/@outfitter/{cli-zahqsaby.js → cli-d40m2x1d.js} +19 -3
- package/dist/shared/@outfitter/{cli-7wp5nj0s.js → cli-dg0cz7rw.js} +39 -81
- package/dist/shared/@outfitter/cli-dv8kk4jw.d.ts +24 -0
- package/dist/shared/@outfitter/cli-g43887b7.js +20 -0
- package/dist/shared/@outfitter/cli-gqtkhgw4.js +52 -0
- package/dist/shared/@outfitter/cli-h4ejpmjs.d.ts +104 -0
- package/dist/shared/@outfitter/cli-htzez8v2.js +70 -0
- package/dist/shared/@outfitter/cli-hvg2m5gf.js +79 -0
- package/dist/shared/@outfitter/cli-n54zs151.d.ts +78 -0
- package/dist/shared/@outfitter/cli-nbpgw7z7.d.ts +15 -0
- package/dist/shared/@outfitter/cli-nkt399zf.d.ts +94 -0
- package/dist/shared/@outfitter/cli-pmd04gtv.d.ts +60 -0
- package/dist/shared/@outfitter/{cli-xy3gs50c.d.ts → cli-q6csxmeh.d.ts} +19 -12
- package/dist/shared/@outfitter/cli-qcskd96y.d.ts +11 -0
- package/dist/shared/@outfitter/cli-ry7btmy4.js +118 -0
- package/dist/shared/@outfitter/cli-sy99pjyj.js +32 -0
- package/dist/shared/@outfitter/cli-tm2fzngs.d.ts +23 -0
- package/dist/shared/@outfitter/cli-vvvhjwks.js +106 -0
- package/dist/shared/@outfitter/cli-wjv7g1aq.d.ts +16 -0
- package/dist/shared/@outfitter/{cli-98aa9104.d.ts → cli-x6qr7bnd.d.ts} +338 -16
- package/dist/shared/@outfitter/cli-xde45xcc.d.ts +53 -0
- package/dist/shared/@outfitter/cli-xw8ys1je.d.ts +123 -0
- package/dist/shared/@outfitter/cli-yfewnyc2.d.ts +43 -0
- package/dist/shared/@outfitter/cli-zkzj0q4q.js +99 -0
- package/dist/shared/@outfitter/cli-zv3ah6f0.js +3 -0
- package/dist/streaming.d.ts +47 -0
- package/dist/streaming.js +13 -0
- package/dist/terminal/index.js +1 -19
- package/dist/truncation.d.ts +104 -0
- package/dist/truncation.js +111 -0
- package/dist/types.d.ts +2 -2
- package/dist/types.js +0 -5
- package/dist/verbs.d.ts +1 -1
- package/package.json +66 -36
- package/dist/shared/@outfitter/cli-n1k0d23k.d.ts +0 -33
- /package/dist/{shared/@outfitter/cli-zw75pdk8.js → internal/envelope-types.js} +0 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
createPreset
|
|
4
|
+
} from "./cli-8999qjdd.js";
|
|
5
|
+
|
|
6
|
+
// packages/cli/src/query.ts
|
|
7
|
+
function argvContainsOutputFlag(argv) {
|
|
8
|
+
for (const arg of argv) {
|
|
9
|
+
if (!arg)
|
|
10
|
+
continue;
|
|
11
|
+
if (arg === "-o" || arg === "--output")
|
|
12
|
+
return true;
|
|
13
|
+
if (arg.startsWith("--output=") || arg.startsWith("-o="))
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
function hasExplicitOutputFlag(flags, config) {
|
|
19
|
+
const mode = flags["output"];
|
|
20
|
+
if (typeof mode !== "string")
|
|
21
|
+
return false;
|
|
22
|
+
const defaultMode = config?.defaultMode ?? "human";
|
|
23
|
+
if (mode !== defaultMode)
|
|
24
|
+
return true;
|
|
25
|
+
return argvContainsOutputFlag(config?.argv ?? process.argv.slice(2));
|
|
26
|
+
}
|
|
27
|
+
function resolveOutputMode(flags, config) {
|
|
28
|
+
const defaultMode = config?.defaultMode ?? "human";
|
|
29
|
+
if (hasExplicitOutputFlag(flags, config)) {
|
|
30
|
+
const raw = flags["output"];
|
|
31
|
+
const mode = raw === "json" || raw === "jsonl" || raw === "human" ? raw : defaultMode;
|
|
32
|
+
return { mode, source: "flag" };
|
|
33
|
+
}
|
|
34
|
+
if (flags["json"] === true)
|
|
35
|
+
return { mode: "json", source: "flag" };
|
|
36
|
+
if (flags["jsonl"] === true)
|
|
37
|
+
return { mode: "jsonl", source: "flag" };
|
|
38
|
+
if (config?.forceHumanWhenImplicit) {
|
|
39
|
+
return { mode: defaultMode, source: "default" };
|
|
40
|
+
}
|
|
41
|
+
if (process.env["OUTFITTER_JSONL"] === "1") {
|
|
42
|
+
return { mode: "jsonl", source: "env" };
|
|
43
|
+
}
|
|
44
|
+
if (process.env["OUTFITTER_JSON"] === "1") {
|
|
45
|
+
return { mode: "json", source: "env" };
|
|
46
|
+
}
|
|
47
|
+
return { mode: defaultMode, source: "default" };
|
|
48
|
+
}
|
|
49
|
+
function outputModePreset(config) {
|
|
50
|
+
const defaultMode = config?.defaultMode ?? "human";
|
|
51
|
+
const baseModes = config?.modes ?? ["human", "json"];
|
|
52
|
+
const modes = new Set(config?.includeJsonl ? [...baseModes, "jsonl"] : baseModes);
|
|
53
|
+
modes.add(defaultMode);
|
|
54
|
+
return createPreset({
|
|
55
|
+
id: "outputMode",
|
|
56
|
+
options: [
|
|
57
|
+
{
|
|
58
|
+
flags: "-o, --output <mode>",
|
|
59
|
+
description: `Output mode (${[...modes].join(", ")})`,
|
|
60
|
+
defaultValue: defaultMode
|
|
61
|
+
}
|
|
62
|
+
],
|
|
63
|
+
resolve: (flags) => {
|
|
64
|
+
const raw = flags["output"];
|
|
65
|
+
if (typeof raw === "string" && modes.has(raw)) {
|
|
66
|
+
return { outputMode: raw };
|
|
67
|
+
}
|
|
68
|
+
return { outputMode: defaultMode };
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
function streamPreset() {
|
|
73
|
+
return createPreset({
|
|
74
|
+
id: "stream",
|
|
75
|
+
options: [
|
|
76
|
+
{
|
|
77
|
+
flags: "--stream",
|
|
78
|
+
description: "Stream progress events as NDJSON to stdout",
|
|
79
|
+
defaultValue: false
|
|
80
|
+
}
|
|
81
|
+
],
|
|
82
|
+
resolve: (flags) => ({
|
|
83
|
+
stream: Boolean(flags["stream"])
|
|
84
|
+
})
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function jqPreset() {
|
|
88
|
+
return createPreset({
|
|
89
|
+
id: "jq",
|
|
90
|
+
options: [
|
|
91
|
+
{
|
|
92
|
+
flags: "--jq <expr>",
|
|
93
|
+
description: "Filter JSON output with a jq expression"
|
|
94
|
+
}
|
|
95
|
+
],
|
|
96
|
+
resolve: (flags) => {
|
|
97
|
+
const raw = flags["jq"];
|
|
98
|
+
if (typeof raw === "string" && raw.length > 0) {
|
|
99
|
+
return { jq: raw };
|
|
100
|
+
}
|
|
101
|
+
return { jq: undefined };
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export { resolveOutputMode, outputModePreset, streamPreset, jqPreset };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for schema introspection commands.
|
|
3
|
+
*
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
6
|
+
/** Options for surface map subcommands (generate, diff). */
|
|
7
|
+
interface SurfaceCommandOptions {
|
|
8
|
+
readonly cwd?: string;
|
|
9
|
+
readonly outputDir?: string;
|
|
10
|
+
}
|
|
11
|
+
/** Options for the schema Commander command. */
|
|
12
|
+
interface SchemaCommandOptions {
|
|
13
|
+
readonly programName?: string;
|
|
14
|
+
readonly surface?: SurfaceCommandOptions;
|
|
15
|
+
}
|
|
16
|
+
export { SurfaceCommandOptions, SchemaCommandOptions };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ActionCliOption } from "@outfitter/contracts";
|
|
1
|
+
import { ActionCliOption, CLIHint } from "@outfitter/contracts";
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import { CancelledError, ErrorCategory, ValidationError } from "@outfitter/contracts";
|
|
4
4
|
import { Result } from "better-result";
|
|
@@ -51,17 +51,59 @@ interface CommandConfig {
|
|
|
51
51
|
readonly name: string;
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
54
|
+
* Factory function for constructing command context.
|
|
55
|
+
*
|
|
56
|
+
* Called after schema validation (if `.input()` is used), before the handler.
|
|
57
|
+
* When `.input()` is used, receives the validated typed input.
|
|
58
|
+
* When `.input()` is not used, receives the raw parsed flags.
|
|
59
|
+
*
|
|
60
|
+
* @typeParam TInput - Type of validated input (from .input() schema)
|
|
61
|
+
* @typeParam TContext - Type of the constructed context object
|
|
62
|
+
*/
|
|
63
|
+
type ContextFactory<
|
|
64
|
+
TInput,
|
|
65
|
+
TContext
|
|
66
|
+
> = (input: TInput extends undefined ? Record<string, unknown> : TInput) => Promise<TContext> | TContext;
|
|
67
|
+
/**
|
|
68
|
+
* Success hint function for transport-local hint generation.
|
|
69
|
+
*
|
|
70
|
+
* Called at output time (not during handler execution) with the handler's
|
|
71
|
+
* result and the validated input. Returns CLI-specific hints for the response.
|
|
72
|
+
*
|
|
73
|
+
* @typeParam TInput - Type of validated input (from .input() schema)
|
|
74
|
+
*/
|
|
75
|
+
type SuccessHintFn<TInput = undefined> = (result: unknown, input: TInput extends undefined ? Record<string, unknown> : TInput) => CLIHint[];
|
|
76
|
+
/**
|
|
77
|
+
* Error hint function for transport-local error hint generation.
|
|
78
|
+
*
|
|
79
|
+
* Called at output time (not during handler execution) with the error
|
|
80
|
+
* and the validated input. Returns CLI-specific hints for error recovery.
|
|
81
|
+
*
|
|
82
|
+
* @typeParam TInput - Type of validated input (from .input() schema)
|
|
83
|
+
*/
|
|
84
|
+
type ErrorHintFn<TInput = undefined> = (error: unknown, input: TInput extends undefined ? Record<string, unknown> : TInput) => CLIHint[];
|
|
85
|
+
/**
|
|
54
86
|
* Action function executed when a command is invoked.
|
|
55
87
|
*
|
|
56
88
|
* @typeParam TFlags - Type of parsed command flags
|
|
57
|
-
|
|
58
|
-
|
|
89
|
+
* @typeParam TInput - Type of validated input (from .input() schema)
|
|
90
|
+
* @typeParam TContext - Type of context object (from .context() factory)
|
|
91
|
+
*/
|
|
92
|
+
type CommandAction<
|
|
93
|
+
TFlags extends CommandFlags = CommandFlags,
|
|
94
|
+
TInput = undefined,
|
|
95
|
+
TContext = undefined
|
|
96
|
+
> = (context: {
|
|
59
97
|
/** Parsed command-line arguments */
|
|
60
98
|
readonly args: readonly string[];
|
|
61
|
-
/** Parsed command flags */
|
|
62
|
-
readonly flags: TFlags;
|
|
63
99
|
/** Raw Commander command instance */
|
|
64
100
|
readonly command: Command;
|
|
101
|
+
/** Context object constructed by .context() factory (undefined when .context() is not used) */
|
|
102
|
+
readonly ctx: TContext;
|
|
103
|
+
/** Parsed command flags */
|
|
104
|
+
readonly flags: TFlags;
|
|
105
|
+
/** Validated input from Zod schema (present when .input() is used) */
|
|
106
|
+
readonly input: TInput;
|
|
65
107
|
}) => Promise<void> | void;
|
|
66
108
|
/**
|
|
67
109
|
* Base type for command flags.
|
|
@@ -70,24 +112,250 @@ type CommandAction<TFlags extends CommandFlags = CommandFlags> = (context: {
|
|
|
70
112
|
type CommandFlags = Record<string, unknown>;
|
|
71
113
|
/**
|
|
72
114
|
* Builder interface for constructing commands fluently.
|
|
115
|
+
*
|
|
116
|
+
* @typeParam TInput - Type of validated input when .input() is used
|
|
117
|
+
* @typeParam TContext - Type of context object when .context() is used
|
|
73
118
|
*/
|
|
74
|
-
interface CommandBuilder
|
|
119
|
+
interface CommandBuilder<
|
|
120
|
+
TInput = undefined,
|
|
121
|
+
TContext = undefined
|
|
122
|
+
> {
|
|
75
123
|
/** Set the action handler */
|
|
76
|
-
action<TFlags extends CommandFlags = CommandFlags>(handler: CommandAction<TFlags>): this;
|
|
124
|
+
action<TFlags extends CommandFlags = CommandFlags>(handler: CommandAction<TFlags, TInput, TContext>): this;
|
|
77
125
|
/** Add command aliases */
|
|
78
126
|
alias(alias: string): this;
|
|
79
127
|
/** Build the underlying Commander command */
|
|
80
128
|
build(): Command;
|
|
129
|
+
/**
|
|
130
|
+
* Set an async context factory.
|
|
131
|
+
*
|
|
132
|
+
* The factory is called after schema validation (if `.input()` is used),
|
|
133
|
+
* before the handler. It receives the validated typed input (or raw parsed
|
|
134
|
+
* flags when `.input()` is not used) and returns a typed context object.
|
|
135
|
+
*
|
|
136
|
+
* Context factory errors are caught and produce proper exit codes.
|
|
137
|
+
*
|
|
138
|
+
* @typeParam T - Type of the context object returned by the factory
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```typescript
|
|
142
|
+
* command("deploy")
|
|
143
|
+
* .input(z.object({ env: z.string() }))
|
|
144
|
+
* .context(async (input) => ({
|
|
145
|
+
* config: await loadConfig(input.env),
|
|
146
|
+
* client: createClient(input.env),
|
|
147
|
+
* }))
|
|
148
|
+
* .action(async ({ input, ctx }) => {
|
|
149
|
+
* await ctx.client.deploy(ctx.config);
|
|
150
|
+
* });
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
context<T>(factory: ContextFactory<TInput, T>): CommandBuilder<TInput, T>;
|
|
81
154
|
/** Set command description */
|
|
82
155
|
description(text: string): this;
|
|
156
|
+
/**
|
|
157
|
+
* Mark the command as destructive.
|
|
158
|
+
*
|
|
159
|
+
* When `isDest` is `true`, a `--dry-run` flag is auto-added to the command
|
|
160
|
+
* (deduplicated if already present from `.option()` or `.preset()`).
|
|
161
|
+
* The handler is responsible for checking the dry-run flag and performing
|
|
162
|
+
* preview-only logic when active.
|
|
163
|
+
*
|
|
164
|
+
* When used with `runHandler({ dryRun: true })`, the response envelope
|
|
165
|
+
* includes a CLIHint with the command to execute without `--dry-run`
|
|
166
|
+
* (preview-then-commit pattern).
|
|
167
|
+
*
|
|
168
|
+
* Default (no `.destructive()` call) is non-destructive.
|
|
169
|
+
*
|
|
170
|
+
* @param isDest - Whether the command is destructive
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```typescript
|
|
174
|
+
* command("delete")
|
|
175
|
+
* .description("Delete resources")
|
|
176
|
+
* .destructive(true)
|
|
177
|
+
* .action(async ({ flags }) => {
|
|
178
|
+
* const isDryRun = Boolean(flags.dryRun);
|
|
179
|
+
* await runHandler({
|
|
180
|
+
* command: "delete",
|
|
181
|
+
* handler: async (input) => isDryRun
|
|
182
|
+
* ? Result.ok({ preview: true, count: 5 })
|
|
183
|
+
* : deleteResources(input),
|
|
184
|
+
* dryRun: isDryRun,
|
|
185
|
+
* });
|
|
186
|
+
* });
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
destructive(isDest: boolean): this;
|
|
190
|
+
/**
|
|
191
|
+
* Mark the command as idempotent.
|
|
192
|
+
*
|
|
193
|
+
* When `true`, indicates that calling this command multiple times with the
|
|
194
|
+
* same input produces the same effect (PUT-like semantics). This metadata
|
|
195
|
+
* is included in the self-documenting command tree (JSON mode) and maps to
|
|
196
|
+
* `idempotentHint` in MCP tool annotations.
|
|
197
|
+
*
|
|
198
|
+
* Default (no `.idempotent()` call) is non-idempotent.
|
|
199
|
+
*
|
|
200
|
+
* @param isIdempotent - Whether the command is idempotent
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* ```typescript
|
|
204
|
+
* command("set")
|
|
205
|
+
* .description("Set a configuration value")
|
|
206
|
+
* .idempotent(true)
|
|
207
|
+
* .action(async ({ flags }) => { ... });
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
210
|
+
idempotent(isIdempotent: boolean): this;
|
|
211
|
+
/**
|
|
212
|
+
* Set a success hint function.
|
|
213
|
+
*
|
|
214
|
+
* The hint function is stored on the builder and invoked at output time
|
|
215
|
+
* (not during handler execution). It receives the handler's result and the
|
|
216
|
+
* validated input (or raw flags when `.input()` is not used), and returns
|
|
217
|
+
* CLI-specific hints for the response.
|
|
218
|
+
*
|
|
219
|
+
* Hint functions are transport-local — handlers remain transport-agnostic.
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* ```typescript
|
|
223
|
+
* command("deploy")
|
|
224
|
+
* .input(z.object({ env: z.string() }))
|
|
225
|
+
* .hints((result, input) => [
|
|
226
|
+
* {
|
|
227
|
+
* description: `Check status for ${input.env}`,
|
|
228
|
+
* command: `deploy status --env ${input.env}`,
|
|
229
|
+
* },
|
|
230
|
+
* ])
|
|
231
|
+
* .action(async ({ input }) => { ... });
|
|
232
|
+
* ```
|
|
233
|
+
*/
|
|
234
|
+
hints(fn: SuccessHintFn<TInput>): this;
|
|
235
|
+
/**
|
|
236
|
+
* Set a Zod object schema for auto-deriving Commander flags.
|
|
237
|
+
*
|
|
238
|
+
* Handles the 80% case automatically:
|
|
239
|
+
* - `z.string()` → string option
|
|
240
|
+
* - `z.number()` → number option with coercion
|
|
241
|
+
* - `z.boolean()` → boolean flag
|
|
242
|
+
* - `z.enum()` → choices option
|
|
243
|
+
*
|
|
244
|
+
* `.describe()` text becomes the option description.
|
|
245
|
+
* `.default()` values become option defaults.
|
|
246
|
+
*
|
|
247
|
+
* Explicit `.option()` / `.requiredOption()` / `.argument()` calls
|
|
248
|
+
* compose alongside `.input()` — they override or supplement auto-derived flags.
|
|
249
|
+
*/
|
|
250
|
+
input<T extends Record<string, unknown>>(schema: ZodObjectLike<T>): CommandBuilder<T, TContext>;
|
|
251
|
+
/**
|
|
252
|
+
* Set an error hint function.
|
|
253
|
+
*
|
|
254
|
+
* The hint function is stored on the builder and invoked at output time
|
|
255
|
+
* (not during handler execution). It receives the error and the validated
|
|
256
|
+
* input (or raw flags when `.input()` is not used), and returns CLI-specific
|
|
257
|
+
* hints for error recovery.
|
|
258
|
+
*
|
|
259
|
+
* Hint functions are transport-local — handlers remain transport-agnostic.
|
|
260
|
+
*
|
|
261
|
+
* @example
|
|
262
|
+
* ```typescript
|
|
263
|
+
* command("deploy")
|
|
264
|
+
* .input(z.object({ env: z.string() }))
|
|
265
|
+
* .onError((error, input) => [
|
|
266
|
+
* {
|
|
267
|
+
* description: "Retry with --force",
|
|
268
|
+
* command: `deploy --env ${input.env} --force`,
|
|
269
|
+
* },
|
|
270
|
+
* ])
|
|
271
|
+
* .action(async ({ input }) => { ... });
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
274
|
+
onError(fn: ErrorHintFn<TInput>): this;
|
|
275
|
+
/**
|
|
276
|
+
* Declare a relationship to another command for action graph navigation.
|
|
277
|
+
*
|
|
278
|
+
* Relationships build a navigable action graph (tier-4 hints) that agents
|
|
279
|
+
* use to discover workflows. Success envelopes include related next-actions
|
|
280
|
+
* from graph neighbors; error envelopes include remediation paths.
|
|
281
|
+
*
|
|
282
|
+
* Multiple `.relatedTo()` calls accumulate — each declares a separate edge.
|
|
283
|
+
*
|
|
284
|
+
* @param target - Name of the related command
|
|
285
|
+
* @param options - Optional relationship metadata (description)
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* ```typescript
|
|
289
|
+
* command("deploy")
|
|
290
|
+
* .description("Deploy application")
|
|
291
|
+
* .relatedTo("status", { description: "Check deployment status" })
|
|
292
|
+
* .relatedTo("rollback", { description: "Rollback if needed" })
|
|
293
|
+
* .action(async ({ flags }) => { ... });
|
|
294
|
+
* ```
|
|
295
|
+
*/
|
|
296
|
+
relatedTo(target: string, options?: RelatedToOptions): this;
|
|
83
297
|
/** Add a command option/flag */
|
|
84
298
|
option(flags: string, description: string, defaultValue?: unknown): this;
|
|
85
|
-
/**
|
|
86
|
-
|
|
299
|
+
/**
|
|
300
|
+
* Mark the command as read-only.
|
|
301
|
+
*
|
|
302
|
+
* When `true`, indicates that this command does not modify any state.
|
|
303
|
+
* This metadata is included in the self-documenting command tree (JSON mode)
|
|
304
|
+
* and maps to `readOnlyHint` in MCP tool annotations.
|
|
305
|
+
*
|
|
306
|
+
* Default (no `.readOnly()` call) is non-read-only.
|
|
307
|
+
*
|
|
308
|
+
* @param isReadOnly - Whether the command is read-only
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* ```typescript
|
|
312
|
+
* command("list")
|
|
313
|
+
* .description("List all resources")
|
|
314
|
+
* .readOnly(true)
|
|
315
|
+
* .action(async ({ flags }) => { ... });
|
|
316
|
+
* ```
|
|
317
|
+
*/
|
|
318
|
+
readOnly(isReadOnly: boolean): this;
|
|
319
|
+
/**
|
|
320
|
+
* Apply a preset to the command.
|
|
321
|
+
*
|
|
322
|
+
* Accepts either a `FlagPreset` (Commander option definitions with resolver)
|
|
323
|
+
* or a `SchemaPreset` (Zod schema fragment with resolver). Schema presets
|
|
324
|
+
* auto-derive Commander flags from the schema, composing with `.input()`.
|
|
325
|
+
*/
|
|
326
|
+
preset(preset: AnyPreset<Record<string, unknown>>): this;
|
|
87
327
|
/** Add a required option */
|
|
88
328
|
requiredOption(flags: string, description: string, defaultValue?: unknown): this;
|
|
89
329
|
}
|
|
90
330
|
/**
|
|
331
|
+
* Options for declaring a command relationship via `.relatedTo()`.
|
|
332
|
+
*/
|
|
333
|
+
interface RelatedToOptions {
|
|
334
|
+
/** Description of the relationship (used in hints) */
|
|
335
|
+
readonly description?: string;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* A stored relationship declaration from `.relatedTo()`.
|
|
339
|
+
*/
|
|
340
|
+
interface RelatedToDeclaration {
|
|
341
|
+
/** Target command name */
|
|
342
|
+
readonly target: string;
|
|
343
|
+
/** Description of the relationship */
|
|
344
|
+
readonly description?: string;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Minimal interface for a Zod-like object schema.
|
|
348
|
+
* Avoids tight coupling to a specific Zod version.
|
|
349
|
+
*/
|
|
350
|
+
interface ZodObjectLike<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
351
|
+
readonly shape: Record<string, unknown>;
|
|
352
|
+
safeParse(data: unknown): {
|
|
353
|
+
success: boolean;
|
|
354
|
+
data?: T;
|
|
355
|
+
error?: unknown;
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
91
359
|
* A family of related command verbs with a primary name and aliases.
|
|
92
360
|
*/
|
|
93
361
|
interface VerbFamily {
|
|
@@ -137,6 +405,39 @@ interface FlagPresetConfig<TResolved extends Record<string, unknown>> {
|
|
|
137
405
|
readonly resolve: (flags: Record<string, unknown>) => TResolved;
|
|
138
406
|
}
|
|
139
407
|
/**
|
|
408
|
+
* A schema-driven preset that uses a Zod schema fragment for flag derivation.
|
|
409
|
+
*
|
|
410
|
+
* Instead of declaring Commander options manually, the schema fragment is
|
|
411
|
+
* introspected to auto-derive flags (same as `.input()`). The schema merges
|
|
412
|
+
* with `.input()` schema automatically when both are used.
|
|
413
|
+
*
|
|
414
|
+
* Eliminates the need for separate `.resolve()` / `preAction` hooks.
|
|
415
|
+
*/
|
|
416
|
+
interface SchemaPreset<TResolved extends Record<string, unknown>> {
|
|
417
|
+
/** Unique identifier for deduplication */
|
|
418
|
+
readonly id: string;
|
|
419
|
+
/** Resolve raw Commander flags into typed values */
|
|
420
|
+
readonly resolve: (flags: Record<string, unknown>) => TResolved;
|
|
421
|
+
/** Zod schema fragment defining the preset's flags */
|
|
422
|
+
readonly schema: ZodObjectLike;
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Configuration for creating a schema-driven preset.
|
|
426
|
+
*/
|
|
427
|
+
interface SchemaPresetConfig<TResolved extends Record<string, unknown>> {
|
|
428
|
+
/** Unique identifier for deduplication */
|
|
429
|
+
readonly id: string;
|
|
430
|
+
/** Resolve raw Commander flags into typed values */
|
|
431
|
+
readonly resolve: (flags: Record<string, unknown>) => TResolved;
|
|
432
|
+
/** Zod schema fragment defining the preset's flags */
|
|
433
|
+
readonly schema: ZodObjectLike;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Union type for any preset accepted by `.preset()`.
|
|
437
|
+
* Includes both flag-based presets and schema-driven presets.
|
|
438
|
+
*/
|
|
439
|
+
type AnyPreset<TResolved extends Record<string, unknown> = Record<string, unknown>> = FlagPreset<TResolved> | SchemaPreset<TResolved>;
|
|
440
|
+
/**
|
|
140
441
|
* Configuration for creating a custom boolean flag preset.
|
|
141
442
|
*/
|
|
142
443
|
interface BooleanFlagPresetConfig<TKey extends string> {
|
|
@@ -340,12 +641,37 @@ type OutputMode = "human" | "json" | "jsonl" | "tree" | "table";
|
|
|
340
641
|
interface OutputOptions {
|
|
341
642
|
/** Exit code to use after output (undefined = don't exit) */
|
|
342
643
|
readonly exitCode?: number;
|
|
343
|
-
/** Force a specific output mode (overrides flag detection) */
|
|
344
|
-
readonly mode?: OutputMode;
|
|
345
644
|
/** Whether to pretty-print JSON output */
|
|
346
645
|
readonly pretty?: boolean;
|
|
347
646
|
/** Stream to write to (defaults to stdout) */
|
|
348
647
|
readonly stream?: NodeJS.WritableStream;
|
|
648
|
+
/**
|
|
649
|
+
* Optional truncation configuration metadata.
|
|
650
|
+
*
|
|
651
|
+
* `output()` applies array slicing when this option is provided.
|
|
652
|
+
* For array data, it uses `offset` + `limit` before rendering.
|
|
653
|
+
*
|
|
654
|
+
* If you need pagination hints or file-pointer metadata, call
|
|
655
|
+
* `truncateOutput()` first and pass `truncated.hints` / `truncated.metadata`
|
|
656
|
+
* through your envelope separately.
|
|
657
|
+
*
|
|
658
|
+
* @example
|
|
659
|
+
* ```typescript
|
|
660
|
+
* await output(items, "json", {
|
|
661
|
+
* truncation: { limit: 50, offset: 0 },
|
|
662
|
+
* });
|
|
663
|
+
* ```
|
|
664
|
+
*/
|
|
665
|
+
readonly truncation?: {
|
|
666
|
+
/** Command name for pagination hints (used by `truncateOutput()`, not `output()`). */
|
|
667
|
+
readonly commandName?: string;
|
|
668
|
+
/** File-pointer threshold (used by `truncateOutput()`, not `output()`). */
|
|
669
|
+
readonly filePointerThreshold?: number;
|
|
670
|
+
/** Maximum items to show. */
|
|
671
|
+
readonly limit: number;
|
|
672
|
+
/** Starting offset (0-based). */
|
|
673
|
+
readonly offset?: number;
|
|
674
|
+
};
|
|
349
675
|
}
|
|
350
676
|
/**
|
|
351
677
|
* Options for collectIds() input utility.
|
|
@@ -361,12 +687,8 @@ interface OutputOptions {
|
|
|
361
687
|
interface CollectIdsOptions {
|
|
362
688
|
/** Allow @file expansion (reads IDs from file) */
|
|
363
689
|
readonly allowFile?: boolean;
|
|
364
|
-
/** Allow glob patterns */
|
|
365
|
-
readonly allowGlob?: boolean;
|
|
366
690
|
/** Allow reading from stdin with "-" */
|
|
367
691
|
readonly allowStdin?: boolean;
|
|
368
|
-
/** Separator for comma-separated values */
|
|
369
|
-
readonly separator?: string;
|
|
370
692
|
}
|
|
371
693
|
/**
|
|
372
694
|
* Options for expandFileArg() input utility.
|
|
@@ -485,4 +807,4 @@ interface CursorOptions {
|
|
|
485
807
|
/** Total count of results (if known) */
|
|
486
808
|
readonly total?: number;
|
|
487
809
|
}
|
|
488
|
-
export { CLIConfig, CLI, CommandConfig, CommandAction, CommandFlags, CommandBuilder, VerbFamily, VerbConfig, FlagPreset, FlagPresetConfig, BooleanFlagPresetConfig, EnumFlagPresetConfig, NumberFlagPresetConfig, StringListFlagPresetConfig, ComposedPreset, PaginationPresetConfig, InteractionFlags, StrictFlags, TimeWindowFlags, TimeWindowPresetConfig, ExecutionFlags, ExecutionPresetConfig, ProjectionFlags, ColorMode, ColorFlags, PaginationFlags, OutputMode, OutputOptions, CollectIdsOptions, ExpandFileOptions, ParseGlobOptions, NormalizeIdOptions, Range, NumericRange, DateRange, FilterExpression, SortCriteria, KeyValuePair, PaginationState, CursorOptions, CancelledError, ErrorCategory, ValidationError, Result };
|
|
810
|
+
export { CLIConfig, CLI, CommandConfig, ContextFactory, SuccessHintFn, ErrorHintFn, CommandAction, CommandFlags, CommandBuilder, RelatedToOptions, RelatedToDeclaration, ZodObjectLike, VerbFamily, VerbConfig, FlagPreset, FlagPresetConfig, SchemaPreset, SchemaPresetConfig, AnyPreset, BooleanFlagPresetConfig, EnumFlagPresetConfig, NumberFlagPresetConfig, StringListFlagPresetConfig, ComposedPreset, PaginationPresetConfig, InteractionFlags, StrictFlags, TimeWindowFlags, TimeWindowPresetConfig, ExecutionFlags, ExecutionPresetConfig, ProjectionFlags, ColorMode, ColorFlags, PaginationFlags, OutputMode, OutputOptions, CollectIdsOptions, ExpandFileOptions, ParseGlobOptions, NormalizeIdOptions, Range, NumericRange, DateRange, FilterExpression, SortCriteria, KeyValuePair, PaginationState, CursorOptions, CancelledError, ErrorCategory, ValidationError, Result };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { CLI, CLIConfig, CommandBuilder } from "./cli-x6qr7bnd.js";
|
|
2
|
+
/**
|
|
3
|
+
* Command metadata for safety signals (readOnly, idempotent).
|
|
4
|
+
* Stored on Commander commands and surfaced in the command tree.
|
|
5
|
+
*/
|
|
6
|
+
interface CommandMetadata {
|
|
7
|
+
/** When true, the command does not modify any state */
|
|
8
|
+
readonly readOnly?: boolean;
|
|
9
|
+
/** When true, calling the command multiple times with the same input has the same effect */
|
|
10
|
+
readonly idempotent?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Create a CLI instance with a portable return type from this module.
|
|
14
|
+
*/
|
|
15
|
+
declare function createCLI(config: CLIConfig): CLI;
|
|
16
|
+
/**
|
|
17
|
+
* Create a new command builder with the given name.
|
|
18
|
+
*
|
|
19
|
+
* The command builder provides a fluent API for defining CLI commands
|
|
20
|
+
* with typed flags, arguments, and actions.
|
|
21
|
+
*
|
|
22
|
+
* @param name - Command name and optional argument syntax (e.g., "list" or "get <id>")
|
|
23
|
+
* @returns A CommandBuilder instance for fluent configuration
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* import { command, output } from "@outfitter/cli";
|
|
28
|
+
*
|
|
29
|
+
* const list = command("list")
|
|
30
|
+
* .description("List all notes")
|
|
31
|
+
* .option("--limit <n>", "Max results", "20")
|
|
32
|
+
* .option("--json", "Output as JSON")
|
|
33
|
+
* .option("--next", "Continue from last position")
|
|
34
|
+
* .action(async ({ flags }) => {
|
|
35
|
+
* const results = await listNotes(flags);
|
|
36
|
+
* output(results);
|
|
37
|
+
* });
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* // Command with required argument
|
|
43
|
+
* const get = command("get <id>")
|
|
44
|
+
* .description("Get a note by ID")
|
|
45
|
+
* .action(async ({ args }) => {
|
|
46
|
+
* const [id] = args;
|
|
47
|
+
* const note = await getNote(id);
|
|
48
|
+
* output(note);
|
|
49
|
+
* });
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
declare function command(name: string): CommandBuilder;
|
|
53
|
+
export { CommandMetadata, createCLI, command };
|