@outfitter/cli 0.5.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/README.md +179 -60
  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/colors/index.js +1 -17
  7. package/dist/command.d.ts +3 -43
  8. package/dist/command.js +241 -13
  9. package/dist/envelope.d.ts +5 -0
  10. package/dist/envelope.js +160 -0
  11. package/dist/flags.d.ts +5 -189
  12. package/dist/flags.js +5 -1
  13. package/dist/hints.d.ts +34 -0
  14. package/dist/hints.js +26 -0
  15. package/dist/index.d.ts +3 -2
  16. package/dist/index.js +2 -17
  17. package/dist/input.d.ts +3 -124
  18. package/dist/input.js +14 -359
  19. package/dist/internal/envelope-helpers.d.ts +4 -0
  20. package/dist/internal/envelope-helpers.js +24 -0
  21. package/dist/internal/envelope-types.d.ts +3 -0
  22. package/dist/internal/flag-builders.d.ts +3 -0
  23. package/dist/internal/flag-builders.js +155 -0
  24. package/dist/internal/flag-types.d.ts +3 -0
  25. package/dist/internal/flag-types.js +13 -0
  26. package/dist/internal/hint-action-graph.d.ts +5 -0
  27. package/dist/internal/hint-action-graph.js +11 -0
  28. package/dist/internal/hint-command-tree.d.ts +5 -0
  29. package/dist/internal/hint-command-tree.js +9 -0
  30. package/dist/internal/hint-error-recovery.d.ts +2 -0
  31. package/dist/internal/hint-error-recovery.js +7 -0
  32. package/dist/internal/hint-types.d.ts +4 -0
  33. package/dist/internal/hint-types.js +1 -0
  34. package/dist/internal/input-helpers.d.ts +18 -0
  35. package/dist/internal/input-helpers.js +11 -0
  36. package/dist/internal/input-normalization.d.ts +3 -0
  37. package/dist/internal/input-normalization.js +9 -0
  38. package/dist/internal/input-parsers.d.ts +3 -0
  39. package/dist/internal/input-parsers.js +19 -0
  40. package/dist/internal/input-security.d.ts +22 -0
  41. package/dist/internal/input-security.js +11 -0
  42. package/dist/internal/output-formatting.d.ts +3 -0
  43. package/dist/internal/output-formatting.js +21 -0
  44. package/dist/internal/presets.d.ts +3 -0
  45. package/dist/{shared/@outfitter/cli-pdb7znbq.js → internal/presets.js} +49 -223
  46. package/dist/internal/schema-commands.d.ts +3 -0
  47. package/dist/{shared/@outfitter/cli-0cjts94k.js → internal/schema-commands.js} +12 -100
  48. package/dist/internal/schema-formatting.d.ts +2 -0
  49. package/dist/internal/schema-formatting.js +7 -0
  50. package/dist/internal/schema-types.d.ts +2 -0
  51. package/dist/internal/schema-types.js +1 -0
  52. package/dist/output.d.ts +4 -3
  53. package/dist/output.js +10 -2
  54. package/dist/pagination.d.ts +1 -1
  55. package/dist/query.d.ts +84 -2
  56. package/dist/query.js +8 -45
  57. package/dist/schema-input.d.ts +80 -0
  58. package/dist/schema-input.js +15 -0
  59. package/dist/schema.d.ts +4 -1
  60. package/dist/schema.js +1 -1
  61. package/dist/shared/@outfitter/cli-10wxfc78.d.ts +45 -0
  62. package/dist/shared/@outfitter/cli-16wg5mka.d.ts +71 -0
  63. package/dist/shared/@outfitter/cli-1q5redaj.js +267 -0
  64. package/dist/shared/@outfitter/cli-2dfxs239.js +98 -0
  65. package/dist/shared/@outfitter/cli-30mt7c5w.d.ts +112 -0
  66. package/dist/shared/@outfitter/cli-3jta1h1h.js +134 -0
  67. package/dist/shared/@outfitter/cli-4h85mpth.js +76 -0
  68. package/dist/shared/@outfitter/cli-6shkwxdc.js +28 -0
  69. package/dist/shared/@outfitter/cli-89335n9a.js +16 -0
  70. package/dist/shared/@outfitter/cli-8999qjdd.js +3 -0
  71. package/dist/shared/@outfitter/cli-8cfxdady.js +60 -0
  72. package/dist/shared/@outfitter/cli-bcajqy33.d.ts +25 -0
  73. package/dist/shared/@outfitter/cli-c09332vm.d.ts +39 -0
  74. package/dist/shared/@outfitter/cli-cgha038c.d.ts +3 -0
  75. package/dist/shared/@outfitter/{cli-zahqsaby.js → cli-d40m2x1d.js} +19 -3
  76. package/dist/shared/@outfitter/{cli-7wp5nj0s.js → cli-dg0cz7rw.js} +39 -81
  77. package/dist/shared/@outfitter/cli-dv8kk4jw.d.ts +24 -0
  78. package/dist/shared/@outfitter/cli-g43887b7.js +20 -0
  79. package/dist/shared/@outfitter/cli-gqtkhgw4.js +52 -0
  80. package/dist/shared/@outfitter/cli-h4ejpmjs.d.ts +104 -0
  81. package/dist/shared/@outfitter/cli-htzez8v2.js +70 -0
  82. package/dist/shared/@outfitter/cli-hvg2m5gf.js +79 -0
  83. package/dist/shared/@outfitter/cli-n54zs151.d.ts +78 -0
  84. package/dist/shared/@outfitter/cli-nbpgw7z7.d.ts +15 -0
  85. package/dist/shared/@outfitter/cli-nkt399zf.d.ts +94 -0
  86. package/dist/shared/@outfitter/cli-pmd04gtv.d.ts +60 -0
  87. package/dist/shared/@outfitter/{cli-xy3gs50c.d.ts → cli-q6csxmeh.d.ts} +19 -12
  88. package/dist/shared/@outfitter/cli-qcskd96y.d.ts +11 -0
  89. package/dist/shared/@outfitter/cli-ry7btmy4.js +118 -0
  90. package/dist/shared/@outfitter/cli-sy99pjyj.js +32 -0
  91. package/dist/shared/@outfitter/cli-tm2fzngs.d.ts +23 -0
  92. package/dist/shared/@outfitter/cli-vvvhjwks.js +106 -0
  93. package/dist/shared/@outfitter/cli-wjv7g1aq.d.ts +16 -0
  94. package/dist/shared/@outfitter/{cli-98aa9104.d.ts → cli-x6qr7bnd.d.ts} +338 -16
  95. package/dist/shared/@outfitter/cli-xde45xcc.d.ts +53 -0
  96. package/dist/shared/@outfitter/cli-xw8ys1je.d.ts +123 -0
  97. package/dist/shared/@outfitter/cli-yfewnyc2.d.ts +43 -0
  98. package/dist/shared/@outfitter/cli-zkzj0q4q.js +99 -0
  99. package/dist/shared/@outfitter/cli-zv3ah6f0.js +3 -0
  100. package/dist/streaming.d.ts +47 -0
  101. package/dist/streaming.js +13 -0
  102. package/dist/terminal/index.js +1 -19
  103. package/dist/truncation.d.ts +104 -0
  104. package/dist/truncation.js +111 -0
  105. package/dist/types.d.ts +2 -2
  106. package/dist/types.js +0 -5
  107. package/dist/verbs.d.ts +1 -1
  108. package/package.json +66 -36
  109. package/dist/shared/@outfitter/cli-n1k0d23k.d.ts +0 -33
  110. /package/dist/{shared/@outfitter/cli-zw75pdk8.js → internal/envelope-types.js} +0 -0
