@outfitter/cli 0.5.3 → 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.
Files changed (106) hide show
  1. package/README.md +105 -2
  2. package/dist/actions.d.ts +5 -2
  3. package/dist/actions.js +2 -2
  4. package/dist/cli.d.ts +1 -1
  5. package/dist/cli.js +8 -1
  6. package/dist/command.d.ts +3 -43
  7. package/dist/command.js +241 -13
  8. package/dist/envelope.d.ts +5 -0
  9. package/dist/envelope.js +160 -0
  10. package/dist/flags.d.ts +5 -189
  11. package/dist/flags.js +5 -1
  12. package/dist/hints.d.ts +34 -0
  13. package/dist/hints.js +26 -0
  14. package/dist/index.d.ts +3 -2
  15. package/dist/input.d.ts +3 -124
  16. package/dist/input.js +14 -359
  17. package/dist/internal/envelope-helpers.d.ts +4 -0
  18. package/dist/internal/envelope-helpers.js +24 -0
  19. package/dist/internal/envelope-types.d.ts +3 -0
  20. package/dist/internal/envelope-types.js +1 -0
  21. package/dist/internal/flag-builders.d.ts +3 -0
  22. package/dist/internal/flag-builders.js +155 -0
  23. package/dist/internal/flag-types.d.ts +3 -0
  24. package/dist/internal/flag-types.js +13 -0
  25. package/dist/internal/hint-action-graph.d.ts +5 -0
  26. package/dist/internal/hint-action-graph.js +11 -0
  27. package/dist/internal/hint-command-tree.d.ts +5 -0
  28. package/dist/internal/hint-command-tree.js +9 -0
  29. package/dist/internal/hint-error-recovery.d.ts +2 -0
  30. package/dist/internal/hint-error-recovery.js +7 -0
  31. package/dist/internal/hint-types.d.ts +4 -0
  32. package/dist/internal/hint-types.js +1 -0
  33. package/dist/internal/input-helpers.d.ts +18 -0
  34. package/dist/internal/input-helpers.js +11 -0
  35. package/dist/internal/input-normalization.d.ts +3 -0
  36. package/dist/internal/input-normalization.js +9 -0
  37. package/dist/internal/input-parsers.d.ts +3 -0
  38. package/dist/internal/input-parsers.js +19 -0
  39. package/dist/internal/input-security.d.ts +22 -0
  40. package/dist/internal/input-security.js +11 -0
  41. package/dist/internal/output-formatting.d.ts +3 -0
  42. package/dist/internal/output-formatting.js +21 -0
  43. package/dist/internal/presets.d.ts +3 -0
  44. package/dist/{shared/@outfitter/cli-pdb7znbq.js → internal/presets.js} +49 -223
  45. package/dist/internal/schema-commands.d.ts +3 -0
  46. package/dist/{shared/@outfitter/cli-5vtr4bdt.js → internal/schema-commands.js} +8 -99
  47. package/dist/internal/schema-formatting.d.ts +2 -0
  48. package/dist/internal/schema-formatting.js +7 -0
  49. package/dist/internal/schema-types.d.ts +2 -0
  50. package/dist/internal/schema-types.js +1 -0
  51. package/dist/output.d.ts +4 -3
  52. package/dist/output.js +13 -166
  53. package/dist/pagination.d.ts +1 -1
  54. package/dist/query.d.ts +84 -2
  55. package/dist/query.js +8 -45
  56. package/dist/schema-input.d.ts +80 -0
  57. package/dist/schema-input.js +15 -0
  58. package/dist/schema.d.ts +4 -1
  59. package/dist/schema.js +1 -1
  60. package/dist/shared/@outfitter/cli-10wxfc78.d.ts +45 -0
  61. package/dist/shared/@outfitter/cli-16wg5mka.d.ts +71 -0
  62. package/dist/shared/@outfitter/cli-1q5redaj.js +267 -0
  63. package/dist/shared/@outfitter/cli-2dfxs239.js +98 -0
  64. package/dist/shared/@outfitter/cli-30mt7c5w.d.ts +112 -0
  65. package/dist/shared/@outfitter/cli-3jta1h1h.js +134 -0
  66. package/dist/shared/@outfitter/cli-4h85mpth.js +76 -0
  67. package/dist/shared/@outfitter/cli-6shkwxdc.js +28 -0
  68. package/dist/shared/@outfitter/cli-89335n9a.js +16 -0
  69. package/dist/shared/@outfitter/cli-8999qjdd.js +3 -0
  70. package/dist/shared/@outfitter/cli-8cfxdady.js +60 -0
  71. package/dist/shared/@outfitter/cli-bcajqy33.d.ts +25 -0
  72. package/dist/shared/@outfitter/cli-c09332vm.d.ts +39 -0
  73. package/dist/shared/@outfitter/cli-cgha038c.d.ts +3 -0
  74. package/dist/shared/@outfitter/{cli-zahqsaby.js → cli-d40m2x1d.js} +19 -3
  75. package/dist/shared/@outfitter/cli-dg0cz7rw.js +127 -0
  76. package/dist/shared/@outfitter/cli-dv8kk4jw.d.ts +24 -0
  77. package/dist/shared/@outfitter/cli-g43887b7.js +20 -0
  78. package/dist/shared/@outfitter/cli-gqtkhgw4.js +52 -0
  79. package/dist/shared/@outfitter/cli-h4ejpmjs.d.ts +104 -0
  80. package/dist/shared/@outfitter/cli-htzez8v2.js +70 -0
  81. package/dist/shared/@outfitter/cli-hvg2m5gf.js +79 -0
  82. package/dist/shared/@outfitter/cli-n54zs151.d.ts +78 -0
  83. package/dist/shared/@outfitter/cli-nbpgw7z7.d.ts +15 -0
  84. package/dist/shared/@outfitter/cli-nkt399zf.d.ts +94 -0
  85. package/dist/shared/@outfitter/cli-pmd04gtv.d.ts +60 -0
  86. package/dist/shared/@outfitter/{cli-xy3gs50c.d.ts → cli-q6csxmeh.d.ts} +19 -12
  87. package/dist/shared/@outfitter/cli-qcskd96y.d.ts +11 -0
  88. package/dist/shared/@outfitter/cli-ry7btmy4.js +118 -0
  89. package/dist/shared/@outfitter/cli-sy99pjyj.js +32 -0
  90. package/dist/shared/@outfitter/cli-tm2fzngs.d.ts +23 -0
  91. package/dist/shared/@outfitter/cli-vvvhjwks.js +106 -0
  92. package/dist/shared/@outfitter/cli-wjv7g1aq.d.ts +16 -0
  93. package/dist/shared/@outfitter/{cli-98aa9104.d.ts → cli-x6qr7bnd.d.ts} +338 -16
  94. package/dist/shared/@outfitter/cli-xde45xcc.d.ts +53 -0
  95. package/dist/shared/@outfitter/cli-xw8ys1je.d.ts +123 -0
  96. package/dist/shared/@outfitter/cli-yfewnyc2.d.ts +43 -0
  97. package/dist/shared/@outfitter/cli-zkzj0q4q.js +99 -0
  98. package/dist/shared/@outfitter/cli-zv3ah6f0.js +3 -0
  99. package/dist/streaming.d.ts +47 -0
  100. package/dist/streaming.js +13 -0
  101. package/dist/truncation.d.ts +104 -0
  102. package/dist/truncation.js +111 -0
  103. package/dist/types.d.ts +2 -2
  104. package/dist/verbs.d.ts +1 -1
  105. package/package.json +55 -25
  106. package/dist/shared/@outfitter/cli-n1k0d23k.d.ts +0 -33
