@outfitter/cli 0.1.0-rc.1 → 0.1.0-rc.2

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 (88) hide show
  1. package/dist/actions.d.ts +1 -12
  2. package/dist/actions.js +3 -170
  3. package/dist/cli.d.ts +2 -103
  4. package/dist/cli.js +4 -51
  5. package/dist/command.d.ts +2 -73
  6. package/dist/command.js +4 -42
  7. package/dist/index.d.ts +7 -606
  8. package/dist/index.js +11 -11
  9. package/dist/input.d.ts +2 -277
  10. package/dist/input.js +11 -426
  11. package/dist/output.d.ts +2 -68
  12. package/dist/output.js +4 -150
  13. package/dist/pagination.d.ts +2 -95
  14. package/dist/pagination.js +6 -84
  15. package/dist/render/colors.d.ts +2 -0
  16. package/dist/render/colors.js +17 -0
  17. package/dist/render/date.d.ts +2 -0
  18. package/dist/render/date.js +12 -0
  19. package/dist/render/format-relative.d.ts +2 -0
  20. package/dist/render/format-relative.js +8 -0
  21. package/dist/render/format.d.ts +2 -0
  22. package/dist/render/format.js +10 -0
  23. package/dist/render/index.d.ts +13 -0
  24. package/dist/render/index.js +101 -0
  25. package/dist/render/json.d.ts +2 -0
  26. package/dist/render/json.js +10 -0
  27. package/dist/render/list.d.ts +2 -0
  28. package/dist/render/list.js +8 -0
  29. package/dist/render/markdown.d.ts +2 -0
  30. package/dist/render/markdown.js +10 -0
  31. package/dist/render/progress.d.ts +2 -0
  32. package/dist/render/progress.js +8 -0
  33. package/dist/render/shapes.d.ts +2 -0
  34. package/dist/render/shapes.js +34 -0
  35. package/dist/render/table.d.ts +2 -0
  36. package/dist/render/table.js +11 -0
  37. package/dist/render/text.d.ts +2 -0
  38. package/dist/render/text.js +24 -0
  39. package/dist/render/tree.d.ts +2 -0
  40. package/dist/render/tree.js +8 -0
  41. package/dist/shared/@outfitter/cli-2vs2gxa8.js +429 -0
  42. package/dist/shared/@outfitter/cli-2y3kxew8.d.ts +58 -0
  43. package/dist/shared/@outfitter/cli-2yq94zst.d.ts +39 -0
  44. package/dist/shared/@outfitter/cli-33e97cjs.d.ts +42 -0
  45. package/dist/shared/@outfitter/cli-3hp8qwx3.js +11 -0
  46. package/dist/shared/@outfitter/cli-6xc869x1.js +26 -0
  47. package/dist/shared/@outfitter/cli-72kg550t.d.ts +53 -0
  48. package/dist/shared/@outfitter/cli-7km1e58p.js +85 -0
  49. package/dist/shared/@outfitter/cli-7na6p4fs.d.ts +59 -0
  50. package/dist/shared/@outfitter/cli-8aa1vhdn.d.ts +119 -0
  51. package/dist/shared/@outfitter/cli-8j5k6mr3.js +71 -0
  52. package/dist/shared/@outfitter/cli-8r0bnyyx.js +43 -0
  53. package/dist/shared/@outfitter/cli-a4q87517.d.ts +64 -0
  54. package/dist/shared/@outfitter/cli-afecwfrn.d.ts +13 -0
  55. package/dist/shared/@outfitter/cli-ag0w4pk0.js +89 -0
  56. package/dist/shared/@outfitter/cli-azzk8a1d.js +59 -0
  57. package/dist/shared/@outfitter/cli-bc17qeh2.js +19 -0
  58. package/dist/shared/@outfitter/cli-bt423m6y.js +4 -0
  59. package/dist/shared/@outfitter/cli-c8q4f71g.js +144 -0
  60. package/dist/shared/@outfitter/cli-c9knfqn5.d.ts +30 -0
  61. package/dist/shared/@outfitter/cli-cf2s94s1.d.ts +42 -0
  62. package/dist/shared/@outfitter/cli-d4fegbad.d.ts +66 -0
  63. package/dist/shared/@outfitter/cli-dds0qqvm.d.ts +145 -0
  64. package/dist/shared/@outfitter/cli-e0ecw3x1.js +64 -0
  65. package/dist/shared/@outfitter/cli-efy6jfcj.js +52 -0
  66. package/dist/shared/@outfitter/cli-evx7qcp1.d.ts +300 -0
  67. package/dist/shared/@outfitter/cli-fheaaa6b.js +25 -0
  68. package/dist/shared/@outfitter/cli-j19a91ck.js +30 -0
  69. package/dist/shared/@outfitter/cli-j361wp3g.d.ts +41 -0
  70. package/dist/shared/@outfitter/cli-jbj78ac5.js +70 -0
  71. package/dist/shared/@outfitter/cli-mhamvbty.d.ts +34 -0
  72. package/dist/shared/@outfitter/cli-n51t773m.d.ts +208 -0
  73. package/dist/shared/@outfitter/cli-p0m2fc51.js +172 -0
  74. package/dist/shared/@outfitter/cli-ttt7r0j7.d.ts +253 -0
  75. package/dist/shared/@outfitter/cli-tyajr8qa.d.ts +63 -0
  76. package/dist/shared/@outfitter/cli-v9mjsvjh.js +118 -0
  77. package/dist/shared/@outfitter/cli-wqc652mx.js +135 -0
  78. package/dist/shared/@outfitter/cli-zact3325.js +152 -0
  79. package/dist/shared/@outfitter/cli-zs6jy1am.d.ts +164 -0
  80. package/dist/shared/@outfitter/cli-zx598p8q.d.ts +26 -0
  81. package/dist/terminal/detection.d.ts +2 -0
  82. package/dist/terminal/detection.js +20 -0
  83. package/dist/terminal/index.d.ts +2 -0
  84. package/dist/terminal/index.js +20 -0
  85. package/dist/types.d.ts +1 -252
  86. package/dist/types.js +1 -1
  87. package/package.json +98 -4
  88. package/dist/shared/@outfitter/cli-4yy82cmp.js +0 -20
