@outfitter/cli 0.1.0-rc.1

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.
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Available output modes for CLI commands.
3
+ */
4
+ type OutputMode = "human" | "json" | "jsonl" | "tree" | "table";
5
+ /**
6
+ * Options for the output() function.
7
+ */
8
+ interface OutputOptions {
9
+ /** Force a specific output mode (overrides flag detection) */
10
+ readonly mode?: OutputMode;
11
+ /** Stream to write to (defaults to stdout) */
12
+ readonly stream?: NodeJS.WritableStream;
13
+ /** Whether to pretty-print JSON output */
14
+ readonly pretty?: boolean;
15
+ /** Exit code to use after output (undefined = don't exit) */
16
+ readonly exitCode?: number;
17
+ }
18
+ /**
19
+ * Output data to the console with automatic mode selection.
20
+ *
21
+ * Respects --json, --jsonl, --tree, --table flags automatically.
22
+ * Defaults to human-friendly output when no flags are present.
23
+ *
24
+ * @param data - The data to output
25
+ * @param options - Output configuration options
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * import { output } from "@outfitter/cli";
30
+ *
31
+ * // Basic usage - mode auto-detected from flags
32
+ * output(results);
33
+ *
34
+ * // Force JSON mode
35
+ * output(results, { mode: "json" });
36
+ *
37
+ * // Pretty-print JSON
38
+ * output(results, { mode: "json", pretty: true });
39
+ *
40
+ * // Output to stderr
41
+ * output(errors, { stream: process.stderr });
42
+ *
43
+ * // Await for large outputs (recommended)
44
+ * await output(largeDataset, { mode: "jsonl" });
45
+ * ```
46
+ */
47
+ declare function output(data: unknown, options?: OutputOptions): Promise<void>;
48
+ /**
49
+ * Exit the process with an error message.
50
+ *
51
+ * Formats the error according to the current output mode (human or JSON)
52
+ * and exits with an appropriate exit code.
53
+ *
54
+ * @param error - The error to display
55
+ * @returns Never returns (exits the process)
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * import { exitWithError } from "@outfitter/cli";
60
+ *
61
+ * try {
62
+ * await riskyOperation();
63
+ * } catch (error) {
64
+ * exitWithError(error instanceof Error ? error : new Error(String(error)));
65
+ * }
66
+ * ```
67
+ */
68
+ declare function exitWithError(error: Error, options?: OutputOptions): never;
69
+ export { output, exitWithError };
package/dist/output.js ADDED
@@ -0,0 +1,156 @@
1
+ // @bun
2
+ import"./shared/@outfitter/cli-4yy82cmp.js";
3
+
4
+ // packages/cli/src/output.ts
5
+ import {
6
+ safeStringify as contractsSafeStringify,
7
+ exitCodeMap
8
+ } from "@outfitter/contracts";
9
+ var DEFAULT_EXIT_CODE = 1;
10
+ function writeWithBackpressure(stream, data) {
11
+ return new Promise((resolve, reject) => {
12
+ const canContinue = stream.write(data, (error) => {
13
+ if (error)
14
+ reject(error);
15
+ });
16
+ if (canContinue) {
17
+ resolve();
18
+ } else {
19
+ stream.once("drain", () => resolve());
20
+ stream.once("error", reject);
21
+ }
22
+ });
23
+ }
24
+ function detectMode(options) {
25
+ if (options?.mode) {
26
+ return options.mode;
27
+ }
28
+ const envJsonl = process.env["OUTFITTER_JSONL"];
29
+ const envJson = process.env["OUTFITTER_JSON"];
30
+ if (envJsonl === "1")
31
+ return "jsonl";
32
+ if (envJson === "1")
33
+ return "json";
34
+ if (envJsonl === "0" || envJson === "0")
35
+ return "human";
36
+ return process.stdout.isTTY ? "human" : "json";
37
+ }
38
+ function isValidCategory(category) {
39
+ return category in exitCodeMap;
40
+ }
41
+ function safeStringify(value, pretty) {
42
+ const wrappedValue = value === undefined ? null : value;
43
+ return contractsSafeStringify(wrappedValue, pretty ? 2 : undefined);
44
+ }
45
+ function formatHuman(data) {
46
+ if (data === null || data === undefined) {
47
+ return "";
48
+ }
49
+ if (typeof data === "string") {
50
+ return data;
51
+ }
52
+ if (typeof data === "number" || typeof data === "boolean") {
53
+ return String(data);
54
+ }
55
+ if (Array.isArray(data)) {
56
+ return data.map((item) => formatHuman(item)).join(`
57
+ `);
58
+ }
59
+ if (typeof data === "object") {
60
+ const lines = [];
61
+ for (const [key, value] of Object.entries(data)) {
62
+ lines.push(`${key}: ${formatHuman(value)}`);
63
+ }
64
+ return lines.join(`
65
+ `);
66
+ }
67
+ return String(data);
68
+ }
69
+ function getErrorProperties(error) {
70
+ const errorObj = error;
71
+ return {
72
+ _tag: errorObj._tag,
73
+ category: errorObj.category,
74
+ context: errorObj.context
75
+ };
76
+ }
77
+ function getExitCode(error) {
78
+ const { category } = getErrorProperties(error);
79
+ if (category !== undefined && isValidCategory(category)) {
80
+ return exitCodeMap[category];
81
+ }
82
+ return DEFAULT_EXIT_CODE;
83
+ }
84
+ function serializeErrorToJson(error) {
85
+ const { _tag, category, context } = getErrorProperties(error);
86
+ const result = {
87
+ message: error.message
88
+ };
89
+ if (_tag !== undefined) {
90
+ result._tag = _tag;
91
+ }
92
+ if (category !== undefined) {
93
+ result.category = category;
94
+ }
95
+ if (context !== undefined) {
96
+ result.context = context;
97
+ }
98
+ return JSON.stringify(result);
99
+ }
100
+ function formatErrorHuman(error) {
101
+ const { _tag } = getErrorProperties(error);
102
+ if (_tag) {
103
+ return `${_tag}: ${error.message}`;
104
+ }
105
+ return error.message;
106
+ }
107
+ async function output(data, options) {
108
+ const mode = detectMode(options);
109
+ const stream = options?.stream ?? process.stdout;
110
+ let outputText;
111
+ switch (mode) {
112
+ case "json": {
113
+ const jsonData = data === undefined ? null : data;
114
+ outputText = safeStringify(jsonData, options?.pretty);
115
+ break;
116
+ }
117
+ case "jsonl": {
118
+ if (Array.isArray(data)) {
119
+ if (data.length === 0) {
120
+ outputText = "";
121
+ } else {
122
+ outputText = data.map((item) => safeStringify(item)).join(`
123
+ `);
124
+ }
125
+ } else {
126
+ outputText = safeStringify(data);
127
+ }
128
+ break;
129
+ }
130
+ default: {
131
+ outputText = formatHuman(data);
132
+ break;
133
+ }
134
+ }
135
+ if (outputText) {
136
+ await writeWithBackpressure(stream, `${outputText}
137
+ `);
138
+ }
139
+ }
140
+ function exitWithError(error, options) {
141
+ const exitCode = getExitCode(error);
142
+ const mode = detectMode(options);
143
+ const isJsonMode = mode === "json" || mode === "jsonl";
144
+ if (isJsonMode) {
145
+ process.stderr.write(`${serializeErrorToJson(error)}
146
+ `);
147
+ } else {
148
+ process.stderr.write(`${formatErrorHuman(error)}
149
+ `);
150
+ }
151
+ process.exit(exitCode);
152
+ }
153
+ export {
154
+ output,
155
+ exitWithError
156
+ };
@@ -0,0 +1,96 @@
1
+ /**
2
+ * State for paginated command results.
3
+ */
4
+ interface PaginationState {
5
+ /** Cursor for the next page */
6
+ readonly cursor: string;
7
+ /** Command that generated this state */
8
+ readonly command: string;
9
+ /** Context key for scoping pagination */
10
+ readonly context?: string;
11
+ /** Timestamp when state was created */
12
+ readonly timestamp: number;
13
+ /** Whether there are more results */
14
+ readonly hasMore: boolean;
15
+ /** Total count (if known) */
16
+ readonly total?: number;
17
+ }
18
+ /**
19
+ * Options for cursor persistence operations.
20
+ */
21
+ interface CursorOptions {
22
+ /** Command name for cursor scoping */
23
+ readonly command: string;
24
+ /** Context key for additional scoping */
25
+ readonly context?: string;
26
+ /** Tool name for XDG path resolution */
27
+ readonly toolName: string;
28
+ /** Maximum age in milliseconds before a cursor is treated as expired */
29
+ readonly maxAgeMs?: number;
30
+ /** Whether there are more results (defaults to true) */
31
+ readonly hasMore?: boolean;
32
+ /** Total count of results (if known) */
33
+ readonly total?: number;
34
+ }
35
+ /**
36
+ * Load persisted pagination state for a command.
37
+ *
38
+ * @param options - Cursor options specifying command and context
39
+ * @returns The pagination state if it exists, undefined otherwise
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * const state = loadCursor({
44
+ * command: "list",
45
+ * toolName: "waymark",
46
+ * });
47
+ *
48
+ * if (state) {
49
+ * // Continue from last position
50
+ * const results = await listNotes({ cursor: state.cursor });
51
+ * }
52
+ * ```
53
+ */
54
+ declare function loadCursor(options: CursorOptions): PaginationState | undefined;
55
+ /**
56
+ * Save pagination state for a command.
57
+ *
58
+ * The cursor is persisted to XDG state directory, scoped by
59
+ * tool name, command, and optional context.
60
+ *
61
+ * @param cursor - The cursor string to persist
62
+ * @param options - Cursor options specifying command and context
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * const results = await listNotes({ limit: 20 });
67
+ *
68
+ * if (results.hasMore) {
69
+ * saveCursor(results.cursor, {
70
+ * command: "list",
71
+ * toolName: "waymark",
72
+ * });
73
+ * }
74
+ * ```
75
+ */
76
+ declare function saveCursor(cursor: string, options: CursorOptions): void;
77
+ /**
78
+ * Clear persisted pagination state for a command.
79
+ *
80
+ * Called when --reset flag is passed or when pagination completes.
81
+ *
82
+ * @param options - Cursor options specifying command and context
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * // User passed --reset flag
87
+ * if (flags.reset) {
88
+ * clearCursor({
89
+ * command: "list",
90
+ * toolName: "waymark",
91
+ * });
92
+ * }
93
+ * ```
94
+ */
95
+ declare function clearCursor(options: CursorOptions): void;
96
+ export { saveCursor, loadCursor, clearCursor };
@@ -0,0 +1,90 @@
1
+ // @bun
2
+ import"./shared/@outfitter/cli-4yy82cmp.js";
3
+
4
+ // packages/cli/src/pagination.ts
5
+ import fs from "fs";
6
+ import os from "os";
7
+ import path from "path";
8
+ var UNSAFE_PATH_PATTERN = /(?:^|[\\/])\.\.(?:[\\/]|$)|^[\\/]|^[a-zA-Z]:/;
9
+ function validatePathComponent(component, name) {
10
+ if (UNSAFE_PATH_PATTERN.test(component)) {
11
+ throw new Error(`Security: path traversal detected in ${name}`);
12
+ }
13
+ }
14
+ function getDefaultStateHome() {
15
+ const home = os.homedir();
16
+ switch (process.platform) {
17
+ case "darwin":
18
+ return path.join(home, "Library", "Application Support");
19
+ case "win32":
20
+ return process.env["LOCALAPPDATA"] ?? path.join(home, "AppData", "Local");
21
+ default:
22
+ return path.join(home, ".local", "state");
23
+ }
24
+ }
25
+ function getStatePath(options) {
26
+ validatePathComponent(options.command, "command");
27
+ validatePathComponent(options.toolName, "toolName");
28
+ if (options.context) {
29
+ validatePathComponent(options.context, "context");
30
+ }
31
+ const xdgState = process.env["XDG_STATE_HOME"] ?? getDefaultStateHome();
32
+ const parts = [xdgState, options.toolName, "cursors", options.command];
33
+ if (options.context) {
34
+ parts.push(options.context);
35
+ }
36
+ return path.join(...parts, "cursor.json");
37
+ }
38
+ function loadCursor(options) {
39
+ const statePath = getStatePath(options);
40
+ if (!fs.existsSync(statePath)) {
41
+ return;
42
+ }
43
+ try {
44
+ const content = fs.readFileSync(statePath, "utf-8");
45
+ const parsed = JSON.parse(content);
46
+ if (typeof parsed !== "object" || parsed === null || typeof parsed["cursor"] !== "string") {
47
+ return;
48
+ }
49
+ const state = parsed;
50
+ if (options.maxAgeMs !== undefined) {
51
+ if (typeof state.timestamp !== "number") {
52
+ return;
53
+ }
54
+ const ageMs = Date.now() - state.timestamp;
55
+ if (ageMs > options.maxAgeMs) {
56
+ return;
57
+ }
58
+ }
59
+ return state;
60
+ } catch {
61
+ return;
62
+ }
63
+ }
64
+ function saveCursor(cursor, options) {
65
+ const statePath = getStatePath(options);
66
+ const stateDir = path.dirname(statePath);
67
+ fs.mkdirSync(stateDir, { recursive: true });
68
+ const state = {
69
+ cursor,
70
+ command: options.command,
71
+ timestamp: Date.now(),
72
+ hasMore: options.hasMore ?? true,
73
+ ...options.context && { context: options.context },
74
+ ...options.total !== undefined && { total: options.total }
75
+ };
76
+ fs.writeFileSync(statePath, JSON.stringify(state), "utf-8");
77
+ }
78
+ function clearCursor(options) {
79
+ const statePath = getStatePath(options);
80
+ try {
81
+ if (fs.existsSync(statePath)) {
82
+ fs.unlinkSync(statePath);
83
+ }
84
+ } catch {}
85
+ }
86
+ export {
87
+ saveCursor,
88
+ loadCursor,
89
+ clearCursor
90
+ };
@@ -0,0 +1,20 @@
1
+ // @bun
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __toESM = (mod, isNodeMode, target) => {
8
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
9
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
+ for (let key of __getOwnPropNames(mod))
11
+ if (!__hasOwnProp.call(to, key))
12
+ __defProp(to, key, {
13
+ get: () => mod[key],
14
+ enumerable: true
15
+ });
16
+ return to;
17
+ };
18
+ var __require = import.meta.require;
19
+
20
+ export { __toESM, __require };
@@ -0,0 +1,253 @@
1
+ import { Command } from "commander";
2
+ import { CancelledError, ErrorCategory, ValidationError } from "@outfitter/contracts";
3
+ import { Result } from "better-result";
4
+ /**
5
+ * Configuration for creating a CLI instance.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * const config: CLIConfig = {
10
+ * name: "waymark",
11
+ * version: "1.0.0",
12
+ * description: "A note management CLI",
13
+ * };
14
+ * ```
15
+ */
16
+ interface CLIConfig {
17
+ /** CLI name (used in help output and error messages) */
18
+ readonly name: string;
19
+ /** CLI version (displayed with --version) */
20
+ readonly version: string;
21
+ /** CLI description (displayed in help output) */
22
+ readonly description?: string;
23
+ /** Custom error handler */
24
+ readonly onError?: (error: Error) => void;
25
+ /** Custom exit handler (defaults to process.exit) */
26
+ readonly onExit?: (code: number) => never;
27
+ }
28
+ /**
29
+ * CLI instance returned by createCLI.
30
+ */
31
+ interface CLI {
32
+ /** Register a command with the CLI */
33
+ register(command: CommandBuilder | Command): this;
34
+ /** Parse arguments and execute the matched command */
35
+ parse(argv?: readonly string[]): Promise<void>;
36
+ /** Get the underlying Commander program */
37
+ readonly program: Command;
38
+ }
39
+ /**
40
+ * Configuration for a single command.
41
+ */
42
+ interface CommandConfig {
43
+ /** Command name and argument syntax (e.g., "list" or "get <id>") */
44
+ readonly name: string;
45
+ /** Command description */
46
+ readonly description?: string;
47
+ /** Command aliases */
48
+ readonly aliases?: readonly string[];
49
+ /** Whether to hide from help output */
50
+ readonly hidden?: boolean;
51
+ }
52
+ /**
53
+ * Action function executed when a command is invoked.
54
+ *
55
+ * @typeParam TFlags - Type of parsed command flags
56
+ */
57
+ type CommandAction<TFlags extends CommandFlags = CommandFlags> = (context: {
58
+ /** Parsed command-line arguments */
59
+ readonly args: readonly string[];
60
+ /** Parsed command flags */
61
+ readonly flags: TFlags;
62
+ /** Raw Commander command instance */
63
+ readonly command: Command;
64
+ }) => Promise<void> | void;
65
+ /**
66
+ * Base type for command flags.
67
+ * All flag types must extend this.
68
+ */
69
+ type CommandFlags = Record<string, unknown>;
70
+ /**
71
+ * Builder interface for constructing commands fluently.
72
+ */
73
+ interface CommandBuilder {
74
+ /** Set command description */
75
+ description(text: string): this;
76
+ /** Add a command option/flag */
77
+ option(flags: string, description: string, defaultValue?: unknown): this;
78
+ /** Add a required option */
79
+ requiredOption(flags: string, description: string, defaultValue?: unknown): this;
80
+ /** Add command aliases */
81
+ alias(alias: string): this;
82
+ /** Set the action handler */
83
+ action<TFlags extends CommandFlags = CommandFlags>(handler: CommandAction<TFlags>): this;
84
+ /** Build the underlying Commander command */
85
+ build(): Command;
86
+ }
87
+ /**
88
+ * Available output modes for CLI commands.
89
+ */
90
+ type OutputMode = "human" | "json" | "jsonl" | "tree" | "table";
91
+ /**
92
+ * Options for the output() function.
93
+ */
94
+ interface OutputOptions {
95
+ /** Force a specific output mode (overrides flag detection) */
96
+ readonly mode?: OutputMode;
97
+ /** Stream to write to (defaults to stdout) */
98
+ readonly stream?: NodeJS.WritableStream;
99
+ /** Whether to pretty-print JSON output */
100
+ readonly pretty?: boolean;
101
+ /** Exit code to use after output (undefined = don't exit) */
102
+ readonly exitCode?: number;
103
+ }
104
+ /**
105
+ * Options for collectIds() input utility.
106
+ *
107
+ * @example
108
+ * ```typescript
109
+ * const ids = await collectIds(args, {
110
+ * allowFile: true, // @file expansion
111
+ * allowStdin: true, // - reads from stdin
112
+ * });
113
+ * ```
114
+ */
115
+ interface CollectIdsOptions {
116
+ /** Allow @file expansion (reads IDs from file) */
117
+ readonly allowFile?: boolean;
118
+ /** Allow glob patterns */
119
+ readonly allowGlob?: boolean;
120
+ /** Allow reading from stdin with "-" */
121
+ readonly allowStdin?: boolean;
122
+ /** Separator for comma-separated values */
123
+ readonly separator?: string;
124
+ }
125
+ /**
126
+ * Options for expandFileArg() input utility.
127
+ */
128
+ interface ExpandFileOptions {
129
+ /** Encoding for file reads (defaults to utf-8) */
130
+ readonly encoding?: BufferEncoding;
131
+ /** Maximum file size to read (in bytes) */
132
+ readonly maxSize?: number;
133
+ /** Whether to trim the file content */
134
+ readonly trim?: boolean;
135
+ }
136
+ /**
137
+ * Options for parseGlob() input utility.
138
+ */
139
+ interface ParseGlobOptions {
140
+ /** Current working directory for glob resolution */
141
+ readonly cwd?: string;
142
+ /** Whether to follow symlinks */
143
+ readonly followSymlinks?: boolean;
144
+ /** Patterns to exclude */
145
+ readonly ignore?: readonly string[];
146
+ /** Only match files (not directories) */
147
+ readonly onlyFiles?: boolean;
148
+ /** Only match directories (not files) */
149
+ readonly onlyDirectories?: boolean;
150
+ }
151
+ /**
152
+ * Options for normalizeId().
153
+ */
154
+ interface NormalizeIdOptions {
155
+ /** Whether to lowercase the ID */
156
+ readonly lowercase?: boolean;
157
+ /** Whether to trim whitespace */
158
+ readonly trim?: boolean;
159
+ /** Minimum length requirement */
160
+ readonly minLength?: number;
161
+ /** Maximum length requirement */
162
+ readonly maxLength?: number;
163
+ /** Pattern the ID must match */
164
+ readonly pattern?: RegExp;
165
+ }
166
+ /**
167
+ * Options for confirmDestructive().
168
+ */
169
+ interface ConfirmDestructiveOptions {
170
+ /** Message to display to the user */
171
+ readonly message: string;
172
+ /** Whether to bypass confirmation (e.g., --yes flag) */
173
+ readonly bypassFlag?: boolean;
174
+ /** Number of items affected (shown in confirmation) */
175
+ readonly itemCount?: number;
176
+ }
177
+ /**
178
+ * Numeric or date range parsed from CLI input.
179
+ */
180
+ type Range = NumericRange | DateRange;
181
+ /**
182
+ * Numeric range (e.g., "1-10").
183
+ */
184
+ interface NumericRange {
185
+ readonly type: "number";
186
+ readonly min: number;
187
+ readonly max: number;
188
+ }
189
+ /**
190
+ * Date range (e.g., "2024-01-01..2024-12-31").
191
+ */
192
+ interface DateRange {
193
+ readonly type: "date";
194
+ readonly start: Date;
195
+ readonly end: Date;
196
+ }
197
+ /**
198
+ * Filter expression parsed from CLI input.
199
+ */
200
+ interface FilterExpression {
201
+ readonly field: string;
202
+ readonly value: string;
203
+ readonly operator?: "eq" | "ne" | "gt" | "lt" | "gte" | "lte" | "contains";
204
+ }
205
+ /**
206
+ * Sort criteria parsed from CLI input.
207
+ */
208
+ interface SortCriteria {
209
+ readonly field: string;
210
+ readonly direction: "asc" | "desc";
211
+ }
212
+ /**
213
+ * Key-value pair parsed from CLI input.
214
+ */
215
+ interface KeyValuePair {
216
+ readonly key: string;
217
+ readonly value: string;
218
+ }
219
+ /**
220
+ * State for paginated command results.
221
+ */
222
+ interface PaginationState {
223
+ /** Cursor for the next page */
224
+ readonly cursor: string;
225
+ /** Command that generated this state */
226
+ readonly command: string;
227
+ /** Context key for scoping pagination */
228
+ readonly context?: string;
229
+ /** Timestamp when state was created */
230
+ readonly timestamp: number;
231
+ /** Whether there are more results */
232
+ readonly hasMore: boolean;
233
+ /** Total count (if known) */
234
+ readonly total?: number;
235
+ }
236
+ /**
237
+ * Options for cursor persistence operations.
238
+ */
239
+ interface CursorOptions {
240
+ /** Command name for cursor scoping */
241
+ readonly command: string;
242
+ /** Context key for additional scoping */
243
+ readonly context?: string;
244
+ /** Tool name for XDG path resolution */
245
+ readonly toolName: string;
246
+ /** Maximum age in milliseconds before a cursor is treated as expired */
247
+ readonly maxAgeMs?: number;
248
+ /** Whether there are more results (defaults to true) */
249
+ readonly hasMore?: boolean;
250
+ /** Total count of results (if known) */
251
+ readonly total?: number;
252
+ }
253
+ export { ValidationError, SortCriteria, Result, Range, ParseGlobOptions, PaginationState, OutputOptions, OutputMode, NumericRange, NormalizeIdOptions, KeyValuePair, FilterExpression, ExpandFileOptions, ErrorCategory, DateRange, CursorOptions, ConfirmDestructiveOptions, CommandFlags, CommandConfig, CommandBuilder, CommandAction, CollectIdsOptions, CancelledError, CLIConfig, CLI };
package/dist/types.js ADDED
@@ -0,0 +1,12 @@
1
+ // @bun
2
+ import"./shared/@outfitter/cli-4yy82cmp.js";
3
+
4
+ // packages/cli/src/types.ts
5
+ import {
6
+ CancelledError,
7
+ ValidationError
8
+ } from "@outfitter/contracts";
9
+ export {
10
+ ValidationError,
11
+ CancelledError
12
+ };