@@ -0,0 +1,79 @@
1
+ // @bun
2
+ // packages/cli/src/internal/hint-action-graph.ts
3
+ function buildActionGraph(program) {
4
+ const nodes = [];
5
+ const edges = [];
6
+ const warnings = [];
7
+ const commandNames = new Set;
8
+ function collectCommandNames(cmds, prefix = "") {
9
+ for (const cmd of cmds) {
10
+ const leaf = cmd.name();
11
+ const fullName = prefix ? `${prefix} ${leaf}` : leaf;
12
+ commandNames.add(fullName);
13
+ if (cmd.commands.length > 0) {
14
+ collectCommandNames(cmd.commands, fullName);
15
+ }
16
+ }
17
+ }
18
+ collectCommandNames(program.commands);
19
+ function walkCommands(cmds, prefix = "") {
20
+ for (const cmd of cmds) {
21
+ const leaf = cmd.name();
22
+ const fullName = prefix ? `${prefix} ${leaf}` : leaf;
23
+ nodes.push(fullName);
24
+ const relatedTo = cmd.__relatedTo;
25
+ if (relatedTo) {
26
+ for (const decl of relatedTo) {
27
+ const edge = {
28
+ from: fullName,
29
+ to: decl.target,
30
+ ...decl.description ? { description: decl.description } : {}
31
+ };
32
+ edges.push(edge);
33
+ if (!commandNames.has(decl.target)) {
34
+ warnings.push(`Unknown relationship target "${decl.target}" from command "${fullName}"`);
35
+ }
36
+ }
37
+ }
38
+ if (cmd.commands.length > 0) {
39
+ walkCommands(cmd.commands, fullName);
40
+ }
41
+ }
42
+ }
43
+ walkCommands(program.commands);
44
+ return {
45
+ nodes,
46
+ edges,
47
+ ...warnings.length > 0 ? { warnings } : {}
48
+ };
49
+ }
50
+ function graphSuccessHints(graph, commandName, cliName) {
51
+ const hints = [];
52
+ for (const edge of graph.edges) {
53
+ if (edge.from === commandName && edge.to !== commandName) {
54
+ const prefix = cliName ? `${cliName} ` : "";
55
+ const description = edge.description ?? `Run ${edge.to}`;
56
+ hints.push({
57
+ description,
58
+ command: `${prefix}${edge.to}`
59
+ });
60
+ }
61
+ }
62
+ return hints;
63
+ }
64
+ function graphErrorHints(graph, commandName, cliName) {
65
+ const hints = [];
66
+ for (const edge of graph.edges) {
67
+ if (edge.from === commandName && edge.to !== commandName) {
68
+ const prefix = cliName ? `${cliName} ` : "";
69
+ const description = edge.description ? `Try: ${edge.description}` : `Try: ${edge.to}`;
70
+ hints.push({
71
+ description,
72
+ command: `${prefix}${edge.to}`
73
+ });
74
+ }
75
+ }
76
+ return hints;
77
+ }
78
+
79
+ export { buildActionGraph, graphSuccessHints, graphErrorHints };
@@ -0,0 +1,78 @@
1
+ import { ComposedPreset, FlagPreset, FlagPresetConfig, SchemaPreset, SchemaPresetConfig } from "./cli-x6qr7bnd.js";
2
+ /** Extracts the resolved type from a flag preset. */
3
+ type ResolvedType<T> = T extends FlagPreset<infer R> ? R : never;
4
+ /** Converts a union type into an intersection type. */
5
+ type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
6
+ /**
7
+ * Resolves the merged type from a tuple of presets.
8
+ * Falls back to Record<string, unknown> when the tuple is empty
9
+ * (since UnionToIntersection<never> produces unknown).
10
+ */
11
+ type MergedPresetResult<TPresets extends readonly FlagPreset<Record<string, unknown>>[]> = UnionToIntersection<ResolvedType<TPresets[number]>> extends Record<string, unknown> ? UnionToIntersection<ResolvedType<TPresets[number]>> : Record<string, unknown>;
12
+ /**
13
+ * Create a typed flag preset.
14
+ *
15
+ * @param config - Preset configuration with id, options, and resolver
16
+ * @returns A flag preset that can be applied to commands or composed
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const myPreset = createPreset({
21
+ * id: "output-format",
22
+ * options: [{ flags: "--format <type>", description: "Output format" }],
23
+ * resolve: (flags) => ({
24
+ * format: String(flags["format"] ?? "text"),
25
+ * }),
26
+ * });
27
+ * ```
28
+ */
29
+ declare function createPreset<TResolved extends Record<string, unknown>>(config: FlagPresetConfig<TResolved>): FlagPreset<TResolved>;
30
+ /**
31
+ * Create a schema-driven preset using a Zod schema fragment.
32
+ *
33
+ * Instead of declaring Commander options manually, the schema fragment
34
+ * is introspected to auto-derive flags (same as `.input()`). The resolve
35
+ * function maps raw Commander flags into typed values.
36
+ *
37
+ * @param config - Preset configuration with id, schema, and resolver
38
+ * @returns A schema preset that can be applied to commands via `.preset()`
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * const outputPreset = createSchemaPreset({
43
+ * id: "output-format",
44
+ * schema: z.object({
45
+ * format: z.enum(["json", "text"]).default("text").describe("Output format"),
46
+ * pretty: z.boolean().default(false).describe("Pretty print"),
47
+ * }),
48
+ * resolve: (flags) => ({
49
+ * format: String(flags["format"] ?? "text"),
50
+ * pretty: Boolean(flags["pretty"]),
51
+ * }),
52
+ * });
53
+ * ```
54
+ */
55
+ declare function createSchemaPreset<TResolved extends Record<string, unknown>>(config: SchemaPresetConfig<TResolved>): SchemaPreset<TResolved>;
56
+ /**
57
+ * Type guard to check whether a preset is a schema-driven preset.
58
+ */
59
+ declare function isSchemaPreset(preset: FlagPreset<Record<string, unknown>> | SchemaPreset<Record<string, unknown>>): preset is SchemaPreset<Record<string, unknown>>;
60
+ /**
61
+ * Compose multiple presets into one, deduplicating by id (first wins).
62
+ *
63
+ * @param presets - Presets to compose together
64
+ * @returns A single preset merging all options and resolvers
65
+ *
66
+ * @example
67
+ * ```typescript
68
+ * const composed = composePresets(
69
+ * verbosePreset(),
70
+ * cwdPreset(),
71
+ * forcePreset(),
72
+ * );
73
+ * // composed.resolve({ verbose: true, cwd: "/tmp", force: false })
74
+ * // => { verbose: true, cwd: "/tmp", force: false }
75
+ * ```
76
+ */
77
+ declare function composePresets<TPresets extends readonly FlagPreset<Record<string, unknown>>[]>(...presets: TPresets): ComposedPreset<MergedPresetResult<TPresets>>;
78
+ export { createPreset, createSchemaPreset, isSchemaPreset, composePresets };
@@ -0,0 +1,15 @@
1
+ import { SchemaCommandOptions } from "./cli-wjv7g1aq.js";
2
+ import { ActionSource } from "@outfitter/schema";
3
+ import { Command } from "commander";
4
+ /**
5
+ * Create a `schema` command for CLI introspection.
6
+ *
7
+ * When `options.surface` is provided, adds `generate` and `diff` subcommands
8
+ * for surface map file I/O and drift detection.
9
+ *
10
+ * @param source - ActionRegistry or array of ActionSpec
11
+ * @param options - Command configuration
12
+ * @returns A Commander command instance
13
+ */
14
+ declare function createSchemaCommand(source: ActionSource, options?: SchemaCommandOptions): Command;
15
+ export { createSchemaCommand };
@@ -0,0 +1,94 @@
1
+ import { CommandEnvelope, ErrorEnvelope, SuccessEnvelope } from "./cli-30mt7c5w.js";
2
+ import { CLIHint, ErrorCategory } from "@outfitter/contracts";
3
+ /**
4
+ * Create a success envelope wrapping a command result.
5
+ *
6
+ * The `hints` field is omitted when hints is undefined, null, or empty.
7
+ *
8
+ * @param command - Command name
9
+ * @param result - Handler result value
10
+ * @param hints - Optional CLI hints for next actions
11
+ * @returns A success envelope
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * const envelope = createSuccessEnvelope("deploy", { status: "deployed" }, [
16
+ * { description: "Check status", command: "deploy status" },
17
+ * ]);
18
+ * ```
19
+ */
20
+ declare function createSuccessEnvelope<T>(command: string, result: T, hints?: CLIHint[]): SuccessEnvelope<T>;
21
+ /**
22
+ * Create an error envelope wrapping a command failure.
23
+ *
24
+ * The `hints` field is omitted when hints is undefined, null, or empty.
25
+ * The `retryable` field is derived from the error category metadata.
26
+ * The `retry_after` field is included only when `retryAfterSeconds` is provided
27
+ * (typically from a `RateLimitError`).
28
+ *
29
+ * @param command - Command name
30
+ * @param category - Error category from the taxonomy
31
+ * @param message - Human-readable error message
32
+ * @param hints - Optional CLI hints for error recovery
33
+ * @param retryAfterSeconds - Optional retry delay in seconds (from RateLimitError)
34
+ * @returns An error envelope
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * const envelope = createErrorEnvelope("deploy", "validation", "Missing env", [
39
+ * { description: "Specify env", command: "deploy --env prod" },
40
+ * ]);
41
+ * // envelope.error.retryable === false
42
+ *
43
+ * const rateLimitEnvelope = createErrorEnvelope(
44
+ * "fetch",
45
+ * "rate_limit",
46
+ * "Too many requests",
47
+ * undefined,
48
+ * 60
49
+ * );
50
+ * // rateLimitEnvelope.error.retryable === true, retry_after === 60
51
+ * ```
52
+ */
53
+ declare function createErrorEnvelope(command: string, category: ErrorCategory, message: string, hints?: CLIHint[], retryAfterSeconds?: number): ErrorEnvelope;
54
+ /**
55
+ * Build a CLIHint for executing the current command without --dry-run.
56
+ *
57
+ * Strips the --dry-run flag and its variants (--dry-run=true, --dry-run=false, etc.),
58
+ * producing a hint like: `delete --id abc --force` (without --dry-run).
59
+ *
60
+ * @param argv - Parsed argv to strip --dry-run from. Defaults to `process.argv.slice(2)`.
61
+ */
62
+ declare function buildDryRunHint(argv?: readonly string[]): CLIHint | undefined;
63
+ /**
64
+ * Extract error category from an error object.
65
+ * Works with OutfitterError instances and duck-typed errors.
66
+ */
67
+ declare function extractCategory(error: unknown): ErrorCategory;
68
+ /**
69
+ * Extract error message from an error object.
70
+ */
71
+ declare function extractMessage(error: unknown): string;
72
+ /**
73
+ * Extract retryAfterSeconds from an error object (if present).
74
+ * Works with RateLimitError instances and duck-typed errors.
75
+ */
76
+ declare function extractRetryAfterSeconds(error: unknown): number | undefined;
77
+ /**
78
+ * Get exit code for an error category.
79
+ */
80
+ declare function getExitCode(category: ErrorCategory): number;
81
+ /**
82
+ * Format an envelope for human-readable output.
83
+ * Returns stdout and stderr portions separately.
84
+ */
85
+ declare function formatEnvelopeHuman(envelope: CommandEnvelope): {
86
+ stdout: string;
87
+ stderr: string;
88
+ };
89
+ /**
90
+ * Safely call a hint function, returning undefined if it throws.
91
+ * Hint functions should never cause the command to fail.
92
+ */
93
+ declare function safeCallHintFn(fn: () => CLIHint[]): CLIHint[] | undefined;
94
+ export { createSuccessEnvelope, createErrorEnvelope, buildDryRunHint, extractCategory, extractMessage, extractRetryAfterSeconds, getExitCode, formatEnvelopeHuman, safeCallHintFn };
@@ -0,0 +1,60 @@
1
+ import { ActionGraph } from "./cli-16wg5mka.js";
2
+ import { CLIHint } from "@outfitter/contracts";
3
+ import { Command } from "commander";
4
+ /**
5
+ * Build an action graph from a Commander program's registered commands.
6
+ *
7
+ * Walks all commands, collecting `.relatedTo()` declarations as edges.
8
+ * Unknown targets produce warnings (not crashes). Self-links and cycles
9
+ * are preserved in the graph — callers handle them during traversal.
10
+ *
11
+ * @param program - The Commander program (root command)
12
+ * @returns An action graph with nodes, edges, and optional warnings
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const graph = buildActionGraph(program);
17
+ * // graph.nodes: ["deploy", "status", "rollback"]
18
+ * // graph.edges: [{ from: "deploy", to: "status", description: "Check status" }]
19
+ * ```
20
+ */
21
+ declare function buildActionGraph(program: Command): ActionGraph;
22
+ /**
23
+ * Generate tier-4 success hints from action graph neighbors (Tier 4).
24
+ *
25
+ * For a given command, returns CLIHint[] for all outgoing edges in the
26
+ * action graph. Self-links are excluded (they would be confusing as
27
+ * "next action" suggestions).
28
+ *
29
+ * @param graph - The action graph
30
+ * @param commandName - The command that just succeeded
31
+ * @param cliName - CLI name for hint command prefix
32
+ * @returns Array of CLI hints for related next-actions
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * const hints = graphSuccessHints(graph, "deploy", "my-cli");
37
+ * // [{ description: "Check deployment status", command: "my-cli status" }]
38
+ * ```
39
+ */
40
+ declare function graphSuccessHints(graph: ActionGraph, commandName: string, cliName?: string): CLIHint[];
41
+ /**
42
+ * Generate tier-4 error hints from action graph neighbors (Tier 4).
43
+ *
44
+ * For a given command that failed, returns CLIHint[] for related
45
+ * remediation paths. Self-links are excluded. Error hints use the
46
+ * relationship description as remediation context.
47
+ *
48
+ * @param graph - The action graph
49
+ * @param commandName - The command that failed
50
+ * @param cliName - CLI name for hint command prefix
51
+ * @returns Array of CLI hints for remediation paths
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * const hints = graphErrorHints(graph, "deploy", "my-cli");
56
+ * // [{ description: "Try: Rollback deployment", command: "my-cli rollback" }]
57
+ * ```
58
+ */
59
+ declare function graphErrorHints(graph: ActionGraph, commandName: string, cliName?: string): CLIHint[];
60
+ export { buildActionGraph, graphSuccessHints, graphErrorHints };
@@ -1,34 +1,40 @@
1
- import { OutputOptions } from "./cli-98aa9104.js";
1
+ import { OutputMode, OutputOptions } from "./cli-x6qr7bnd.js";
2
2
  /**
3
3
  * Output data to the console with automatic mode selection.
4
4
  *
5
5
  * Respects --json, --jsonl, --tree, --table flags automatically.
6
6
  * Defaults to human-friendly output when no flags are present.
7
7
  *
8
+ * Detection hierarchy (highest wins):
9
+ * 1. Explicit `format` parameter
10
+ * 2. Environment variables (`OUTFITTER_JSON`, `OUTFITTER_JSONL`)
11
+ * 3. Default: `"human"`
12
+ *
8
13
  * @param data - The data to output
14
+ * @param format - Explicit output format (e.g. from a resolved CLI flag)
9
15
  * @param options - Output configuration options
10
16
  *
11
17
  * @example
12
18
  * ```typescript
13
19
  * import { output } from "@outfitter/cli";
14
20
  *
15
- * // Basic usage - mode auto-detected from flags
21
+ * // Basic usage - mode auto-detected from env
16
22
  * output(results);
17
23
  *
18
- * // Force JSON mode
19
- * output(results, { mode: "json" });
24
+ * // Explicit format from resolved flag
25
+ * output(results, "json");
20
26
  *
21
27
  * // Pretty-print JSON
22
- * output(results, { mode: "json", pretty: true });
28
+ * output(results, "json", { pretty: true });
23
29
  *
24
30
  * // Output to stderr
25
- * output(errors, { stream: process.stderr });
31
+ * output(errors, undefined, { stream: process.stderr });
26
32
  *
27
33
  * // Await for large outputs (recommended)
28
- * await output(largeDataset, { mode: "jsonl" });
34
+ * await output(largeDataset, "jsonl");
29
35
  * ```
30
36
  */