@@ -0,0 +1,123 @@
1
+ import { ColorFlags, ExecutionFlags, ExecutionPresetConfig, FlagPreset, InteractionFlags, PaginationFlags, PaginationPresetConfig, ProjectionFlags, StrictFlags, TimeWindowFlags, TimeWindowPresetConfig } from "./cli-x6qr7bnd.js";
2
+ /**
3
+ * Verbose output flag preset.
4
+ *
5
+ * Adds: `-v, --verbose`
6
+ * Resolves: `{ verbose: boolean }`
7
+ */
8
+ declare function verbosePreset(): FlagPreset<{
9
+ verbose: boolean;
10
+ }>;
11
+ /**
12
+ * Working directory flag preset.
13
+ *
14
+ * Adds: `--cwd <path>`
15
+ * Resolves: `{ cwd: string }` (defaults to `process.cwd()`)
16
+ */
17
+ declare function cwdPreset(): FlagPreset<{
18
+ cwd: string;
19
+ }>;
20
+ /**
21
+ * Dry-run flag preset.
22
+ *
23
+ * Adds: `--dry-run`
24
+ * Resolves: `{ dryRun: boolean }`
25
+ */
26
+ declare function dryRunPreset(): FlagPreset<{
27
+ dryRun: boolean;
28
+ }>;
29
+ /**
30
+ * Force flag preset.
31
+ *
32
+ * Adds: `-f, --force`
33
+ * Resolves: `{ force: boolean }`
34
+ */
35
+ declare function forcePreset(): FlagPreset<{
36
+ force: boolean;
37
+ }>;
38
+ /**
39
+ * Interaction mode flag preset.
40
+ *
41
+ * Adds: `--non-interactive`, `--no-input`, `-y, --yes`
42
+ * Resolves: `{ interactive: boolean, yes: boolean }`
43
+ *
44
+ * `interactive` defaults to `true` and is set to `false` when
45
+ * `--non-interactive` or `--no-input` is passed. The two flags
46
+ * are synonyms for user convenience.
47
+ */
48
+ declare function interactionPreset(): FlagPreset<InteractionFlags>;
49
+ /**
50
+ * Strict mode flag preset.
51
+ *
52
+ * Adds: `--strict`
53
+ * Resolves: `{ strict: boolean }`
54
+ */
55
+ declare function strictPreset(): FlagPreset<StrictFlags>;
56
+ /**
57
+ * Color mode flag preset.
58
+ *
59
+ * Adds: `--color [mode]`, `--no-color`
60
+ * Resolves: `{ color: "auto" | "always" | "never" }`
61
+ *
62
+ * Commander's `--no-color` negation sets `flags["color"]` to `false`,
63
+ * which resolves to `"never"`. Invalid values default to `"auto"`.
64
+ *
65
+ * This preset resolves the user's *intent*. Consumers should compose
66
+ * with terminal detection (`supportsColor()`, `resolveColorEnv()`)
67
+ * to determine actual color output behavior.
68
+ */
69
+ declare function colorPreset(): FlagPreset<ColorFlags>;
70
+ /**
71
+ * Projection flag preset.
72
+ *
73
+ * Adds: `--fields <fields>`, `--exclude-fields <fields>`, `--count`
74
+ * Resolves: `{ fields: string[] | undefined, excludeFields: string[] | undefined, count: boolean }`
75
+ *
76
+ * Fields are parsed as comma-separated strings with whitespace trimming.
77
+ * `undefined` when not provided (not empty array) to distinguish
78
+ * "not specified" from "empty".
79
+ *
80
+ * Conflict between `--fields` and `--exclude-fields` is a handler
81
+ * concern (documented, not enforced).
82
+ */
83
+ declare function projectionPreset(): FlagPreset<ProjectionFlags>;
84
+ /**
85
+ * Time-window flag preset.
86
+ *
87
+ * Adds: `--since <date>`, `--until <date>`
88
+ * Resolves: `{ since: Date | undefined, until: Date | undefined }`
89
+ *
90
+ * Accepts ISO 8601 dates (`2024-01-15`) or relative durations
91
+ * (`7d`, `24h`, `30m`, `2w`). Durations are past-relative
92
+ * (subtracted from now).
93
+ *
94
+ * Returns `undefined` for unparseable values (does not throw).
95
+ * When `config.maxRange` is set and both bounds are provided,
96
+ * ranges above the limit are treated as invalid.
97
+ */
98
+ declare function timeWindowPreset(config?: TimeWindowPresetConfig): FlagPreset<TimeWindowFlags>;
99
+ /**
100
+ * Execution flag preset.
101
+ *
102
+ * Adds: `--timeout <ms>`, `--retries <n>`, `--offline`
103
+ * Resolves: `{ timeout: number | undefined, retries: number, offline: boolean }`
104
+ *
105
+ * Timeout is parsed as a positive integer in milliseconds.
106
+ * Retries are parsed as a non-negative integer, clamped to maxRetries.
107
+ */
108
+ declare function executionPreset(config?: ExecutionPresetConfig): FlagPreset<ExecutionFlags>;
109
+ /**
110
+ * Pagination flag preset.
111
+ *
112
+ * Adds: `-l, --limit <n>`, `--next`, `--reset`
113
+ * Resolves: `{ limit: number, next: boolean, reset: boolean }`
114
+ *
115
+ * Limit is parsed as an integer, clamped to maxLimit, and defaults
116
+ * to defaultLimit on invalid input. Mutual exclusivity of --next
117
+ * and --reset is a handler concern (not enforced here).
118
+ *
119
+ * Integrates with loadCursor/saveCursor/clearCursor from
120
+ * `@outfitter/cli/pagination`.
121
+ */
122
+ declare function paginationPreset(config?: PaginationPresetConfig): FlagPreset<PaginationFlags>;
123
+ export { verbosePreset, cwdPreset, dryRunPreset, forcePreset, interactionPreset, strictPreset, colorPreset, projectionPreset, timeWindowPreset, executionPreset, paginationPreset };
@@ -0,0 +1,43 @@
1
+ import { CommandTree } from "./cli-16wg5mka.js";
2
+ import { CLIHint } from "@outfitter/contracts";
3
+ import { Command } from "commander";
4
+ /**
5
+ * Build a structured command tree from a Commander program instance.
6
+ *
7
+ * Recursively walks the program's registered commands, extracting
8
+ * names, descriptions, options, and nested subcommands.
9
+ *
10
+ * @param program - The Commander program (root command)
11
+ * @returns A structured command tree
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { Command } from "commander";
16
+ * import { buildCommandTree } from "@outfitter/cli/hints";
17
+ *
18
+ * const program = new Command("my-cli").version("1.0.0");
19
+ * program.command("list").description("List items");
20
+ *
21
+ * const tree = buildCommandTree(program);
22
+ * // { name: "my-cli", version: "1.0.0", commands: [{ name: "list", description: "List items" }] }
23
+ * ```
24
+ */
25
+ declare function buildCommandTree(program: Command): CommandTree;
26
+ /**
27
+ * Auto-generate CLIHint[] from a command tree (Tier 1).
28
+ *
29
+ * Produces one hint per registered command, including nested subcommands
30
+ * with their full command path.
31
+ *
32
+ * @param tree - The command tree from buildCommandTree()
33
+ * @returns Array of CLI hints for available commands
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * const tree = buildCommandTree(program);
38
+ * const hints = commandTreeHints(tree);
39
+ * // [{ description: "List items", command: "my-cli list" }, ...]
40
+ * ```
41
+ */
42
+ declare function commandTreeHints(tree: CommandTree): CLIHint[];
43
+ export { buildCommandTree, commandTreeHints };
@@ -0,0 +1,99 @@
1
+ // @bun
2
+ // packages/cli/src/internal/schema-formatting.ts
3
+ function formatManifestHuman(manifest, programName, actionId) {
4
+ if (actionId) {
5
+ return formatActionDetail(manifest, actionId);
6
+ }
7
+ return formatSummary(manifest, programName);
8
+ }
9
+ function formatSummary(manifest, programName) {
10
+ const lines = [];
11
+ const name = programName ?? "cli";
12
+ const actionCount = manifest.actions.length;
13
+ const surfaceCount = manifest.surfaces.length;
14
+ const surfaceLabel = surfaceCount === 1 ? `${surfaceCount} surface` : `${surfaceCount} surfaces`;
15
+ lines.push(`${name} \u2014 ${actionCount} actions across ${surfaceLabel}`);
16
+ lines.push("");
17
+ const grouped = new Map;
18
+ const ungrouped = [];
19
+ for (const action of manifest.actions) {
20
+ const group = action.cli?.group;
21
+ if (group) {
22
+ const existing = grouped.get(group) ?? [];
23
+ existing.push(action);
24
+ grouped.set(group, existing);
25
+ } else {
26
+ ungrouped.push(action);
27
+ }
28
+ }
29
+ for (const [groupName, groupActions] of grouped.entries()) {
30
+ lines.push(groupName);
31
+ for (const action of groupActions) {
32
+ const commandPart = action.cli?.command ?? "";
33
+ const isBase = !commandPart || commandPart.startsWith("[") || commandPart.startsWith("<");
34
+ const displayCommand = isBase ? ` ${groupName} ${commandPart}`.trimEnd() : ` ${groupName} ${commandPart}`;
35
+ const desc = action.cli?.description ?? action.description ?? "";
36
+ lines.push(padCommand(displayCommand, desc));
37
+ }
38
+ lines.push("");
39
+ }
40
+ for (const action of ungrouped) {
41
+ const commandPart = action.cli?.command ?? action.id;
42
+ const desc = action.cli?.description ?? action.description ?? "";
43
+ lines.push(padCommand(`${commandPart}`, desc));
44
+ }
45
+ lines.push("");
46
+ lines.push("Use --output json for machine-readable format.");
47
+ lines.push("Use --surface <name> to filter (cli, mcp, api, server).");
48
+ return lines.join(`
49
+ `);
50
+ }
51
+ function padCommand(command, description) {
52
+ const padding = Math.max(1, 32 - command.length);
53
+ return `${command}${" ".repeat(padding)}${description}`;
54
+ }
55
+ function formatActionDetail(manifest, actionId) {
56
+ const entry = manifest.actions.find((a) => a.id === actionId);
57
+ if (!entry) {
58
+ return `Unknown action: ${actionId}`;
59
+ }
60
+ const lines = [];
61
+ const desc = entry.cli?.description ?? entry.description ?? "";
62
+ lines.push(`${entry.id} \u2014 ${desc}`);
63
+ lines.push("");
64
+ if (entry.cli) {
65
+ const group = entry.cli.group;
66
+ const commandPart = entry.cli.command ?? (group ? "" : entry.id);
67
+ const fullCommand = group ? `${group} ${commandPart}`.trimEnd() : commandPart;
68
+ lines.push(` Command: ${fullCommand}`);
69
+ }
70
+ lines.push(` Surfaces: ${entry.surfaces.join(", ")}`);
71
+ if (entry.cli?.group) {
72
+ lines.push(` Group: ${entry.cli.group}`);
73
+ }
74
+ if (entry.cli?.aliases && entry.cli.aliases.length > 0) {
75
+ lines.push(` Aliases: ${entry.cli.aliases.join(", ")}`);
76
+ }
77
+ if (entry.cli?.options && entry.cli.options.length > 0) {
78
+ lines.push("");
79
+ lines.push(" Options:");
80
+ for (const opt of entry.cli.options) {
81
+ const defaultStr = opt.defaultValue !== undefined ? ` [${String(opt.defaultValue)}]` : "";
82
+ lines.push(padCommand(` ${opt.flags}`, `${opt.description}${defaultStr}`));
83
+ }
84
+ }
85
+ if (entry.mcp) {
86
+ lines.push("");
87
+ lines.push(" MCP:");
88
+ if (entry.mcp.tool) {
89
+ lines.push(` Tool: ${entry.mcp.tool}`);
90
+ }
91
+ if (entry.mcp.description) {
92
+ lines.push(` Description: ${entry.mcp.description}`);
93
+ }
94
+ }
95
+ return lines.join(`
96
+ `);
97
+ }
98
+
99
+ export { formatManifestHuman };
@@ -0,0 +1,3 @@
1
+ export { generateManifest } from "@outfitter/schema";
2
+ export { formatManifestHuman } from "../../internal/schema-formatting.js";
3
+ export { createSchemaCommand } from "../../internal/schema-commands.js";
@@ -0,0 +1,47 @@
1
+ import "./shared/@outfitter/cli-c09332vm.js";
2
+ import "./shared/@outfitter/cli-nkt399zf.js";
3
+ import { CommandEnvelope } from "./shared/@outfitter/cli-30mt7c5w.js";
4
+ import "./shared/@outfitter/cli-x6qr7bnd.js";
5
+ import { ProgressCallback, StreamEvent } from "@outfitter/contracts/stream";
6
+ /**
7
+ * A single line of NDJSON stream output.
8
+ *
9
+ * Can be a stream event (start, step, progress) or a terminal envelope.
10
+ */
11
+ type StreamLine = StreamEvent | CommandEnvelope;
12
+ /**
13
+ * Write a single NDJSON line to stdout.
14
+ *
15
+ * The data is serialized as compact JSON followed by a newline character.
16
+ * Writes synchronously to stdout to maintain event ordering.
17
+ *
18
+ * @param data - Any JSON-serializable value to write as a single NDJSON line
19
+ */
20
+ declare function writeNdjsonLine(data: unknown): void;
21
+ /**
22
+ * Write a terminal envelope as the final NDJSON line to stdout.
23
+ *
24
+ * In stream mode, both success and error envelopes are written to stdout
25
+ * (not stderr) so the entire NDJSON stream is on a single file descriptor.
26
+ *
27
+ * @param envelope - The terminal command envelope (success or error)
28
+ */
29
+ declare function writeStreamEnvelope(envelope: CommandEnvelope): void;
30
+ /**
31
+ * Create a `ProgressCallback` that writes NDJSON lines to stdout.
32
+ *
33
+ * The callback is provided to handlers via `ctx.progress` when streaming
34
+ * is active. Each call to the returned function writes one NDJSON line.
35
+ *
36
+ * @param _commandName - Optional command name (reserved for future use in adapter context)
37
+ * @returns A `ProgressCallback` that writes stream events as NDJSON
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * const progress = createNdjsonProgress("deploy");
42
+ * progress({ type: "start", command: "deploy", ts: new Date().toISOString() });
43
+ * progress({ type: "progress", current: 1, total: 10 });
44
+ * ```
45
+ */
46
+ declare function createNdjsonProgress(_commandName?: string): ProgressCallback;
47
+ export { writeStreamEnvelope, writeNdjsonLine, createNdjsonProgress, StreamLine };
@@ -0,0 +1,13 @@
1
+ // @bun
2
+ import {
3
+ createNdjsonProgress,
4
+ writeNdjsonLine,
5
+ writeStreamEnvelope
6
+ } from "./shared/@outfitter/cli-g43887b7.js";
7
+ import"./shared/@outfitter/cli-4h85mpth.js";
8
+ import"./shared/@outfitter/cli-dg0cz7rw.js";
9
+ export {
10
+ writeStreamEnvelope,
11
+ writeNdjsonLine,
12
+ createNdjsonProgress
13
+ };
@@ -1,19 +1 @@
1
- // @bun
2
- import {
3
- getEnvValue,
4
- getTerminalWidth,
5
- hasNoColorEnv,
6
- isInteractive,
7
- resolveColorEnv,
8
- resolveForceColorEnv,
9
- supportsColor
10
- } from "../shared/@outfitter/cli-jbj78ac5.js";
11
- export {
12
- supportsColor,
13
- resolveForceColorEnv,
14
- resolveColorEnv,
15
- isInteractive,
16
- hasNoColorEnv,
17
- getTerminalWidth,
18
- getEnvValue
19
- };
1
+ export { getEnvValue, getTerminalWidth, hasNoColorEnv, isInteractive, resolveColorEnv, resolveForceColorEnv, supportsColor } from "./detection.js";
@@ -0,0 +1,104 @@
1
+ import { CLIHint } from "@outfitter/contracts";
2
+ /**
3
+ * Default threshold (in items) above which a file pointer is generated
4
+ * for the full output. Can be overridden via `filePointerThreshold` option.
5
+ */
6
+ declare const DEFAULT_FILE_POINTER_THRESHOLD = 1e3;
7
+ /**
8
+ * Options for output truncation.
9
+ */
10
+ interface TruncationOptions {
11
+ /**
12
+ * Command name used in pagination hints.
13
+ * When provided, hints include the full command for continuation.
14
+ */
15
+ readonly commandName?: string;
16
+ /**
17
+ * Threshold (in items) above which full output is written to a temp file.
18
+ * Defaults to {@link DEFAULT_FILE_POINTER_THRESHOLD}.
19
+ */
20
+ readonly filePointerThreshold?: number;
21
+ /**
22
+ * Maximum number of items to include in the output.
23
+ * When undefined, output is not truncated.
24
+ */
25
+ readonly limit?: number;
26
+ /**
27
+ * Starting offset for pagination (0-based).
28
+ * Defaults to 0.
29
+ */
30
+ readonly offset?: number;
31
+ /**
32
+ * Custom temp directory for file pointers.
33
+ * Defaults to the OS temp directory.
34
+ * Primarily used for testing fault injection.
35
+ */
36
+ readonly tempDir?: string;
37
+ }
38
+ /**
39
+ * Metadata about truncation applied to the output.
40
+ *
41
+ * Present only when truncation was applied.
42
+ */
43
+ interface TruncationMetadata {
44
+ /**
45
+ * Path to a temp file containing the complete output.
46
+ * Present only when output exceeds the file pointer threshold.
47
+ */
48
+ readonly full_output?: string;
49
+ /** Number of items in the truncated output. */
50
+ readonly showing: number;
51
+ /** Total number of items before truncation. */
52
+ readonly total: number;
53
+ /** Always `true` when metadata is present. */
54
+ readonly truncated: true;
55
+ }
56
+ /**
57
+ * Result of applying truncation to an output array.
58
+ *
59
+ * @typeParam T - Type of items in the output array
60
+ */
61
+ interface TruncationResult<T = unknown> {
62
+ /** The (possibly truncated) output data. */
63
+ readonly data: T[];
64
+ /** Pagination and warning hints. Empty when no truncation applied. */
65
+ readonly hints: CLIHint[];
66
+ /** Truncation metadata. Undefined when no truncation was applied. */
67
+ readonly metadata: TruncationMetadata | undefined;
68
+ }
69
+ /**
70
+ * Truncate output with pagination hints and optional file pointers.
71
+ *
72
+ * When `limit` is not configured (undefined), output passes through untouched.
73
+ * When data length is at or below limit, output passes through untouched.
74
+ * When data exceeds limit, it is truncated with metadata and hints.
75
+ *
76
+ * For very large output (exceeding `filePointerThreshold`), the full result
77
+ * is written to a temp file and a file pointer is included in metadata.
78
+ * If the file write fails, truncated output is returned with a warning hint.
79
+ *
80
+ * @typeParam T - Type of items in the output array
81
+ * @param data - The output data to potentially truncate
82
+ * @param options - Truncation configuration
83
+ * @returns Truncation result with data, metadata, and hints
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * import { truncateOutput } from "@outfitter/cli/truncation";
88
+ *
89
+ * // No truncation (limit not set)
90
+ * const result1 = truncateOutput(items, {});
91
+ * // result1.data === items, result1.metadata === undefined
92
+ *
93
+ * // Truncate to 20 items
94
+ * const result2 = truncateOutput(items, { limit: 20, commandName: "list" });
95
+ * // result2.data.length === 20
96
+ * // result2.metadata === { showing: 20, total: 100, truncated: true }
97
+ * // result2.hints includes pagination continuation
98
+ *
99
+ * // With offset (page 2)
100
+ * const result3 = truncateOutput(items, { limit: 20, offset: 20 });
101
+ * ```
102
+ */
103
+ declare function truncateOutput<T>(data: T[] | unknown, options: TruncationOptions): TruncationResult<T>;
104
+ export { truncateOutput, TruncationResult, TruncationOptions, TruncationMetadata, DEFAULT_FILE_POINTER_THRESHOLD };
@@ -0,0 +1,111 @@
1
+ // @bun
2
+ import"./shared/@outfitter/cli-4h85mpth.js";
3
+ import {
4
+ cliStringify
5
+ } from "./shared/@outfitter/cli-dg0cz7rw.js";
6
+
7
+ // packages/cli/src/truncation.ts
8
+ import { writeFileSync } from "fs";
9
+ import { tmpdir } from "os";
10
+ import { isAbsolute, join, normalize } from "path";
11
+ var DEFAULT_FILE_POINTER_THRESHOLD = 1000;
12
+ var TEMP_FILE_PREFIX = "outfitter-output-";
13
+ var PATH_TRAVERSAL_PATTERN = /(?:^|[\\/])\.\.(?:[\\/]|$)/;
14
+ function validateTempDir(dir) {
15
+ if (!isAbsolute(dir)) {
16
+ return;
17
+ }
18
+ const normalized = normalize(dir);
19
+ if (PATH_TRAVERSAL_PATTERN.test(normalized)) {
20
+ return;
21
+ }
22
+ if (PATH_TRAVERSAL_PATTERN.test(dir)) {
23
+ return;
24
+ }
25
+ return normalized;
26
+ }
27
+ function generateTempFilePath(dir) {
28
+ const timestamp = Date.now();
29
+ const random = Math.random().toString(36).slice(2, 8);
30
+ return join(dir, `${TEMP_FILE_PREFIX}${timestamp}-${random}.json`);
31
+ }
32
+ function writeFullOutput(data, tempDir) {
33
+ try {
34
+ const filePath = generateTempFilePath(tempDir);
35
+ const content = cliStringify(data, true);
36
+ writeFileSync(filePath, content, "utf-8");
37
+ return { path: filePath };
38
+ } catch (err) {
39
+ const message = err instanceof Error ? err.message : String(err);
40
+ return { error: message };
41
+ }
42
+ }
43
+ function buildPaginationHints(total, limit, offset, commandName) {
44
+ const hints = [];
45
+ const nextOffset = offset + limit;
46
+ if (nextOffset < total) {
47
+ const remaining = total - nextOffset;
48
+ const nextPageSize = Math.min(limit, remaining);
49
+ const cmdPrefix = commandName ? `${commandName} ` : "";
50
+ hints.push({
51
+ description: `Show next ${nextPageSize} of ${remaining} remaining items`,
52
+ command: `${cmdPrefix}--offset ${nextOffset} --limit ${limit}`
53
+ });
54
+ }
55
+ return hints;
56
+ }
57
+ function truncateOutput(data, options) {
58
+ const { limit, commandName } = options;
59
+ if (!Array.isArray(data)) {
60
+ return {
61
+ data,
62
+ metadata: undefined,
63
+ hints: []
64
+ };
65
+ }
66
+ const items = data;
67
+ if (limit === undefined) {
68
+ return {
69
+ data: items,
70
+ metadata: undefined,
71
+ hints: []
72
+ };
73
+ }
74
+ const total = items.length;
75
+ if (total <= limit && (options.offset === undefined || options.offset <= 0)) {
76
+ return {
77
+ data: items,
78
+ metadata: undefined,
79
+ hints: []
80
+ };
81
+ }
82
+ const offset = Math.max(0, options.offset ?? 0);
83
+ const sliced = items.slice(offset, offset + limit);
84
+ const showing = sliced.length;
85
+ const hints = buildPaginationHints(total, limit, offset, commandName);
86
+ const filePointerThreshold = options.filePointerThreshold ?? DEFAULT_FILE_POINTER_THRESHOLD;
87
+ let fullOutput;
88
+ if (total > filePointerThreshold) {
89
+ const rawTempDir = options.tempDir;
90
+ const tempDir = rawTempDir ? validateTempDir(rawTempDir) ?? tmpdir() : tmpdir();
91
+ const writeResult = writeFullOutput(items, tempDir);
92
+ if ("path" in writeResult) {
93
+ fullOutput = writeResult.path;
94
+ } else {
95
+ hints.push({
96
+ description: `Warning: Could not write full output to file (${writeResult.error})`,
97
+ command: `${commandName ? `${commandName} ` : ""}--limit ${total}`
98
+ });
99
+ }
100
+ }
101
+ const metadata = fullOutput ? { showing, total, truncated: true, full_output: fullOutput } : { showing, total, truncated: true };
102
+ return {
103
+ data: sliced,
104
+ metadata,
105
+ hints
106
+ };
107
+ }
108
+ export {
109
+ truncateOutput,
110
+ DEFAULT_FILE_POINTER_THRESHOLD
111
+ };
package/dist/types.d.ts CHANGED
@@ -1,2 +1,2 @@
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-98aa9104.js";
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 };
1
+ import { AnyPreset, BooleanFlagPresetConfig, CLI, CLIConfig, CancelledError, CollectIdsOptions, ColorFlags, ColorMode, CommandAction, CommandBuilder, CommandConfig, CommandFlags, ComposedPreset, ContextFactory, CursorOptions, DateRange, EnumFlagPresetConfig, ErrorCategory, ErrorHintFn, ExecutionFlags, ExecutionPresetConfig, ExpandFileOptions, FilterExpression, FlagPreset, FlagPresetConfig, InteractionFlags, KeyValuePair, NormalizeIdOptions, NumberFlagPresetConfig, NumericRange, OutputMode, OutputOptions, PaginationFlags, PaginationPresetConfig, PaginationState, ParseGlobOptions, ProjectionFlags, Range, RelatedToDeclaration, RelatedToOptions, Result, SchemaPreset, SchemaPresetConfig, SortCriteria, StrictFlags, StringListFlagPresetConfig, SuccessHintFn, TimeWindowFlags, TimeWindowPresetConfig, ValidationError, VerbConfig, VerbFamily, ZodObjectLike } from "./shared/@outfitter/cli-x6qr7bnd.js";
2
+ export { ZodObjectLike, VerbFamily, VerbConfig, ValidationError, TimeWindowPresetConfig, TimeWindowFlags, SuccessHintFn, StringListFlagPresetConfig, StrictFlags, SortCriteria, SchemaPresetConfig, SchemaPreset, Result, RelatedToOptions, RelatedToDeclaration, Range, ProjectionFlags, ParseGlobOptions, PaginationState, PaginationPresetConfig, PaginationFlags, OutputOptions, OutputMode, NumericRange, NumberFlagPresetConfig, NormalizeIdOptions, KeyValuePair, InteractionFlags, FlagPresetConfig, FlagPreset, FilterExpression, ExpandFileOptions, ExecutionPresetConfig, ExecutionFlags, ErrorHintFn, ErrorCategory, EnumFlagPresetConfig, DateRange, CursorOptions, ContextFactory, ComposedPreset, CommandFlags, CommandConfig, CommandBuilder, CommandAction, ColorMode, ColorFlags, CollectIdsOptions, CancelledError, CLIConfig, CLI, BooleanFlagPresetConfig, AnyPreset };
package/dist/types.js CHANGED
@@ -1,9 +1,4 @@
1
1
  // @bun
2
- // packages/cli/src/types.ts
3
- import {
4
- CancelledError,
5
- ValidationError
6
- } from "@outfitter/contracts";
7
2
  export {
8
3
  ValidationError,
9
4
  CancelledError
package/dist/verbs.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { CommandBuilder, VerbConfig, VerbFamily } from "./shared/@outfitter/cli-98aa9104.js";
1
+ import { CommandBuilder, VerbConfig, VerbFamily } from "./shared/@outfitter/cli-x6qr7bnd.js";
2
2
  /**
3
3
  * Built-in verb families with standard primary verbs and aliases.
4
4
  *