@outfitter/cli 0.4.1 → 0.5.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.
package/README.md CHANGED
@@ -486,7 +486,7 @@ import type { PaginationState, CursorOptions } from "@outfitter/cli/pagination";
486
486
 
487
487
  ## Upgrading
488
488
 
489
- Run `outfitter update --guide` for version-specific migration instructions, or check the [migration docs](https://github.com/outfitter-dev/outfitter/tree/main/plugins/outfitter/shared/migrations) for detailed upgrade steps.
489
+ Run `outfitter upgrade --guide` for version-specific migration instructions, or check the [migration docs](https://github.com/outfitter-dev/outfitter/tree/main/plugins/outfitter/shared/migrations) for detailed upgrade steps.
490
490
 
491
491
  ## License
492
492
 
package/dist/actions.d.ts CHANGED
@@ -1,4 +1,6 @@
1
- import { ActionRegistry, ActionSurface, AnyActionSpec, HandlerContext } from "@outfitter/contracts";
1
+ import { SchemaCommandOptions } from "./shared/@outfitter/cli-n1k0d23k";
2
+ import { FlagPreset } from "./shared/@outfitter/cli-7n5zmndx";
3
+ import { ActionCliInputContext, ActionCliOption, ActionRegistry, ActionSurface, AnyActionSpec, HandlerContext } from "@outfitter/contracts";
2
4
  import { Command } from "commander";
3
5
  interface BuildCliCommandsOptions {
4
6
  readonly createContext?: (input: {
@@ -7,7 +9,22 @@ interface BuildCliCommandsOptions {
7
9
  flags: Record<string, unknown>;
8
10
  }) => HandlerContext;
9
11
  readonly includeSurfaces?: readonly ActionSurface[];
12
+ readonly schema?: boolean | SchemaCommandOptions;
10
13
  }
11
14
  type ActionSource = ActionRegistry | readonly AnyActionSpec[];
15
+ type ResolvedType<T> = T extends FlagPreset<infer R> ? R : never;
16
+ type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
17
+ type MergedPresetResult<TPresets extends readonly FlagPreset<Record<string, unknown>>[]> = UnionToIntersection<ResolvedType<TPresets[number]>> extends Record<string, unknown> ? UnionToIntersection<ResolvedType<TPresets[number]>> : Record<string, unknown>;
18
+ interface ActionCliPresetAdapter<TResolved extends Record<string, unknown>> {
19
+ readonly options: readonly ActionCliOption[];
20
+ readonly resolve: (input: ActionCliInputContext | Record<string, unknown>) => TResolved;
21
+ }
22
+ /**
23
+ * Compose flag presets for action-spec CLI definitions.
24
+ *
25
+ * Returns an options array for `action.cli.options` and a typed `resolve()`
26
+ * that accepts either raw flags or full `ActionCliInputContext`.
27
+ */
28
+ declare function actionCliPresets<TPresets extends readonly FlagPreset<Record<string, unknown>>[]>(...presets: TPresets): ActionCliPresetAdapter<MergedPresetResult<TPresets>>;
12
29
  declare function buildCliCommands(source: ActionSource, options?: BuildCliCommandsOptions): Command[];
13
- export { buildCliCommands, BuildCliCommandsOptions };
30
+ export { buildCliCommands, actionCliPresets, BuildCliCommandsOptions, ActionCliPresetAdapter };
package/dist/actions.js CHANGED
@@ -1,4 +1,11 @@
1
1
  // @bun
2
+ import {
3
+ createSchemaCommand
4
+ } from "./shared/@outfitter/cli-g0sn0r0b.js";
5
+ import {
6
+ composePresets
7
+ } from "./shared/@outfitter/cli-pdb7znbq.js";
8
+
2
9
  // packages/cli/src/actions.ts
3
10
  import {
4
11
  createContext as createHandlerContext,
@@ -7,6 +14,19 @@ import {
7
14
  } from "@outfitter/contracts";
8
15
  import { Command } from "commander";
9
16
  var ARGUMENT_PREFIXES = ["<", "["];
17
+ function isInputContext(input) {
18
+ return "flags" in input && typeof input.flags === "object" && "args" in input && Array.isArray(input.args);
19
+ }
20
+ function actionCliPresets(...presets) {
21
+ const composed = composePresets(...presets);
22
+ return {
23
+ options: composed.options,
24
+ resolve: (input) => {
25
+ const flags = isInputContext(input) ? input.flags : input;
26
+ return composed.resolve(flags);
27
+ }
28
+ };
29
+ }
10
30
  function isArgumentToken(token) {
11
31
  if (!token) {
12
32
  return false;
@@ -163,11 +183,19 @@ function buildCliCommands(source, options = {}) {
163
183
  }
164
184
  commands.push(groupCommand);
165
185
  }
186
+ if (options.schema !== false) {
187
+ const hasSchemaCommand = commands.some((cmd) => cmd.name() === "schema");
188
+ if (!hasSchemaCommand) {
189
+ const schemaOptions = typeof options.schema === "object" ? options.schema : undefined;
190
+ commands.push(createSchemaCommand(source, schemaOptions));
191
+ }
192
+ }
166
193
  return commands;
167
194
  }
168
195
  function isActionRegistry(source) {
169
196
  return "list" in source;
170
197
  }
171
198
  export {
172
- buildCliCommands
199
+ buildCliCommands,
200
+ actionCliPresets
173
201
  };
package/dist/cli.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { CLI, CLIConfig } from "./shared/@outfitter/cli-md9347gn";
1
+ import { CLI, CLIConfig } from "./shared/@outfitter/cli-7n5zmndx";
2
2
  /**
3
3
  * Create a new CLI instance with the given configuration.
4
4
  *
package/dist/command.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { CLI, CLIConfig, CommandAction, CommandBuilder, CommandConfig, CommandFlags, FlagPreset } from "./shared/@outfitter/cli-md9347gn";
1
+ import { CLI, CLIConfig, CommandAction, CommandBuilder, CommandConfig, CommandFlags, FlagPreset } from "./shared/@outfitter/cli-7n5zmndx";
2
2
  /**
3
3
  * Create a CLI instance with a portable return type from this module.
4
4
  */
package/dist/flags.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ColorFlags, ColorMode, ComposedPreset, ExecutionFlags, ExecutionPresetConfig, FlagPreset, FlagPresetConfig, InteractionFlags, PaginationFlags, PaginationPresetConfig, ProjectionFlags, StrictFlags, TimeWindowFlags, TimeWindowPresetConfig } from "./shared/@outfitter/cli-md9347gn";
1
+ import { BooleanFlagPresetConfig, ColorFlags, ColorMode, ComposedPreset, EnumFlagPresetConfig, ExecutionFlags, ExecutionPresetConfig, FlagPreset, FlagPresetConfig, InteractionFlags, NumberFlagPresetConfig, PaginationFlags, PaginationPresetConfig, ProjectionFlags, StrictFlags, StringListFlagPresetConfig, TimeWindowFlags, TimeWindowPresetConfig } from "./shared/@outfitter/cli-7n5zmndx";
2
2
  /**
3
3
  * Create a typed flag preset.
4
4
  *
@@ -44,6 +44,28 @@ type MergedPresetResult<TPresets extends readonly FlagPreset<Record<string, unkn
44
44
  */
45
45
  declare function composePresets<TPresets extends readonly FlagPreset<Record<string, unknown>>[]>(...presets: TPresets): ComposedPreset<MergedPresetResult<TPresets>>;
46
46
  /**
47
+ * Generic boolean custom-flag builder.
48
+ *
49
+ * Supports normal sources and negated sources so `--no-foo` patterns can
50
+ * resolve consistently across Commander flag-shape differences.
51
+ */
52
+ declare function booleanFlagPreset<TKey extends string>(config: BooleanFlagPresetConfig<TKey>): FlagPreset<{ [K in TKey] : boolean }>;
53
+ /**
54
+ * Generic enum custom-flag builder.
55
+ */
56
+ declare function enumFlagPreset<
57
+ TKey extends string,
58
+ TValue extends string
59
+ >(config: EnumFlagPresetConfig<TKey, TValue>): FlagPreset<{ [K in TKey] : TValue }>;
60
+ /**
61
+ * Generic number custom-flag builder.
62
+ */
63
+ declare function numberFlagPreset<TKey extends string>(config: NumberFlagPresetConfig<TKey>): FlagPreset<{ [K in TKey] : number }>;
64
+ /**
65
+ * Generic string-list custom-flag builder.
66
+ */
67
+ declare function stringListFlagPreset<TKey extends string>(config: StringListFlagPresetConfig<TKey>): FlagPreset<{ [K in TKey] : string[] | undefined }>;
68
+ /**
47
69
  * Verbose output flag preset.
48
70
  *
49
71
  * Adds: `-v, --verbose`
@@ -164,4 +186,4 @@ declare function executionPreset(config?: ExecutionPresetConfig): FlagPreset<Exe
164
186
  * `@outfitter/cli/pagination`.
165
187
  */
166
188
  declare function paginationPreset(config?: PaginationPresetConfig): FlagPreset<PaginationFlags>;
167
- export { verbosePreset, timeWindowPreset, strictPreset, projectionPreset, paginationPreset, interactionPreset, forcePreset, executionPreset, dryRunPreset, cwdPreset, createPreset, composePresets, colorPreset, TimeWindowPresetConfig, TimeWindowFlags, StrictFlags, ProjectionFlags, PaginationPresetConfig, PaginationFlags, InteractionFlags, FlagPresetConfig, FlagPreset, ExecutionPresetConfig, ExecutionFlags, ComposedPreset, ColorMode, ColorFlags };
189
+ export { verbosePreset, timeWindowPreset, stringListFlagPreset, strictPreset, projectionPreset, paginationPreset, numberFlagPreset, interactionPreset, forcePreset, executionPreset, enumFlagPreset, dryRunPreset, cwdPreset, createPreset, composePresets, colorPreset, booleanFlagPreset, TimeWindowPresetConfig, TimeWindowFlags, StringListFlagPresetConfig, StrictFlags, ProjectionFlags, PaginationPresetConfig, PaginationFlags, NumberFlagPresetConfig, InteractionFlags, FlagPresetConfig, FlagPreset, ExecutionPresetConfig, ExecutionFlags, EnumFlagPresetConfig, ComposedPreset, ColorMode, ColorFlags, BooleanFlagPresetConfig };
package/dist/flags.js CHANGED
@@ -1,31 +1,39 @@
1
1
  // @bun
2
2
  import {
3
+ booleanFlagPreset,
3
4
  colorPreset,
4
5
  composePresets,
5
6
  createPreset,
6
7
  cwdPreset,
7
8
  dryRunPreset,
9
+ enumFlagPreset,
8
10
  executionPreset,
9
11
  forcePreset,
10
12
  interactionPreset,
13
+ numberFlagPreset,
11
14
  paginationPreset,
12
15
  projectionPreset,
13
16
  strictPreset,
17
+ stringListFlagPreset,
14
18
  timeWindowPreset,
15
19
  verbosePreset
16
- } from "./shared/@outfitter/cli-b2zk8fb3.js";
20
+ } from "./shared/@outfitter/cli-pdb7znbq.js";
17
21
  export {
18
22
  verbosePreset,
19
23
  timeWindowPreset,
24
+ stringListFlagPreset,
20
25
  strictPreset,
21
26
  projectionPreset,
22
27
  paginationPreset,
28
+ numberFlagPreset,
23
29
  interactionPreset,
24
30
  forcePreset,
25
31
  executionPreset,
32
+ enumFlagPreset,
26
33
  dryRunPreset,
27
34
  cwdPreset,
28
35
  createPreset,
29
36
  composePresets,
30
- colorPreset
37
+ colorPreset,
38
+ booleanFlagPreset
31
39
  };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import "./shared/@outfitter/cli-qz47jk6d";
2
2
  import { ANSI, Theme, Tokens, createTheme } from "./shared/@outfitter/cli-xppg982q";
3
- import { exitWithError, output } from "./shared/@outfitter/cli-k0yvzn6d";
4
- import { OutputMode } from "./shared/@outfitter/cli-md9347gn";
3
+ import { exitWithError, output } from "./shared/@outfitter/cli-z7mgapx5";
4
+ import { OutputMode } from "./shared/@outfitter/cli-7n5zmndx";
5
5
  export { output, exitWithError, createTheme, Tokens, Theme, OutputMode, ANSI };
package/dist/input.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { CollectIdsOptions, ExpandFileOptions, FilterExpression, KeyValuePair, NormalizeIdOptions, ParseGlobOptions, Range, SortCriteria } from "./shared/@outfitter/cli-md9347gn";
1
+ import { CollectIdsOptions, ExpandFileOptions, FilterExpression, KeyValuePair, NormalizeIdOptions, ParseGlobOptions, Range, SortCriteria } from "./shared/@outfitter/cli-7n5zmndx";
2
2
  import { ValidationError } from "@outfitter/contracts";
3
3
  import { Result } from "better-result";
4
4
  /**
package/dist/output.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- import { exitWithError, output, resolveVerbose } from "./shared/@outfitter/cli-k0yvzn6d";
2
- import "./shared/@outfitter/cli-md9347gn";
1
+ import { exitWithError, output, resolveVerbose } from "./shared/@outfitter/cli-z7mgapx5";
2
+ import "./shared/@outfitter/cli-7n5zmndx";
3
3
  export { resolveVerbose, output, exitWithError };
@@ -1,4 +1,4 @@
1
- import { CursorOptions, PaginationState } from "./shared/@outfitter/cli-md9347gn";
1
+ import { CursorOptions, PaginationState } from "./shared/@outfitter/cli-7n5zmndx";
2
2
  /**
3
3
  * Load persisted pagination state for a command.
4
4
  *
package/dist/query.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { FlagPreset, OutputMode } from "./shared/@outfitter/cli-md9347gn";
1
+ import { FlagPreset, OutputMode } from "./shared/@outfitter/cli-7n5zmndx";
2
2
  /**
3
3
  * Configuration for the output mode preset.
4
4
  */
package/dist/query.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  createPreset
4
- } from "./shared/@outfitter/cli-b2zk8fb3.js";
4
+ } from "./shared/@outfitter/cli-pdb7znbq.js";
5
5
 