31
- declare function output(data: unknown, options?: OutputOptions): Promise<void>;
37
+ declare function output(data: unknown, format?: OutputMode, options?: OutputOptions): Promise<void>;
32
38
  /**
33
39
  * Exit the process with an error message.
34
40
  *
@@ -36,6 +42,7 @@ declare function output(data: unknown, options?: OutputOptions): Promise<void>;
36
42
  * and exits with an appropriate exit code.
37
43
  *
38
44
  * @param error - The error to display
45
+ * @param format - Explicit output format (e.g. from a resolved CLI flag)
39
46
  * @returns Never returns (exits the process)
40
47
  *
41
48
  * @example
@@ -49,7 +56,7 @@ declare function output(data: unknown, options?: OutputOptions): Promise<void>;
49
56
  * }
50
57
  * ```
51
58
  */
52
- declare function exitWithError(error: Error, options?: OutputOptions): never;
59
+ declare function exitWithError(error: Error, format?: OutputMode): never;
53
60
  /**
54
61
  * Resolve verbose mode from environment configuration.
55
62
  *
@@ -69,9 +76,9 @@ declare function exitWithError(error: Error, options?: OutputOptions): never;
69
76
  * // Auto-resolve from environment
70
77
  * const isVerbose = resolveVerbose();
71
78
  *
72
- * // With OUTFITTER_ENV=development true
73
- * // With OUTFITTER_VERBOSE=0 false (overrides everything)
74
- * // With nothing set false
79
+ * // With OUTFITTER_ENV=development -> true
80
+ * // With OUTFITTER_VERBOSE=0 -> false (overrides everything)
81
+ * // With nothing set -> false
75
82
  *
76
83
  * // From CLI flag
77
84
  * const isVerbose = resolveVerbose(cliFlags.verbose);
@@ -0,0 +1,11 @@
1
+ import { ActionManifest } from "@outfitter/schema";
2
+ /**
3
+ * Format a manifest for human-readable terminal output.
4
+ *
5
+ * @param manifest - The manifest to format
6
+ * @param programName - CLI program name (for header)
7
+ * @param actionId - If provided, show detail for this single action
8
+ * @returns Formatted string
9
+ */
10
+ declare function formatManifestHuman(manifest: ActionManifest, programName?: string, actionId?: string): string;
11
+ export { formatManifestHuman };
@@ -0,0 +1,118 @@
1
+ // @bun
2
+ import {
3
+ formatHuman
4
+ } from "./cli-dg0cz7rw.js";
5
+
6
+ // packages/cli/src/internal/envelope-helpers.ts
7
+ import { errorCategoryMeta, exitCodeMap } from "@outfitter/contracts";
8
+ function createSuccessEnvelope(command, result, hints) {
9
+ const envelope = {
10
+ ok: true,
11
+ command,
12
+ result
13
+ };
14
+ if (hints && hints.length > 0) {
15
+ return { ...envelope, hints };
16
+ }
17
+ return envelope;
18
+ }
19
+ function createErrorEnvelope(command, category, message, hints, retryAfterSeconds) {
20
+ const meta = errorCategoryMeta(category);
21
+ const includeRetryAfter = retryAfterSeconds != null && category === "rate_limit";
22
+ const errorField = includeRetryAfter ? {
23
+ category,
24
+ message,
25
+ retryable: meta.retryable,
26
+ retry_after: retryAfterSeconds
27
+ } : { category, message, retryable: meta.retryable };
28
+ const envelope = {
29
+ ok: false,
30
+ command,
31
+ error: errorField
32
+ };
33
+ if (hints && hints.length > 0) {
34
+ return { ...envelope, hints };
35
+ }
36
+ return envelope;
37
+ }
38
+ function buildDryRunHint(argv = process.argv.slice(2)) {
39
+ const filteredArgs = argv.filter((arg) => arg !== "--dry-run" && !arg.startsWith("--dry-run="));
40
+ const command = filteredArgs.join(" ");
41
+ if (!command)
42
+ return;
43
+ return {
44
+ description: "Execute without dry-run",
45
+ command
46
+ };
47
+ }
48
+ var DEFAULT_CATEGORY = "internal";
49
+ var DEFAULT_EXIT_CODE = 1;
50
+ function extractCategory(error) {
51
+ if (error !== null && typeof error === "object" && "category" in error && typeof error["category"] === "string") {
52
+ const cat = error["category"];
53
+ if (cat in exitCodeMap) {
54
+ return cat;
55
+ }
56
+ }
57
+ return DEFAULT_CATEGORY;
58
+ }
59
+ function extractMessage(error) {
60
+ if (error instanceof Error) {
61
+ return error.message;
62
+ }
63
+ return String(error);
64
+ }
65
+ function extractRetryAfterSeconds(error) {
66
+ if (error !== null && typeof error === "object" && "retryAfterSeconds" in error && typeof error["retryAfterSeconds"] === "number") {
67
+ return error["retryAfterSeconds"];
68
+ }
69
+ return;
70
+ }
71
+ function getExitCode(category) {
72
+ return exitCodeMap[category] ?? DEFAULT_EXIT_CODE;
73
+ }
74
+ function formatEnvelopeHuman(envelope) {
75
+ if (envelope.ok) {
76
+ const parts2 = [];
77
+ const formatted = formatHuman(envelope.result);
78
+ if (formatted) {
79
+ parts2.push(formatted);
80
+ }
81
+ if (envelope.hints && envelope.hints.length > 0) {
82
+ parts2.push("");
83
+ parts2.push("Hints:");
84
+ for (const hint of envelope.hints) {
85
+ parts2.push(` ${hint.description}`);
86
+ if (hint.command) {
87
+ parts2.push(` $ ${hint.command}`);
88
+ }
89
+ }
90
+ }
91
+ return { stdout: parts2.join(`
92
+ `), stderr: "" };
93
+ }
94
+ const parts = [];
95
+ parts.push(`Error: ${envelope.error.message}`);
96
+ if (envelope.hints && envelope.hints.length > 0) {
97
+ parts.push("");
98
+ parts.push("Hints:");
99
+ for (const hint of envelope.hints) {
100
+ parts.push(` ${hint.description}`);
101
+ if (hint.command) {
102
+ parts.push(` $ ${hint.command}`);
103
+ }
104
+ }
105
+ }
106
+ return { stdout: "", stderr: parts.join(`
107
+ `) };
108
+ }
109
+ function safeCallHintFn(fn) {
110
+ try {
111
+ const hints = fn();
112
+ return hints.length > 0 ? hints : undefined;
113
+ } catch {
114
+ return;
115
+ }
116
+ }
117
+
118
+ export { createSuccessEnvelope, createErrorEnvelope, buildDryRunHint, extractCategory, extractMessage, extractRetryAfterSeconds, getExitCode, formatEnvelopeHuman, safeCallHintFn };
@@ -0,0 +1,32 @@
1
+ // @bun
2
+ // packages/cli/src/internal/input-security.ts
3
+ import path from "path";
4
+ function isSecurePath(filePath, allowAbsolute = false) {
5
+ if (filePath.includes("\x00")) {
6
+ return false;
7
+ }
8
+ if (filePath.includes("..")) {
9
+ return false;
10
+ }
11
+ const normalized = path.normalize(filePath);
12
+ if (!allowAbsolute && path.isAbsolute(normalized)) {
13
+ return false;
14
+ }
15
+ return true;
16
+ }
17
+ function isSecureGlobPattern(pattern) {
18
+ if (pattern.startsWith("..")) {
19
+ return false;
20
+ }
21
+ if (pattern.includes("/../")) {
22
+ return false;
23
+ }
24
+ return true;
25
+ }
26
+ function isWithinWorkspace(resolvedPath, workspaceRoot) {
27
+ const normalizedPath = path.normalize(resolvedPath);
28
+ const normalizedRoot = path.normalize(workspaceRoot);
29
+ return normalizedPath === normalizedRoot || normalizedPath.startsWith(normalizedRoot + path.sep);
30
+ }
31
+
32
+ export { isSecurePath, isSecureGlobPattern, isWithinWorkspace };
@@ -0,0 +1,23 @@
1
+ import { CLIHint, ErrorCategory } from "@outfitter/contracts";
2
+ /**
3
+ * Produce standard recovery CLIHints for an error category (Tier 2).
4
+ *
5
+ * Uses the enriched ErrorCategory metadata (retryable flags from OS-347)
6
+ * to generate appropriate recovery actions. Retryable categories include
7
+ * retry hints; non-retryable categories guide toward root cause resolution.
8
+ *
9
+ * @param category - The error category
10
+ * @param cliName - Optional CLI name for command hints
11
+ * @param commandName - Optional command name to replace `<previous-command>` placeholder
12
+ * @returns Array of recovery hints
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const hints = errorRecoveryHints("conflict", "my-cli", "update");
17
+ * // [{ description: "Resolve the conflict and retry",
18
+ * // command: "my-cli update --force",
19
+ * // params: { retryable: true } }]
20
+ * ```
21
+ */
22
+ declare function errorRecoveryHints(category: ErrorCategory, cliName?: string, commandName?: string): CLIHint[];
23
+ export { errorRecoveryHints };