@@ -0,0 +1,85 @@
1
+ // @bun
2
+ // packages/cli/src/pagination.ts
3
+ import fs from "fs";
4
+ import os from "os";
5
+ import path from "path";
6
+ var UNSAFE_PATH_PATTERN = /(?:^|[\\/])\.\.(?:[\\/]|$)|^[\\/]|^[a-zA-Z]:/;
7
+ function validatePathComponent(component, name) {
8
+ if (UNSAFE_PATH_PATTERN.test(component)) {
9
+ throw new Error(`Security: path traversal detected in ${name}`);
10
+ }
11
+ }
12
+ function getDefaultStateHome() {
13
+ const home = os.homedir();
14
+ switch (process.platform) {
15
+ case "darwin":
16
+ return path.join(home, "Library", "Application Support");
17
+ case "win32":
18
+ return process.env["LOCALAPPDATA"] ?? path.join(home, "AppData", "Local");
19
+ default:
20
+ return path.join(home, ".local", "state");
21
+ }
22
+ }
23
+ function getStatePath(options) {
24
+ validatePathComponent(options.command, "command");
25
+ validatePathComponent(options.toolName, "toolName");
26
+ if (options.context) {
27
+ validatePathComponent(options.context, "context");
28
+ }
29
+ const xdgState = process.env["XDG_STATE_HOME"] ?? getDefaultStateHome();
30
+ const parts = [xdgState, options.toolName, "cursors", options.command];
31
+ if (options.context) {
32
+ parts.push(options.context);
33
+ }
34
+ return path.join(...parts, "cursor.json");
35
+ }
36
+ function loadCursor(options) {
37
+ const statePath = getStatePath(options);
38
+ if (!fs.existsSync(statePath)) {
39
+ return;
40
+ }
41
+ try {
42
+ const content = fs.readFileSync(statePath, "utf-8");
43
+ const parsed = JSON.parse(content);
44
+ if (typeof parsed !== "object" || parsed === null || typeof parsed["cursor"] !== "string") {
45
+ return;
46
+ }
47
+ const state = parsed;
48
+ if (options.maxAgeMs !== undefined) {
49
+ if (typeof state.timestamp !== "number") {
50
+ return;
51
+ }
52
+ const ageMs = Date.now() - state.timestamp;
53
+ if (ageMs > options.maxAgeMs) {
54
+ return;
55
+ }
56
+ }
57
+ return state;
58
+ } catch {
59
+ return;
60
+ }
61
+ }
62
+ function saveCursor(cursor, options) {
63
+ const statePath = getStatePath(options);
64
+ const stateDir = path.dirname(statePath);
65
+ fs.mkdirSync(stateDir, { recursive: true });
66
+ const state = {
67
+ cursor,
68
+ command: options.command,
69
+ timestamp: Date.now(),
70
+ hasMore: options.hasMore ?? true,
71
+ ...options.context && { context: options.context },
72
+ ...options.total !== undefined && { total: options.total }
73
+ };
74
+ fs.writeFileSync(statePath, JSON.stringify(state), "utf-8");
75
+ }
76
+ function clearCursor(options) {
77
+ const statePath = getStatePath(options);
78
+ try {
79
+ if (fs.existsSync(statePath)) {
80
+ fs.unlinkSync(statePath);
81
+ }
82
+ } catch {}
83
+ }
84
+
85
+ export { loadCursor, saveCursor, clearCursor };
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Progress bar rendering utilities.
3
+ *
4
+ * Renders progress bars with filled and empty segments.
5
+ *
6
+ * @packageDocumentation
7
+ */
8
+ /**
9
+ * Configuration options for {@link renderProgress}.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * const options: ProgressOptions = {
14
+ * current: 75,
15
+ * total: 100,
16
+ * width: 20,
17
+ * showPercent: true,
18
+ * };
19
+ * // Renders: [###############.....] 75%
20
+ * ```
21
+ */
22
+ interface ProgressOptions {
23
+ /** Current progress value */
24
+ current: number;
25
+ /** Total value (100% when current equals total) */
26
+ total: number;
27
+ /** Width of the progress bar in characters (default: 20) */
28
+ width?: number;
29
+ /** Whether to show percentage after the bar (default: false) */
30
+ showPercent?: boolean;
31
+ }
32
+ /**
33
+ * Renders a progress bar with filled and empty segments.
34
+ *
35
+ * Uses unicode block characters: filled segments, empty segments.
36
+ * Optionally displays percentage after the bar.
37
+ *
38
+ * Handles edge cases:
39
+ * - `total <= 0`: Returns empty bar with 0%
40
+ * - `current > total`: Caps at 100%
41
+ * - `current < 0`: Floors at 0%
42
+ *
43
+ * @param options - Progress bar configuration
44
+ * @returns Formatted progress bar string
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * renderProgress({ current: 50, total: 100 });
49
+ * // [##########..........]
50
+ *
51
+ * renderProgress({ current: 75, total: 100, showPercent: true });
52
+ * // [###############.....] 75%
53
+ *
54
+ * renderProgress({ current: 30, total: 100, width: 10 });
55
+ * // [###.......]
56
+ * ```
57
+ */
58
+ declare function renderProgress(options: ProgressOptions): string;
59
+ export { ProgressOptions, renderProgress };
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Options for terminal detection functions.
3
+ *
4
+ * These options allow overriding automatic detection for testing
5
+ * or specific environments.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * // Force TTY mode for testing
10
+ * supportsColor({ isTTY: true });
11
+ *
12
+ * // Simulate CI environment
13
+ * isInteractive({ isTTY: true, isCI: true }); // returns false
14
+ * ```
15
+ */
16
+ interface TerminalOptions {
17
+ /** Override TTY detection (uses process.stdout.isTTY if not specified) */
18
+ isTTY?: boolean;
19
+ /** Override CI detection (uses CI env variable if not specified) */
20
+ isCI?: boolean;
21
+ }
22
+ /**
23
+ * Gets the value of an environment variable.
24
+ *
25
+ * @param key - Environment variable name
26
+ * @returns The value if set, undefined otherwise
27
+ */
28
+ declare function getEnvValue(key: "NO_COLOR" | "FORCE_COLOR"): string | undefined;
29
+ /**
30
+ * Checks if NO_COLOR environment variable is set.
31
+ *
32
+ * @returns `true` if NO_COLOR is set (even if empty)
33
+ */
34
+ declare function hasNoColorEnv(): boolean;
35
+ /**
36
+ * Resolves the FORCE_COLOR environment variable.
37
+ *
38
+ * @returns `true` if colors should be forced, `false` if explicitly disabled, `undefined` if not set
39
+ */
40
+ declare function resolveForceColorEnv(): boolean | undefined;
41
+ /**
42
+ * Resolves color environment variables with configurable priority.
43
+ *
44
+ * @param options - Options for resolution priority
45
+ * @returns `true` if colors enabled, `false` if disabled, `undefined` if not determined by env
46
+ */
47
+ declare function resolveColorEnv(options?: {
48
+ forceColorFirst?: boolean;
49
+ }): boolean | undefined;
50
+ /**
51
+ * Checks if the current environment supports ANSI colors.
52
+ *
53
+ * Detection priority:
54
+ * 1. `NO_COLOR` env variable - if set (even empty), returns false (per no-color.org)
55
+ * 2. `FORCE_COLOR` env variable - if set, returns true (numeric levels > 0)
56
+ * 3. Falls back to TTY detection via `process.stdout.isTTY`
57
+ *
58
+ * @param options - Optional overrides for TTY detection
59
+ * @returns `true` if colors are supported, `false` otherwise
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * if (supportsColor()) {
64
+ * console.log("\x1b[32mGreen text\x1b[0m");
65
+ * } else {
66
+ * console.log("Plain text");
67
+ * }
68
+ *
69
+ * // With explicit TTY override for testing
70
+ * supportsColor({ isTTY: true }); // true (unless NO_COLOR is set)
71
+ * supportsColor({ isTTY: false }); // false (unless FORCE_COLOR is set)
72
+ * ```
73
+ */
74
+ declare function supportsColor(options?: TerminalOptions): boolean;
75
+ /**
76
+ * Gets the terminal width in columns.
77
+ *
78
+ * Returns `process.stdout.columns` when in a TTY, or 80 as a fallback
79
+ * for non-TTY environments (pipes, CI, etc.).
80
+ *
81
+ * @param options - Optional overrides for TTY detection
82
+ * @returns Terminal width in columns (default: 80 if not TTY)
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * const width = getTerminalWidth();
87
+ * console.log(`Terminal is ${width} columns wide`);
88
+ *
89
+ * // Force non-TTY mode (always returns 80)
90
+ * getTerminalWidth({ isTTY: false }); // 80
91
+ * ```
92
+ */
93
+ declare function getTerminalWidth(options?: TerminalOptions): number;
94
+ /**
95
+ * Checks if the terminal is interactive.
96
+ *
97
+ * A terminal is considered interactive when:
98
+ * - It is a TTY (process.stdout.isTTY is true)
99
+ * - It is NOT running in a CI environment (CI env variable is not set)
100
+ *
101
+ * Use this to decide whether to show interactive prompts or use
102
+ * non-interactive fallbacks.
103
+ *
104
+ * @param options - Optional overrides for TTY and CI detection
105
+ * @returns `true` if interactive prompts are safe to use
106
+ *
107
+ * @example
108
+ * ```typescript
109
+ * if (isInteractive()) {
110
+ * // Safe to use interactive prompts
111
+ * const answer = await prompt("Continue?");
112
+ * } else {
113
+ * // Use non-interactive defaults
114
+ * console.log("Running in non-interactive mode");
115
+ * }
116
+ * ```
117
+ */
118
+ declare function isInteractive(options?: TerminalOptions): boolean;
119
+ export { TerminalOptions, getEnvValue, hasNoColorEnv, resolveForceColorEnv, resolveColorEnv, supportsColor, getTerminalWidth, isInteractive };
@@ -0,0 +1,71 @@
1
+ // @bun
2
+ // packages/cli/src/render/format-relative.ts
3
+ function formatRelative(date) {
4
+ let timestamp;
5
+ if (date instanceof Date) {
6
+ timestamp = date.getTime();
7
+ } else if (typeof date === "number") {
8
+ timestamp = date;
9
+ } else {
10
+ const parsed = Date.parse(date);
11
+ if (Number.isNaN(parsed)) {
12
+ return "invalid date";
13
+ }
14
+ timestamp = parsed;
15
+ }
16
+ if (!Number.isFinite(timestamp)) {
17
+ return "invalid date";
18
+ }
19
+ const now = Date.now();
20
+ const diffMs = now - timestamp;
21
+ const absDiffMs = Math.abs(diffMs);
22
+ const isFuture = diffMs < 0;
23
+ const SECOND = 1000;
24
+ const MINUTE = 60 * SECOND;
25
+ const HOUR = 60 * MINUTE;
26
+ const DAY = 24 * HOUR;
27
+ const MONTH = 30 * DAY;
28
+ const YEAR = 365 * DAY;
29
+ if (absDiffMs < 10 * SECOND) {
30
+ return "just now";
31
+ }
32
+ if (absDiffMs < MINUTE) {
33
+ const seconds = Math.floor(absDiffMs / SECOND);
34
+ return isFuture ? `in ${seconds} seconds` : `${seconds} seconds ago`;
35
+ }
36
+ if (absDiffMs < HOUR) {
37
+ const minutes = Math.floor(absDiffMs / MINUTE);
38
+ if (minutes === 1) {
39
+ return isFuture ? "in 1 minute" : "1 minute ago";
40
+ }
41
+ return isFuture ? `in ${minutes} minutes` : `${minutes} minutes ago`;
42
+ }
43
+ if (absDiffMs < DAY) {
44
+ const hours = Math.floor(absDiffMs / HOUR);
45
+ if (hours === 1) {
46
+ return isFuture ? "in 1 hour" : "1 hour ago";
47
+ }
48
+ return isFuture ? `in ${hours} hours` : `${hours} hours ago`;
49
+ }
50
+ if (absDiffMs < 2 * DAY) {
51
+ return isFuture ? "tomorrow" : "yesterday";
52
+ }
53
+ if (absDiffMs < MONTH) {
54
+ const days = Math.floor(absDiffMs / DAY);
55
+ return isFuture ? `in ${days} days` : `${days} days ago`;
56
+ }
57
+ if (absDiffMs < YEAR) {
58
+ const months = Math.floor(absDiffMs / MONTH);
59
+ if (months === 1) {
60
+ return isFuture ? "in 1 month" : "1 month ago";
61
+ }
62
+ return isFuture ? `in ${months} months` : `${months} months ago`;
63
+ }
64
+ const years = Math.floor(absDiffMs / YEAR);
65
+ if (years === 1) {
66
+ return isFuture ? "in 1 year" : "1 year ago";
67
+ }
68
+ return isFuture ? `in ${years} years` : `${years} years ago`;
69
+ }
70
+
71
+ export { formatRelative };
@@ -0,0 +1,43 @@
1
+ // @bun
2
+ // packages/cli/src/command.ts
3
+ import { Command } from "commander";
4
+
5
+ class CommandBuilderImpl {
6
+ command;
7
+ constructor(name) {
8
+ this.command = new Command(name);
9
+ }
10
+ description(text) {
11
+ this.command.description(text);
12
+ return this;
13
+ }
14
+ option(flags, description, defaultValue) {
15
+ this.command.option(flags, description, defaultValue);
16
+ return this;
17
+ }
18
+ requiredOption(flags, description, defaultValue) {
19
+ this.command.requiredOption(flags, description, defaultValue);
20
+ return this;
21
+ }
22
+ alias(alias) {
23
+ this.command.alias(alias);
24
+ return this;
25
+ }
26
+ action(handler) {
27
+ this.command.action(async (...args) => {
28
+ const command = args.at(-1);
29
+ const flags = command.optsWithGlobals?.() ?? command.opts();
30
+ const positional = command.args;
31
+ await handler({ args: positional, flags, command });
32
+ });
33
+ return this;
34
+ }
35
+ build() {
36
+ return this.command;
37
+ }
38
+ }
39
+ function command(name) {
40
+ return new CommandBuilderImpl(name);
41
+ }
42
+
43
+ export { command };
@@ -0,0 +1,64 @@
1
+ import { Result, ValidationError } from "@outfitter/contracts";
2
+ /**
3
+ * Represents a date range with start and end dates.
4
+ */
5
+ interface DateRange {
6
+ /** Start of the range (inclusive, at 00:00:00.000) */
7
+ start: Date;
8
+ /** End of the range (inclusive, at 23:59:59.999) */
9
+ end: Date;
10
+ }
11
+ /**
12
+ * Returns a new Date set to the start of the day (00:00:00.000).
13
+ *
14
+ * @param date - The date to adjust
15
+ * @returns A new Date object at 00:00:00.000 on the same day
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * startOfDay(new Date("2024-06-15T14:30:00Z"))
20
+ * // Returns 2024-06-15T00:00:00.000 (local time)
21
+ * ```
22
+ */
23
+ declare function startOfDay(date: Date): Date;
24
+ /**
25
+ * Returns a new Date set to the end of the day (23:59:59.999).
26
+ *
27
+ * @param date - The date to adjust
28
+ * @returns A new Date object at 23:59:59.999 on the same day
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * endOfDay(new Date("2024-06-15T14:30:00Z"))
33
+ * // Returns 2024-06-15T23:59:59.999 (local time)
34
+ * ```
35
+ */
36
+ declare function endOfDay(date: Date): Date;
37
+ /**
38
+ * Parses date range strings into structured DateRange.
39
+ *
40
+ * Supported formats:
41
+ * - `"today"` - Current day (00:00:00 to 23:59:59)
42
+ * - `"yesterday"` - Previous day
43
+ * - `"last week"` - Last 7 days
44
+ * - `"last month"` - Last 30 days
45
+ * - `"2024-01-01..2024-12-31"` - Explicit range
46
+ * - `"2024-01-01"` - Single day
47
+ *
48
+ * @param input - The date range string to parse
49
+ * @returns Result containing DateRange on success, or ValidationError on failure
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * parseDateRange("today")
54
+ * // Ok({ start: today 00:00, end: today 23:59:59 })
55
+ *
56
+ * parseDateRange("2024-01-01..2024-12-31")
57
+ * // Ok({ start: 2024-01-01 00:00, end: 2024-12-31 23:59:59 })
58
+ *
59
+ * parseDateRange("invalid")
60
+ * // Err(ValidationError)
61
+ * ```
62
+ */
63
+ declare function parseDateRange(input: string): Result<DateRange, InstanceType<typeof ValidationError>>;
64
+ export { DateRange, startOfDay, endOfDay, parseDateRange };
@@ -0,0 +1,13 @@
1
+ import { ActionRegistry, ActionSurface, AnyActionSpec, HandlerContext } from "@outfitter/contracts";
2
+ import { Command } from "commander";
3
+ interface BuildCliCommandsOptions {
4
+ readonly createContext?: (input: {
5
+ action: AnyActionSpec;
6
+ args: readonly string[];
7
+ flags: Record<string, unknown>;
8
+ }) => HandlerContext;
9
+ readonly includeSurfaces?: readonly ActionSurface[];
10
+ }
11
+ type ActionSource = ActionRegistry | readonly AnyActionSpec[];
12
+ declare function buildCliCommands(source: ActionSource, options?: BuildCliCommandsOptions): Command[];
13
+ export { BuildCliCommandsOptions, buildCliCommands };
@@ -0,0 +1,89 @@
1
+ // @bun
2
+ import {
3
+ resolveColorEnv,
4
+ supportsColor
5
+ } from "./cli-jbj78ac5.js";
6
+
7
+ // packages/cli/src/render/colors.ts
8
+ var ANSI = {
9
+ reset: "\x1B[0m",
10
+ bold: "\x1B[1m",
11
+ dim: "\x1B[2m",
12
+ italic: "\x1B[3m",
13
+ green: "\x1B[32m",
14
+ yellow: "\x1B[33m",
15
+ red: "\x1B[31m",
16
+ blue: "\x1B[34m",
17
+ cyan: "\x1B[36m",
18
+ magenta: "\x1B[35m",
19
+ white: "\x1B[37m",
20
+ gray: "\x1B[90m"
21
+ };
22
+ function createTheme() {
23
+ const colorEnabled = supportsColor();
24
+ const colorFn = (ansiCode) => (text) => {
25
+ if (!colorEnabled) {
26
+ return text;
27
+ }
28
+ return `${ansiCode}${text}${ANSI.reset}`;
29
+ };
30
+ return {
31
+ success: colorFn(ANSI.green),
32
+ warning: colorFn(ANSI.yellow),
33
+ error: colorFn(ANSI.red),
34
+ info: colorFn(ANSI.blue),
35
+ primary: colorFn(""),
36
+ secondary: colorFn(ANSI.gray),
37
+ muted: colorFn(ANSI.dim)
38
+ };
39
+ }
40
+ function resolveTokenColorEnabled(options) {
41
+ if (options?.colorLevel === 0) {
42
+ return false;
43
+ }
44
+ if (options?.forceColor === true) {
45
+ return true;
46
+ }
47
+ if (options?.colorLevel !== undefined) {
48
+ return options.colorLevel > 0;
49
+ }
50
+ const env = resolveColorEnv({ forceColorFirst: true });
51
+ if (env !== undefined) {
52
+ return env;
53
+ }
54
+ return process.stdout.isTTY ?? false;
55
+ }
56
+ function createTokens(options) {
57
+ const colorEnabled = resolveTokenColorEnabled(options);
58
+ if (!colorEnabled) {
59
+ return {
60
+ success: "",
61
+ warning: "",
62
+ error: "",
63
+ info: "",
64
+ muted: "",
65
+ accent: "",
66
+ primary: "",
67
+ secondary: ""
68
+ };
69
+ }
70
+ return {
71
+ success: ANSI.green,
72
+ warning: ANSI.yellow,
73
+ error: ANSI.red,
74
+ info: ANSI.blue,
75
+ muted: ANSI.dim,
76
+ accent: ANSI.cyan,
77
+ primary: "",
78
+ secondary: ANSI.gray
79
+ };
80
+ }
81
+ function applyColor(text, color) {
82
+ if (!supportsColor()) {
83
+ return text;
84
+ }
85
+ const ansiCode = ANSI[color];
86
+ return `${ansiCode}${text}${ANSI.reset}`;
87
+ }
88
+
89
+ export { ANSI, createTheme, resolveTokenColorEnabled, createTokens, applyColor };
@@ -0,0 +1,59 @@
1
+ // @bun
2
+ import {
3
+ ANSI
4
+ } from "./cli-ag0w4pk0.js";
5
+ import {
6
+ supportsColor
7
+ } from "./cli-jbj78ac5.js";
8
+
9
+ // packages/cli/src/render/markdown.ts
10
+ function renderMarkdown(markdown) {
11
+ const colorEnabled = supportsColor();
12
+ let result = markdown;
13
+ result = result.replace(/```(\w*)\n([\s\S]*?)```/g, (_match, _lang, code) => {
14
+ const trimmed = code.trimEnd();
15
+ if (colorEnabled) {
16
+ return `${ANSI.dim}${trimmed}${ANSI.reset}`;
17
+ }
18
+ return trimmed;
19
+ });
20
+ result = result.replace(/^(#{1,6})\s+(.+)$/gm, (_match, _hashes, text) => {
21
+ if (colorEnabled) {
22
+ return `${ANSI.bold}${text}${ANSI.reset}`;
23
+ }
24
+ return text;
25
+ });
26
+ result = result.replace(/\*\*(.+?)\*\*/g, (_match, text) => {
27
+ if (colorEnabled) {
28
+ return `${ANSI.bold}${text}${ANSI.reset}`;
29
+ }
30
+ return text;
31
+ });
32
+ result = result.replace(/__(.+?)__/g, (_match, text) => {
33
+ if (colorEnabled) {
34
+ return `${ANSI.bold}${text}${ANSI.reset}`;
35
+ }
36
+ return text;
37
+ });
38
+ result = result.replace(/(?<!\*)\*([^*]+)\*(?!\*)/g, (_match, text) => {
39
+ if (colorEnabled) {
40
+ return `${ANSI.italic}${text}${ANSI.reset}`;
41
+ }
42
+ return text;
43
+ });
44
+ result = result.replace(/(?<!_)_([^_]+)_(?!_)/g, (_match, text) => {
45
+ if (colorEnabled) {
46
+ return `${ANSI.italic}${text}${ANSI.reset}`;
47
+ }
48
+ return text;
49
+ });
50
+ result = result.replace(/`([^`]+)`/g, (_match, text) => {
51
+ if (colorEnabled) {
52
+ return `${ANSI.cyan}${text}${ANSI.reset}`;
53
+ }
54
+ return text;
55
+ });
56
+ return result;
57
+ }
58
+
59
+ export { renderMarkdown };
@@ -0,0 +1,19 @@
1
+ // @bun
2
+ // packages/cli/src/render/progress.ts
3
+ function renderProgress(options) {
4
+ const { current, total, width = 20, showPercent = false } = options;
5
+ if (total <= 0) {
6
+ const bar2 = "\u2591".repeat(width);
7
+ return showPercent ? `[${bar2}] 0%` : `[${bar2}]`;
8
+ }
9
+ const percent = Math.min(100, Math.max(0, current / total * 100));
10
+ const filled = Math.round(percent / 100 * width);
11
+ const empty = width - filled;
12
+ const bar = "\u2588".repeat(filled) + "\u2591".repeat(empty);
13
+ if (showPercent) {
14
+ return `[${bar}] ${Math.round(percent)}%`;
15
+ }
16
+ return `[${bar}]`;
17
+ }
18
+
19
+ export { renderProgress };
@@ -0,0 +1,4 @@
1
+ // @bun
2
+ var __require = import.meta.require;
3
+
4
+ export { __require };