6
6
  // packages/cli/src/query.ts
7
7
  function outputModePreset(config) {
@@ -0,0 +1,2 @@
1
+ import { ActionManifest, ActionManifestEntry, ActionSource, GenerateManifestOptions, SchemaCommandOptions, SurfaceCommandOptions, createSchemaCommand, formatManifestHuman, generateManifest } from "./shared/@outfitter/cli-n1k0d23k";
2
+ export { generateManifest, formatManifestHuman, createSchemaCommand, SurfaceCommandOptions, SchemaCommandOptions, GenerateManifestOptions, ActionSource, ActionManifestEntry, ActionManifest };
package/dist/schema.js ADDED
@@ -0,0 +1,11 @@
1
+ // @bun
2
+ import {
3
+ createSchemaCommand,
4
+ formatManifestHuman,
5
+ generateManifest
6
+ } from "./shared/@outfitter/cli-g0sn0r0b.js";
7
+ export {
8
+ generateManifest,
9
+ formatManifestHuman,
10
+ createSchemaCommand
11
+ };
@@ -137,6 +137,99 @@ interface FlagPresetConfig<TResolved extends Record<string, unknown>> {
137
137
  readonly resolve: (flags: Record<string, unknown>) => TResolved;
138
138
  }
139
139
  /**
140
+ * Configuration for creating a custom boolean flag preset.
141
+ */
142
+ interface BooleanFlagPresetConfig<TKey extends string> {
143
+ /** Unique identifier for deduplication */
144
+ readonly id: string;
145
+ /** Resolved output property name */
146
+ readonly key: TKey;
147
+ /** Commander option definition (e.g., "--force" or "--no-codemods") */
148
+ readonly flags: string;
149
+ /** Help description for the option */
150
+ readonly description: string;
151
+ /** Default resolved value (defaults to false) */
152
+ readonly defaultValue?: boolean;
153
+ /** Candidate raw flag keys to read (defaults to [key]) */
154
+ readonly sources?: readonly string[];
155
+ /** Positive keys that should be negated (e.g., "codemods" for --no-codemods) */
156
+ readonly negatedSources?: readonly string[];
157
+ /** Whether the option is required */
158
+ readonly required?: boolean;
159
+ }
160
+ /**
161
+ * Configuration for creating a custom enum flag preset.
162
+ */
163
+ interface EnumFlagPresetConfig<
164
+ TKey extends string,
165
+ TValue extends string
166
+ > {
167
+ /** Unique identifier for deduplication */
168
+ readonly id: string;
169
+ /** Resolved output property name */
170
+ readonly key: TKey;
171
+ /** Commander option definition */
172
+ readonly flags: string;
173
+ /** Help description for the option */
174
+ readonly description: string;
175
+ /** Allowed enum values */
176
+ readonly values: readonly TValue[];
177
+ /** Fallback value when input is missing or invalid */
178
+ readonly defaultValue: TValue;
179
+ /** Candidate raw flag keys to read (defaults to [key]) */
180
+ readonly sources?: readonly string[];
181
+ /** Whether the option is required */
182
+ readonly required?: boolean;
183
+ }
184
+ /**
185
+ * Configuration for creating a custom numeric flag preset.
186
+ */
187
+ interface NumberFlagPresetConfig<TKey extends string> {
188
+ /** Unique identifier for deduplication */
189
+ readonly id: string;
190
+ /** Resolved output property name */
191
+ readonly key: TKey;
192
+ /** Commander option definition */
193
+ readonly flags: string;
194
+ /** Help description for the option */
195
+ readonly description: string;
196
+ /** Fallback value when input is missing or invalid */
197
+ readonly defaultValue: number;
198
+ /** Candidate raw flag keys to read (defaults to [key]) */
199
+ readonly sources?: readonly string[];
200
+ /** Lower bound (inclusive) */
201
+ readonly min?: number;
202
+ /** Upper bound (inclusive) */
203
+ readonly max?: number;
204
+ /** Floor parsed values (defaults to true) */
205
+ readonly integer?: boolean;
206
+ /** Whether the option is required */
207
+ readonly required?: boolean;
208
+ }
209
+ /**
210
+ * Configuration for creating a custom string-list flag preset.
211
+ */
212
+ interface StringListFlagPresetConfig<TKey extends string> {
213
+ /** Unique identifier for deduplication */
214
+ readonly id: string;
215
+ /** Resolved output property name */
216
+ readonly key: TKey;
217
+ /** Commander option definition */
218
+ readonly flags: string;
219
+ /** Help description for the option */
220
+ readonly description: string;
221
+ /** Candidate raw flag keys to read (defaults to [key]) */
222
+ readonly sources?: readonly string[];
223
+ /** Fallback list when input is missing or invalid */
224
+ readonly defaultValue?: readonly string[];
225
+ /** Split string values by this separator (defaults to ",") */
226
+ readonly separator?: string;
227
+ /** Remove duplicate values while preserving order */
228
+ readonly dedupe?: boolean;
229
+ /** Whether the option is required */
230
+ readonly required?: boolean;
231
+ }
232
+ /**
140
233
  * Result of composing multiple presets together.
141
234
  * Options are deduplicated by preset id (first wins).
142
235
  */
@@ -392,4 +485,4 @@ interface CursorOptions {
392
485
  /** Total count of results (if known) */
393
486
  readonly total?: number;
394
487
  }
395
- export { CLIConfig, CLI, CommandConfig, CommandAction, CommandFlags, CommandBuilder, VerbFamily, VerbConfig, FlagPreset, FlagPresetConfig, 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 };
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 };
@@ -0,0 +1,319 @@
1
+ // @bun
2
+ // packages/cli/src/schema.ts
3
+ import { mkdir, writeFile } from "fs/promises";
4
+ import { dirname, join } from "path";
5
+ import {
6
+ diffSurfaceMaps,
7
+ formatManifestMarkdown,
8
+ generateManifest,
9
+ generateSurfaceMap,
10
+ readSurfaceMap,
11
+ resolveSnapshotPath,
12
+ writeSurfaceMap
13
+ } from "@outfitter/schema";
14
+ import { Command } from "commander";
15
+ import { generateManifest as generateManifest2 } from "@outfitter/schema";
16
+ function formatManifestHuman(manifest, programName, actionId) {
17
+ if (actionId) {
18
+ return formatActionDetail(manifest, actionId);
19
+ }
20
+ return formatSummary(manifest, programName);
21
+ }
22
+ function formatSummary(manifest, programName) {
23
+ const lines = [];
24
+ const name = programName ?? "cli";
25
+ const actionCount = manifest.actions.length;
26
+ const surfaceCount = manifest.surfaces.length;
27
+ const surfaceLabel = surfaceCount === 1 ? `${surfaceCount} surface` : `${surfaceCount} surfaces`;
28
+ lines.push(`${name} \u2014 ${actionCount} actions across ${surfaceLabel}`);
29
+ lines.push("");
30
+ const grouped = new Map;
31
+ const ungrouped = [];
32
+ for (const action of manifest.actions) {
33
+ const group = action.cli?.group;
34
+ if (group) {
35
+ const existing = grouped.get(group) ?? [];
36
+ existing.push(action);
37
+ grouped.set(group, existing);
38
+ } else {
39
+ ungrouped.push(action);
40
+ }
41
+ }
42
+ for (const [groupName, groupActions] of grouped.entries()) {
43
+ lines.push(groupName);
44
+ for (const action of groupActions) {
45
+ const commandPart = action.cli?.command ?? action.id;
46
+ const isBase = !commandPart || commandPart.startsWith("[") || commandPart.startsWith("<");
47
+ const displayCommand = isBase ? ` ${groupName} ${commandPart ?? ""}`.trimEnd() : ` ${groupName} ${commandPart}`;
48
+ const desc = action.cli?.description ?? action.description ?? "";
49
+ lines.push(padCommand(displayCommand, desc));
50
+ }
51
+ lines.push("");
52
+ }
53
+ for (const action of ungrouped) {
54
+ const commandPart = action.cli?.command ?? action.id;
55
+ const desc = action.cli?.description ?? action.description ?? "";
56
+ lines.push(padCommand(`${commandPart}`, desc));
57
+ }
58
+ lines.push("");
59
+ lines.push("Use --output json for machine-readable format.");
60
+ lines.push("Use --surface <name> to filter (cli, mcp, api, server).");
61
+ return lines.join(`
62
+ `);
63
+ }
64
+ function padCommand(command, description) {
65
+ const padding = Math.max(1, 32 - command.length);
66
+ return `${command}${" ".repeat(padding)}${description}`;
67
+ }
68
+ function formatActionDetail(manifest, actionId) {
69
+ const entry = manifest.actions.find((a) => a.id === actionId);
70
+ if (!entry) {
71
+ return `Unknown action: ${actionId}`;
72
+ }
73
+ const lines = [];
74
+ const desc = entry.cli?.description ?? entry.description ?? "";
75
+ lines.push(`${entry.id} \u2014 ${desc}`);
76
+ lines.push("");
77
+ if (entry.cli) {
78
+ const group = entry.cli.group;
79
+ const commandPart = entry.cli.command ?? entry.id;
80
+ const fullCommand = group ? `${group} ${commandPart}` : commandPart;
81
+ lines.push(` Command: ${fullCommand}`);
82
+ }
83
+ lines.push(` Surfaces: ${entry.surfaces.join(", ")}`);
84
+ if (entry.cli?.group) {
85
+ lines.push(` Group: ${entry.cli.group}`);
86
+ }
87
+ if (entry.cli?.aliases && entry.cli.aliases.length > 0) {
88
+ lines.push(` Aliases: ${entry.cli.aliases.join(", ")}`);
89
+ }
90
+ if (entry.cli?.options && entry.cli.options.length > 0) {
91
+ lines.push("");
92
+ lines.push(" Options:");
93
+ for (const opt of entry.cli.options) {
94
+ const defaultStr = opt.defaultValue !== undefined ? ` [${String(opt.defaultValue)}]` : "";
95
+ lines.push(padCommand(` ${opt.flags}`, `${opt.description}${defaultStr}`));
96
+ }
97
+ }
98
+ if (entry.mcp) {
99
+ lines.push("");
100
+ lines.push(" MCP:");
101
+ if (entry.mcp.tool) {
102
+ lines.push(` Tool: ${entry.mcp.tool}`);
103
+ }
104
+ if (entry.mcp.description) {
105
+ lines.push(` Description: ${entry.mcp.description}`);
106
+ }
107
+ }
108
+ return lines.join(`
109
+ `);
110
+ }
111
+ function handleShow(source, programName, parentCmd, actionArg, cmdOptions) {
112
+ const manifestOptions = {};
113
+ if (cmdOptions.surface) {
114
+ manifestOptions.surface = cmdOptions.surface;
115
+ }
116
+ const manifest = generateManifest(source, manifestOptions);
117
+ if (cmdOptions.output === "json") {
118
+ const indent = cmdOptions.pretty ? 2 : undefined;
119
+ if (actionArg) {
120
+ const entry = manifest.actions.find((a) => a.id === actionArg);
121
+ if (!entry) {
122
+ process.stderr.write(`Unknown action: ${actionArg}
123
+ `);
124
+ process.exitCode = 1;
125
+ return;
126
+ }
127
+ process.stdout.write(`${JSON.stringify(entry, null, indent)}
128
+ `);
129
+ } else {
130
+ process.stdout.write(`${JSON.stringify(manifest, null, indent)}
131
+ `);
132
+ }
133
+ return;
134
+ }
135
+ const resolvedName = programName ?? parentCmd.parent?.name() ?? undefined;
136
+ const output = formatManifestHuman(manifest, resolvedName, actionArg);
137
+ process.stdout.write(`${output}
138
+ `);
139
+ }
140
+ function createSchemaCommand(source, options) {
141
+ const cmd = new Command("schema").description("Show CLI schema for machine or human consumption").argument("[action]", "Show detail for a specific action").option("--output <mode>", "Output mode (human, json)", "human").option("--surface <name>", "Filter by surface (cli, mcp, api, server)").option("--pretty", "Pretty-print JSON output").action((actionArg, cmdOptions) => {
142
+ handleShow(source, options?.programName, cmd, actionArg, cmdOptions);
143
+ });
144
+ const showCmd = new Command("show").description("Show schema for all actions or a specific action").argument("[action]", "Show detail for a specific action").option("--output <mode>", "Output mode (human, json)", "human").option("--surface <name>", "Filter by surface (cli, mcp, api, server)").option("--pretty", "Pretty-print JSON output").action((actionArg, cmdOptions) => {
145
+ handleShow(source, options?.programName, cmd, actionArg, cmdOptions);
146
+ });
147
+ cmd.addCommand(showCmd);
148
+ if (options?.surface) {
149
+ const surfaceOpts = options.surface;
150
+ const cwd = surfaceOpts.cwd ?? process.cwd();
151
+ const outputDir = surfaceOpts.outputDir ?? ".outfitter";
152
+ const generateCmd = new Command("generate").description("Generate surface map and write to disk").option("--dry-run", "Print surface map without writing to disk").option("--snapshot <version>", "Write snapshot to .outfitter/snapshots/<version>.json").action(async (genOptions) => {
153
+ const surfaceMap = generateSurfaceMap(source, {
154
+ generator: "build"
155
+ });
156
+ if (genOptions.dryRun) {
157
+ process.stdout.write(`${JSON.stringify(surfaceMap, null, 2)}
158
+ `);
159
+ return;
160
+ }
161
+ if (genOptions.snapshot) {
162
+ const snapshotPath = resolveSnapshotPath(cwd, outputDir, genOptions.snapshot);
163
+ await writeSurfaceMap(surfaceMap, snapshotPath);
164
+ process.stdout.write(`Snapshot written to ${snapshotPath}
165
+ `);
166
+ return;
167
+ }
168
+ const outputPath = join(cwd, outputDir, "surface.json");
169
+ await writeSurfaceMap(surfaceMap, outputPath);
170
+ process.stdout.write(`Surface map written to ${outputPath}
171
+ `);
172
+ });
173
+ cmd.addCommand(generateCmd);
174
+ const diffCmd = new Command("diff").description("Compare runtime schema against committed surface map").option("--output <mode>", "Output mode (human, json)", "human").option("--against <version>", "Compare runtime against a named snapshot").option("--from <version>", "Base snapshot for snapshot-to-snapshot diff").option("--to <version>", "Target snapshot for snapshot-to-snapshot diff").action(async (diffOptions) => {
175
+ let left;
176
+ let right;
177
+ if (diffOptions.from && !diffOptions.to || !diffOptions.from && diffOptions.to) {
178
+ process.stderr.write(`Both --from and --to are required for snapshot-to-snapshot diff.
179
+ `);
180
+ process.exitCode = 1;
181
+ return;
182
+ }
183
+ if (diffOptions.from && diffOptions.to) {
184
+ const fromPath = resolveSnapshotPath(cwd, outputDir, diffOptions.from);
185
+ const toPath = resolveSnapshotPath(cwd, outputDir, diffOptions.to);
186
+ try {
187
+ left = await readSurfaceMap(fromPath);
188
+ } catch (err) {
189
+ if (err.code === "ENOENT") {
190
+ process.stderr.write(`No snapshot at ${fromPath}
191
+ `);
192
+ } else {
193
+ process.stderr.write(`Failed to read snapshot at ${fromPath}: ${err instanceof Error ? err.message : String(err)}
194
+ `);
195
+ }
196
+ process.exitCode = 1;
197
+ return;
198
+ }
199
+ try {
200
+ right = await readSurfaceMap(toPath);
201
+ } catch (err) {
202
+ if (err.code === "ENOENT") {
203
+ process.stderr.write(`No snapshot at ${toPath}
204
+ `);
205
+ } else {
206
+ process.stderr.write(`Failed to read snapshot at ${toPath}: ${err instanceof Error ? err.message : String(err)}
207
+ `);
208
+ }
209
+ process.exitCode = 1;
210
+ return;
211
+ }
212
+ } else if (diffOptions.against) {
213
+ const snapshotPath = resolveSnapshotPath(cwd, outputDir, diffOptions.against);
214
+ try {
215
+ left = await readSurfaceMap(snapshotPath);
216
+ } catch (err) {
217
+ if (err.code === "ENOENT") {
218
+ process.stderr.write(`No snapshot at ${snapshotPath}
219
+ `);
220
+ } else {
221
+ process.stderr.write(`Failed to read snapshot at ${snapshotPath}: ${err instanceof Error ? err.message : String(err)}
222
+ `);
223
+ }
224
+ process.exitCode = 1;
225
+ return;
226
+ }
227
+ right = generateSurfaceMap(source, { generator: "runtime" });
228
+ } else {
229
+ const surfacePath = join(cwd, outputDir, "surface.json");
230
+ try {
231
+ left = await readSurfaceMap(surfacePath);
232
+ } catch (err) {
233
+ if (err.code === "ENOENT") {
234
+ process.stderr.write(`No committed surface map at ${surfacePath}
235
+ `);
236
+ process.stderr.write(`Run 'schema generate' first.
237
+ `);
238
+ } else {
239
+ process.stderr.write(`Failed to read surface map at ${surfacePath}: ${err instanceof Error ? err.message : String(err)}
240
+ `);
241
+ }
242
+ process.exitCode = 1;
243
+ return;
244
+ }
245
+ right = generateSurfaceMap(source, { generator: "runtime" });
246
+ }
247
+ const result = diffSurfaceMaps(left, right);
248
+ if (diffOptions.output === "json") {
249
+ process.stdout.write(`${JSON.stringify(result, null, 2)}
250
+ `);
251
+ } else {
252
+ if (!result.hasChanges) {
253
+ process.stdout.write(`No schema drift detected.
254
+ `);
255
+ return;
256
+ }
257
+ const lines = [];
258
+ lines.push(`Schema drift detected:
259
+ `);
260
+ if (result.added.length > 0) {
261
+ lines.push(" Added:");
262
+ for (const entry of result.added) {
263
+ lines.push(` + ${entry.id}`);
264
+ }
265
+ }
266
+ if (result.removed.length > 0) {
267
+ lines.push(" Removed:");
268
+ for (const entry of result.removed) {
269
+ lines.push(` - ${entry.id}`);
270
+ }
271
+ }
272
+ if (result.modified.length > 0) {
273
+ lines.push(" Modified:");
274
+ for (const entry of result.modified) {
275
+ lines.push(` ~ ${entry.id} (${entry.changes.join(", ")})`);
276
+ }
277
+ }
278
+ if (result.metadataChanges.length > 0) {
279
+ lines.push(" Metadata:");
280
+ for (const field of result.metadataChanges) {
281
+ lines.push(` ~ ${field}`);
282
+ }
283
+ }
284
+ process.stdout.write(`${lines.join(`
285
+ `)}
286
+ `);
287
+ }
288
+ if (result.hasChanges) {
289
+ process.exitCode = 1;
290
+ }
291
+ });
292
+ cmd.addCommand(diffCmd);
293
+ const docsCmd = new Command("docs").description("Generate markdown reference documentation").option("--surface <name>", "Which surface to document (cli, mcp)", "mcp").option("--output-dir <dir>", "Directory to write the reference doc", "docs/reference").option("--dry-run", "Print to stdout instead of writing to disk").action(async (docsOptions) => {
294
+ const surface = docsOptions.surface ?? "mcp";
295
+ if (surface !== "mcp" && surface !== "cli") {
296
+ process.stderr.write(`Unsupported surface for docs: "${surface}". Use "mcp" or "cli".
297
+ `);
298
+ process.exitCode = 1;
299
+ return;
300
+ }
301
+ const manifest = generateManifest(source, { surface });
302
+ const markdown = formatManifestMarkdown(manifest, { surface });
303
+ if (docsOptions.dryRun) {
304
+ process.stdout.write(markdown);
305
+ return;
306
+ }
307
+ const outDir = docsOptions.outputDir ?? "docs/reference";
308
+ const outputPath = join(cwd, outDir, `${surface.toUpperCase()}_REFERENCE.md`);
309
+ await mkdir(dirname(outputPath), { recursive: true });
310
+ await writeFile(outputPath, markdown, "utf-8");
311
+ process.stdout.write(`Reference written to ${outputPath}
312
+ `);
313
+ });
314
+ cmd.addCommand(docsCmd);
315
+ }
316
+ return cmd;
317
+ }
318
+
319
+ export { formatManifestHuman, createSchemaCommand, generateManifest2 as generateManifest };
@@ -0,0 +1,33 @@
1
+ import { ActionManifest, ActionSource } from "@outfitter/schema";
2
+ import { Command } from "commander";
3
+ import { ActionManifest as ActionManifest2, ActionManifestEntry, ActionSource as ActionSource2, GenerateManifestOptions } from "@outfitter/schema";
4
+ import { generateManifest } from "@outfitter/schema";
5
+ interface SurfaceCommandOptions {
6
+ readonly cwd?: string;
7
+ readonly outputDir?: string;
8
+ }
9
+ interface SchemaCommandOptions {
10
+ readonly programName?: string;
11
+ readonly surface?: SurfaceCommandOptions;
12
+ }
13
+ /**
14
+ * Format a manifest for human-readable terminal output.
15
+ *
16
+ * @param manifest - The manifest to format
17
+ * @param programName - CLI program name (for header)
18
+ * @param actionId - If provided, show detail for this single action
19
+ * @returns Formatted string
20
+ */
21
+ declare function formatManifestHuman(manifest: ActionManifest, programName?: string, actionId?: string): string;
22
+ /**
23
+ * Create a `schema` command for CLI introspection.
24
+ *
25
+ * When `options.surface` is provided, adds `generate` and `diff` subcommands
26
+ * for surface map file I/O and drift detection.
27
+ *
28
+ * @param source - ActionRegistry or array of ActionSpec
29
+ * @param options - Command configuration
30
+ * @returns A Commander command instance
31
+ */
32
+ declare function createSchemaCommand(source: ActionSource, options?: SchemaCommandOptions): Command;
33
+ export { SurfaceCommandOptions, SchemaCommandOptions, formatManifestHuman, createSchemaCommand, ActionManifest2 as ActionManifest, ActionManifestEntry, ActionSource2 as ActionSource, GenerateManifestOptions, generateManifest };
@@ -46,6 +46,149 @@ function composePresets(...presets) {
46
46
  composed[PRESET_IDS] = mergedIds;
47
47
  return composed;
48
48
  }
49
+ function resolveSourceKeys(key, sources) {
50
+ return sources && sources.length > 0 ? sources : [key];
51
+ }
52
+ function booleanFlagPreset(config) {
53
+ const sources = resolveSourceKeys(config.key, config.sources);
54
+ const defaultValue = config.defaultValue ?? false;
55
+ const isNegatedFlag = config.flags.includes("--no-");
56
+ const optionDefault = isNegatedFlag && config.defaultValue === undefined ? {} : { defaultValue };
57
+ return createPreset({
58
+ id: config.id,
59
+ options: [
60
+ {
61
+ flags: config.flags,
62
+ description: config.description,
63
+ ...optionDefault,
64
+ ...config.required === true ? { required: true } : {}
65
+ }
66
+ ],
67
+ resolve: (flags) => {
68
+ for (const source of sources) {
69
+ const value = flags[source];
70
+ if (typeof value === "boolean") {
71
+ return { [config.key]: value };
72
+ }
73
+ }
74
+ if (config.negatedSources) {
75
+ for (const source of config.negatedSources) {
76
+ const value = flags[source];
77
+ if (typeof value === "boolean") {
78
+ return { [config.key]: !value };
79
+ }
80
+ }
81
+ }
82
+ return { [config.key]: defaultValue };
83
+ }
84
+ });
85
+ }
86
+ function enumFlagPreset(config) {
87
+ const sources = resolveSourceKeys(config.key, config.sources);
88
+ const allowed = new Set(config.values);
89
+ return createPreset({
90
+ id: config.id,
91
+ options: [
92
+ {
93
+ flags: config.flags,
94
+ description: config.description,
95
+ ...config.required === true ? { required: true } : {}
96
+ }
97
+ ],
98
+ resolve: (flags) => {
99
+ for (const source of sources) {
100
+ const value = flags[source];
101
+ if (typeof value === "string" && allowed.has(value)) {
102
+ return { [config.key]: value };
103
+ }
104
+ }
105
+ return {
106
+ [config.key]: config.defaultValue
107
+ };
108
+ }
109
+ });
110
+ }
111
+ function numberFlagPreset(config) {
112
+ const sources = resolveSourceKeys(config.key, config.sources);
113
+ return createPreset({
114
+ id: config.id,
115
+ options: [
116
+ {
117
+ flags: config.flags,
118
+ description: config.description,
119
+ ...config.required === true ? { required: true } : {}
120
+ }
121
+ ],
122
+ resolve: (flags) => {
123
+ let parsed;
124
+ for (const source of sources) {
125
+ const value = flags[source];
126
+ if (value === "" || value == null || typeof value === "boolean") {
127
+ continue;
128
+ }
129
+ const numeric = Number(value);
130
+ if (Number.isFinite(numeric)) {
131
+ parsed = config.integer === false ? numeric : Math.floor(numeric);
132
+ break;
133
+ }
134
+ }
135
+ if (parsed === undefined) {
136
+ parsed = config.defaultValue;
137
+ }
138
+ if (typeof config.min === "number" && Number.isFinite(config.min)) {
139
+ parsed = Math.max(parsed, config.min);
140
+ }
141
+ if (typeof config.max === "number" && Number.isFinite(config.max)) {
142
+ parsed = Math.min(parsed, config.max);
143
+ }
144
+ return { [config.key]: parsed };
145
+ }
146
+ });
147
+ }
148
+ function normalizeStringListInput(value, separator) {
149
+ if (Array.isArray(value)) {
150
+ const items2 = value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean);
151
+ return items2.length > 0 ? items2 : undefined;
152
+ }
153
+ if (typeof value !== "string")
154
+ return;
155
+ const items = value.split(separator).map((item) => item.trim()).filter(Boolean);
156
+ return items.length > 0 ? items : undefined;
157
+ }
158
+ function stringListFlagPreset(config) {
159
+ const sources = resolveSourceKeys(config.key, config.sources);
160
+ const separator = config.separator ?? ",";
161
+ const fallback = config.defaultValue === undefined ? undefined : [...config.defaultValue];
162
+ return createPreset({
163
+ id: config.id,
164
+ options: [
165
+ {
166
+ flags: config.flags,
167
+ description: config.description,
168
+ ...config.required === true ? { required: true } : {}
169
+ }
170
+ ],
171
+ resolve: (flags) => {
172
+ let resolved;
173
+ for (const source of sources) {
174
+ const parsed = normalizeStringListInput(flags[source], separator);
175
+ if (parsed !== undefined) {
176
+ resolved = parsed;
177
+ break;
178
+ }
179
+ }
180
+ if (resolved === undefined) {
181
+ resolved = fallback === undefined ? undefined : [...fallback];
182
+ }
183
+ if (resolved && config.dedupe) {
184
+ resolved = [...new Set(resolved)];
185
+ }
186
+ return {
187
+ [config.key]: resolved
188
+ };
189
+ }
190
+ });
191
+ }
49
192
  function verbosePreset() {
50
193
  return createPreset({
51
194
  id: "verbose",
@@ -354,4 +497,4 @@ function paginationPreset(config) {
354
497
  });
355
498
  }
356
499
 
357
- export { createPreset, composePresets, verbosePreset, cwdPreset, dryRunPreset, forcePreset, interactionPreset, strictPreset, colorPreset, projectionPreset, timeWindowPreset, executionPreset, paginationPreset };
500
+ export { createPreset, composePresets, booleanFlagPreset, enumFlagPreset, numberFlagPreset, stringListFlagPreset, verbosePreset, cwdPreset, dryRunPreset, forcePreset, interactionPreset, strictPreset, colorPreset, projectionPreset, timeWindowPreset, executionPreset, paginationPreset };
@@ -1,4 +1,4 @@
1
- import { OutputOptions } from "./cli-md9347gn";
1
+ import { OutputOptions } from "./cli-7n5zmndx";
2
2
  /**
3
3
  * Output data to the console with automatic mode selection.
4
4
  *
package/dist/types.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { CLI, CLIConfig, CancelledError, CollectIdsOptions, ColorFlags, ColorMode, CommandAction, CommandBuilder, CommandConfig, CommandFlags, ComposedPreset, CursorOptions, DateRange, ErrorCategory, ExecutionFlags, ExecutionPresetConfig, ExpandFileOptions, FilterExpression, FlagPreset, FlagPresetConfig, InteractionFlags, KeyValuePair, NormalizeIdOptions, NumericRange, OutputMode, OutputOptions, PaginationFlags, PaginationPresetConfig, PaginationState, ParseGlobOptions, ProjectionFlags, Range, Result, SortCriteria, StrictFlags, TimeWindowFlags, TimeWindowPresetConfig, ValidationError, VerbConfig, VerbFamily } from "./shared/@outfitter/cli-md9347gn";
2
- export { VerbFamily, VerbConfig, ValidationError, TimeWindowPresetConfig, TimeWindowFlags, StrictFlags, SortCriteria, Result, Range, ProjectionFlags, ParseGlobOptions, PaginationState, PaginationPresetConfig, PaginationFlags, OutputOptions, OutputMode, NumericRange, NormalizeIdOptions, KeyValuePair, InteractionFlags, FlagPresetConfig, FlagPreset, FilterExpression, ExpandFileOptions, ExecutionPresetConfig, ExecutionFlags, ErrorCategory, DateRange, CursorOptions, ComposedPreset, CommandFlags, CommandConfig, CommandBuilder, CommandAction, ColorMode, ColorFlags, CollectIdsOptions, CancelledError, CLIConfig, CLI };
1
+ import { BooleanFlagPresetConfig, CLI, CLIConfig, CancelledError, CollectIdsOptions, ColorFlags, ColorMode, CommandAction, CommandBuilder, CommandConfig, CommandFlags, ComposedPreset, CursorOptions, DateRange, EnumFlagPresetConfig, ErrorCategory, ExecutionFlags, ExecutionPresetConfig, ExpandFileOptions, FilterExpression, FlagPreset, FlagPresetConfig, InteractionFlags, KeyValuePair, NormalizeIdOptions, NumberFlagPresetConfig, NumericRange, OutputMode, OutputOptions, PaginationFlags, PaginationPresetConfig, PaginationState, ParseGlobOptions, ProjectionFlags, Range, Result, SortCriteria, StrictFlags, StringListFlagPresetConfig, TimeWindowFlags, TimeWindowPresetConfig, ValidationError, VerbConfig, VerbFamily } from "./shared/@outfitter/cli-7n5zmndx";
2
+ export { VerbFamily, VerbConfig, ValidationError, TimeWindowPresetConfig, TimeWindowFlags, StringListFlagPresetConfig, StrictFlags, SortCriteria, Result, Range, ProjectionFlags, ParseGlobOptions, PaginationState, PaginationPresetConfig, PaginationFlags, OutputOptions, OutputMode, NumericRange, NumberFlagPresetConfig, NormalizeIdOptions, KeyValuePair, InteractionFlags, FlagPresetConfig, FlagPreset, FilterExpression, ExpandFileOptions, ExecutionPresetConfig, ExecutionFlags, ErrorCategory, EnumFlagPresetConfig, DateRange, CursorOptions, ComposedPreset, CommandFlags, CommandConfig, CommandBuilder, CommandAction, ColorMode, ColorFlags, CollectIdsOptions, CancelledError, CLIConfig, CLI, BooleanFlagPresetConfig };
package/dist/verbs.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { CommandBuilder, VerbConfig, VerbFamily } from "./shared/@outfitter/cli-md9347gn";
1
+ import { CommandBuilder, VerbConfig, VerbFamily } from "./shared/@outfitter/cli-7n5zmndx";
2
2
  /**
3
3
  * Built-in verb families with standard primary verbs and aliases.
4
4
  *
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@outfitter/cli",
3
3
  "description": "Typed CLI runtime with terminal detection, rendering, output contracts, and input parsing",
4
- "version": "0.4.1",
4
+ "version": "0.5.1",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist"
@@ -70,6 +70,12 @@
70
70
  "default": "./dist/query.js"
71
71
  }
72
72
  },
73
+ "./schema": {
74
+ "import": {
75
+ "types": "./dist/schema.d.ts",
76
+ "default": "./dist/schema.js"
77
+ }
78
+ },
73
79
  "./terminal": {
74
80
  "import": {
75
81
  "types": "./dist/terminal/index.d.ts",
@@ -109,7 +115,8 @@
109
115
  "lint": "biome lint ./src",
110
116
  "lint:fix": "biome lint --write ./src",
111
117
  "typecheck": "tsc --noEmit",
112
- "clean": "rm -rf dist"
118
+ "clean": "rm -rf dist",
119
+ "prepublishOnly": "bun ../../scripts/check-publish-manifest.ts"
113
120
  },
114
121
  "dependencies": {
115
122
  "@clack/prompts": "^0.11.0",
@@ -119,12 +126,14 @@
119
126
  "peerDependencies": {
120
127
  "@outfitter/config": ">=0.3.0",
121
128
  "@outfitter/contracts": ">=0.2.0",
129
+ "@outfitter/schema": ">=0.1.0",
122
130
  "@outfitter/types": ">=0.2.0",
123
131
  "zod": "^4.3.5"
124
132
  },
125
133
  "devDependencies": {
126
- "@outfitter/config": "0.3.1",
127
- "@outfitter/contracts": "0.3.0",
134
+ "@outfitter/config": "0.3.2",
135
+ "@outfitter/contracts": "0.4.0",
136
+ "@outfitter/schema": "0.2.1",
128
137
  "@types/bun": "^1.3.7",
129
138
  "@types/node": "^25.0.10",
130
139
  "typescript": "^5.9.3"