@jango-blockchained/hoox-cli 0.5.1 → 0.5.6

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/bin/hoox.js +3 -4
  2. package/dist/index.js +32545 -0
  3. package/package.json +10 -15
  4. package/src/commands/check/check-command.test.ts +0 -476
  5. package/src/commands/check/check-command.ts +0 -1144
  6. package/src/commands/check/index.ts +0 -10
  7. package/src/commands/check/prerequisites-command.test.ts +0 -19
  8. package/src/commands/check/prerequisites-command.ts +0 -96
  9. package/src/commands/check/types.ts +0 -103
  10. package/src/commands/clone/clone-command.test.ts +0 -442
  11. package/src/commands/clone/clone-command.ts +0 -431
  12. package/src/commands/clone/index.ts +0 -1
  13. package/src/commands/config/config-command.test.ts +0 -583
  14. package/src/commands/config/config-command.ts +0 -897
  15. package/src/commands/config/env-command.test.ts +0 -43
  16. package/src/commands/config/env-command.ts +0 -295
  17. package/src/commands/config/index.ts +0 -3
  18. package/src/commands/config/kv-command.test.ts +0 -14
  19. package/src/commands/config/kv-command.ts +0 -290
  20. package/src/commands/dashboard/dashboard-command.test.ts +0 -47
  21. package/src/commands/dashboard/dashboard-command.ts +0 -128
  22. package/src/commands/dashboard/index.ts +0 -1
  23. package/src/commands/db/db-command.test.ts +0 -21
  24. package/src/commands/db/db-command.ts +0 -312
  25. package/src/commands/db/index.ts +0 -1
  26. package/src/commands/deploy/deploy-command.test.ts +0 -304
  27. package/src/commands/deploy/deploy-command.ts +0 -1135
  28. package/src/commands/deploy/index.ts +0 -2
  29. package/src/commands/deploy/telegram-service.ts +0 -76
  30. package/src/commands/deploy/types.ts +0 -34
  31. package/src/commands/dev/dev-command.test.ts +0 -383
  32. package/src/commands/dev/dev-command.ts +0 -398
  33. package/src/commands/dev/index.ts +0 -1
  34. package/src/commands/infra/index.ts +0 -5
  35. package/src/commands/infra/infra-command.test.ts +0 -719
  36. package/src/commands/infra/infra-command.ts +0 -974
  37. package/src/commands/infra/types.ts +0 -23
  38. package/src/commands/init/__tests__/cli-provisioner.test.ts +0 -17
  39. package/src/commands/init/cli-provisioner.ts +0 -71
  40. package/src/commands/init/index.ts +0 -1
  41. package/src/commands/init/init-command.test.ts +0 -876
  42. package/src/commands/init/init-command.ts +0 -593
  43. package/src/commands/init/types.ts +0 -23
  44. package/src/commands/logs/index.ts +0 -1
  45. package/src/commands/logs/logs-command.test.ts +0 -294
  46. package/src/commands/logs/logs-command.ts +0 -452
  47. package/src/commands/monitor/index.ts +0 -2
  48. package/src/commands/monitor/monitor-command.test.ts +0 -263
  49. package/src/commands/monitor/monitor-command.ts +0 -269
  50. package/src/commands/monitor/monitor-service.ts +0 -65
  51. package/src/commands/monitor/types.ts +0 -13
  52. package/src/commands/repair/index.ts +0 -2
  53. package/src/commands/repair/repair-command.test.ts +0 -224
  54. package/src/commands/repair/repair-command.ts +0 -220
  55. package/src/commands/repair/repair-service.ts +0 -138
  56. package/src/commands/repair/types.ts +0 -13
  57. package/src/commands/test/index.ts +0 -2
  58. package/src/commands/test/test-command.test.ts +0 -319
  59. package/src/commands/test/test-command.ts +0 -406
  60. package/src/commands/tui/index.ts +0 -1
  61. package/src/commands/tui/tui-command.ts +0 -87
  62. package/src/commands/update/index.ts +0 -1
  63. package/src/commands/update/update-command.ts +0 -168
  64. package/src/commands/waf/index.ts +0 -2
  65. package/src/commands/waf/types.ts +0 -48
  66. package/src/commands/waf/waf-command.test.ts +0 -506
  67. package/src/commands/waf/waf-command.ts +0 -548
  68. package/src/index.ts +0 -269
  69. package/src/services/cloudflare/cloudflare-service.test.ts +0 -658
  70. package/src/services/cloudflare/cloudflare-service.ts +0 -433
  71. package/src/services/cloudflare/index.ts +0 -2
  72. package/src/services/cloudflare/types.ts +0 -30
  73. package/src/services/config/config-service.test.ts +0 -395
  74. package/src/services/config/config-service.ts +0 -207
  75. package/src/services/config/index.ts +0 -9
  76. package/src/services/config/types.ts +0 -66
  77. package/src/services/db/db-service.test.ts +0 -51
  78. package/src/services/db/db-service.ts +0 -131
  79. package/src/services/db/index.ts +0 -1
  80. package/src/services/docker/docker-service.ts +0 -155
  81. package/src/services/docker/index.ts +0 -1
  82. package/src/services/env/env-service.test.ts +0 -236
  83. package/src/services/env/env-service.ts +0 -386
  84. package/src/services/env/index.ts +0 -1
  85. package/src/services/kv/index.ts +0 -1
  86. package/src/services/kv/kv-sync-service.test.ts +0 -38
  87. package/src/services/kv/kv-sync-service.ts +0 -234
  88. package/src/services/prerequisites/index.ts +0 -1
  89. package/src/services/prerequisites/prerequisites-service.test.ts +0 -89
  90. package/src/services/prerequisites/prerequisites-service.ts +0 -377
  91. package/src/services/prerequisites/types.ts +0 -48
  92. package/src/services/secrets/index.ts +0 -11
  93. package/src/services/secrets/secrets-service.test.ts +0 -495
  94. package/src/services/secrets/secrets-service.ts +0 -287
  95. package/src/services/secrets/types.ts +0 -50
  96. package/src/services/update/index.ts +0 -2
  97. package/src/services/update/update-service.test.ts +0 -76
  98. package/src/services/update/update-service.ts +0 -193
  99. package/src/ui/banner.ts +0 -141
  100. package/src/ui/index.ts +0 -8
  101. package/src/ui/menu.ts +0 -490
  102. package/src/utils/errors.test.ts +0 -69
  103. package/src/utils/errors.ts +0 -23
  104. package/src/utils/formatters.test.ts +0 -180
  105. package/src/utils/formatters.ts +0 -269
  106. package/src/utils/theme.ts +0 -94
@@ -1,69 +0,0 @@
1
- import { describe, it, expect } from "bun:test";
2
- import { CLIError, ExitCode } from "./errors.js";
3
-
4
- // CommandItem is defined in errors.js and re-exported; avoid redeclare
5
-
6
- describe("ExitCode", () => {
7
- it("has correct numeric values", () => {
8
- expect(ExitCode.SUCCESS).toBe(0);
9
- expect(ExitCode.ERROR).toBe(1);
10
- expect(ExitCode.INVALID_USAGE).toBe(2);
11
- expect(ExitCode.INFRA_UNAVAILABLE).toBe(3);
12
- });
13
-
14
- it("is a numeric enum with expected values", () => {
15
- expect(ExitCode.SUCCESS).toBe(0);
16
- expect(ExitCode.ERROR).toBe(1);
17
- expect(ExitCode.INVALID_USAGE).toBe(2);
18
- expect(ExitCode.INFRA_UNAVAILABLE).toBe(3);
19
- // All values are distinct
20
- const values = [
21
- ExitCode.SUCCESS,
22
- ExitCode.ERROR,
23
- ExitCode.INVALID_USAGE,
24
- ExitCode.INFRA_UNAVAILABLE,
25
- ];
26
- expect(new Set(values).size).toBe(4);
27
- });
28
- });
29
-
30
- describe("CLIError", () => {
31
- it("constructs with message and defaults", () => {
32
- const err = new CLIError("something went wrong");
33
- expect(err).toBeInstanceOf(Error);
34
- expect(err).toBeInstanceOf(CLIError);
35
- expect(err.name).toBe("CLIError");
36
- expect(err.message).toBe("something went wrong");
37
- expect(err.code).toBe(ExitCode.ERROR);
38
- expect(err.details).toBeUndefined();
39
- expect(err.recoverable).toBe(false);
40
- });
41
-
42
- it("constructs with all fields", () => {
43
- const err = new CLIError(
44
- "invalid input",
45
- ExitCode.INVALID_USAGE,
46
- "Expected --name to be a non-empty string",
47
- true
48
- );
49
- expect(err.message).toBe("invalid input");
50
- expect(err.code).toBe(ExitCode.INVALID_USAGE);
51
- expect(err.details).toBe("Expected --name to be a non-empty string");
52
- expect(err.recoverable).toBe(true);
53
- });
54
-
55
- it("supports INFRA_UNAVAILABLE code", () => {
56
- const err = new CLIError(
57
- "Cloudflare API unreachable",
58
- ExitCode.INFRA_UNAVAILABLE,
59
- "Check your network or try again later"
60
- );
61
- expect(err.code).toBe(3);
62
- expect(err.recoverable).toBe(false);
63
- });
64
-
65
- it("has a string representation that includes the name", () => {
66
- const err = new CLIError("fail");
67
- expect(err.toString()).toBe("CLIError: fail");
68
- });
69
- });
@@ -1,23 +0,0 @@
1
- /**
2
- * CLIError — structured error class for the Hoox CLI.
3
- * Provides exit codes, optional details, and recoverable flag.
4
- */
5
-
6
- export const enum ExitCode {
7
- SUCCESS = 0,
8
- ERROR = 1,
9
- INVALID_USAGE = 2,
10
- INFRA_UNAVAILABLE = 3,
11
- }
12
-
13
- export class CLIError extends Error {
14
- constructor(
15
- message: string,
16
- public readonly code: ExitCode = ExitCode.ERROR,
17
- public readonly details?: string,
18
- public readonly recoverable = false
19
- ) {
20
- super(message);
21
- this.name = "CLIError";
22
- }
23
- }
@@ -1,180 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach, mock } from "bun:test";
2
- import {
3
- formatSuccess,
4
- formatError,
5
- formatTable,
6
- formatJson,
7
- formatKeyValue,
8
- } from "./formatters.js";
9
- import { CLIError, ExitCode } from "./errors.js";
10
-
11
- /**
12
- * Helper: captures everything written to process.stdout.write into a string.
13
- * Returns a cleanup function that restores the original write.
14
- */
15
- function captureStdout(): { output: () => string; restore: () => void } {
16
- const chunks: string[] = [];
17
- const originalWrite = process.stdout.write.bind(process.stdout);
18
- const writeMock = mock((chunk: string | Buffer) => {
19
- chunks.push(typeof chunk === "string" ? chunk : chunk.toString());
20
- return true;
21
- });
22
- process.stdout.write = writeMock as unknown as typeof process.stdout.write;
23
-
24
- return {
25
- output: () => chunks.join(""),
26
- restore: () => {
27
- process.stdout.write = originalWrite;
28
- },
29
- };
30
- }
31
-
32
- describe("formatSuccess", () => {
33
- let capture: ReturnType<typeof captureStdout>;
34
-
35
- beforeEach(() => {
36
- capture = captureStdout();
37
- });
38
-
39
- afterEach(() => {
40
- capture.restore();
41
- });
42
-
43
- it("outputs human-formatted success", () => {
44
- formatSuccess("Deploy complete");
45
- const out = capture.output();
46
- expect(out).toContain("✓");
47
-
48
- // Check status indicator is present (just the checkmark)
49
- expect(out).toContain("✓");
50
- expect(out).not.toContain("✗");
51
- });
52
- });
53
-
54
- describe("formatTable", () => {
55
- let capture: ReturnType<typeof captureStdout>;
56
-
57
- beforeEach(() => {
58
- capture = captureStdout();
59
- });
60
-
61
- afterEach(() => {
62
- capture.restore();
63
- });
64
-
65
- const sampleRows = [
66
- { name: "alpha", status: "ok" },
67
- { name: "beta-long", status: "error" },
68
- ];
69
-
70
- it("outputs human-formatted table with box drawing", () => {
71
- formatTable(sampleRows);
72
- const out = capture.output();
73
- expect(out).toContain("┌");
74
- expect(out).toContain("┬");
75
- expect(out).toContain("┐");
76
- expect(out).toContain("├");
77
- expect(out).toContain("┼");
78
- expect(out).toContain("┤");
79
- expect(out).toContain("└");
80
- expect(out).toContain("┴");
81
- expect(out).toContain("┘");
82
- expect(out).toContain("alpha");
83
- expect(out).toContain("beta-long");
84
- expect(out).toContain("ok");
85
- expect(out).toContain("error");
86
- });
87
-
88
- it("outputs JSON array when json=true", () => {
89
- formatTable(sampleRows, { json: true });
90
- const out = capture.output();
91
- const parsed = JSON.parse(out);
92
- expect(parsed).toEqual(sampleRows);
93
- });
94
-
95
- it("outputs nothing when quiet=true", () => {
96
- formatTable(sampleRows, { quiet: true });
97
- expect(capture.output()).toBe("");
98
- });
99
-
100
- it("outputs (empty) for empty rows", () => {
101
- formatTable([]);
102
- const out = capture.output();
103
- expect(out).toContain("(empty)");
104
- expect(out).not.toContain("┌");
105
- });
106
- });
107
-
108
- describe("formatJson", () => {
109
- let capture: ReturnType<typeof captureStdout>;
110
-
111
- beforeEach(() => {
112
- capture = captureStdout();
113
- });
114
-
115
- afterEach(() => {
116
- capture.restore();
117
- });
118
-
119
- it("outputs pretty JSON by default", () => {
120
- formatJson({ key: "value" });
121
- const out = capture.output();
122
- const parsed = JSON.parse(out);
123
- expect(parsed).toEqual({ key: "value" });
124
- // Pretty-printed: contains newlines and indentation
125
- expect(out).toContain("\n");
126
- expect(out).toContain('"key"');
127
- });
128
-
129
- it("outputs compact JSON when quiet=true", () => {
130
- formatJson({ key: "value" }, { quiet: true });
131
- const out = capture.output();
132
- const parsed = JSON.parse(out);
133
- expect(parsed).toEqual({ key: "value" });
134
- // Compact: single line of content (console.log appends newline)
135
- expect(out.trim()).not.toContain("\n");
136
- });
137
-
138
- it("outputs pretty JSON when json=true", () => {
139
- formatJson({ key: "value" }, { json: true });
140
- const out = capture.output();
141
- expect(out).toContain("\n");
142
- });
143
- });
144
-
145
- describe("formatKeyValue", () => {
146
- let capture: ReturnType<typeof captureStdout>;
147
-
148
- beforeEach(() => {
149
- capture = captureStdout();
150
- });
151
-
152
- afterEach(() => {
153
- capture.restore();
154
- });
155
-
156
- const pairs = { Name: "alpha", Status: "running", Region: "us-east" };
157
-
158
- it("outputs human-formatted key-value pairs", () => {
159
- formatKeyValue(pairs);
160
- const out = capture.output();
161
- expect(out).toContain("Name");
162
- expect(out).toContain("alpha");
163
- expect(out).toContain("Status");
164
- expect(out).toContain("running");
165
- expect(out).toContain("Region");
166
- expect(out).toContain("us-east");
167
- });
168
-
169
- it("outputs JSON object when json=true", () => {
170
- formatKeyValue(pairs, { json: true });
171
- const out = capture.output();
172
- const parsed = JSON.parse(out);
173
- expect(parsed).toEqual(pairs);
174
- });
175
-
176
- it("outputs nothing when quiet=true", () => {
177
- formatKeyValue(pairs, { quiet: true });
178
- expect(capture.output()).toBe("");
179
- });
180
- });
@@ -1,269 +0,0 @@
1
- /**
2
- * Output formatters for the Hoox CLI.
3
- * Each function respects --json / --quiet flags passed via FormatOptions.
4
- * Human-readable mode uses ansis styling from the theme.
5
- */
6
-
7
- import { CLIError, ExitCode } from "./errors.js";
8
- import { theme, icons, stripAnsi, hr } from "./theme.js";
9
- import { Command } from "commander";
10
-
11
- export interface FormatOptions {
12
- json?: boolean;
13
- quiet?: boolean;
14
- }
15
-
16
- // ── Progress bar ──────────────────────────────────────────────────
17
-
18
- const PROGRESS_BAR_WIDTH = 30;
19
-
20
- /**
21
- * Render an inline progress bar string.
22
- * @param current Current step (0-based)
23
- * @param total Total steps
24
- * @param opts Width override
25
- */
26
- export function renderProgressBar(
27
- current: number,
28
- total: number,
29
- opts?: { width?: number }
30
- ): string {
31
- const width = opts?.width ?? PROGRESS_BAR_WIDTH;
32
- const ratio = total > 0 ? Math.min(current / total, 1) : 0;
33
- const filled = Math.round(width * ratio);
34
- const empty = width - filled;
35
- const pct = Math.round(ratio * 100);
36
-
37
- const bar =
38
- theme.accent("[") +
39
- theme.success("█".repeat(filled)) +
40
- theme.dim("░".repeat(empty)) +
41
- theme.accent("]") +
42
- ` ${pct}%`;
43
-
44
- return bar;
45
- }
46
-
47
- /**
48
- * Render a multi-line progress display for sequential steps.
49
- * Returns lines suitable for overwriting via \r or manual clear.
50
- */
51
- export function renderStepProgress(
52
- steps: Array<{
53
- name: string;
54
- status: "pending" | "running" | "done" | "failed";
55
- }>
56
- ): string {
57
- const iconMap: Record<string, string> = {
58
- pending: theme.dim("○"),
59
- running: theme.info("◌"),
60
- done: theme.success("●"),
61
- failed: theme.error("✗"),
62
- };
63
-
64
- return steps
65
- .map((s) => ` ${iconMap[s.status]} ${theme.bold(s.name)}`)
66
- .join("\n");
67
- }
68
-
69
- // ── Basic output ──────────────────────────────────────────────────
70
-
71
- /**
72
- * Output a success message.
73
- * - JSON mode: {"success":true,"message":"..."}
74
- * - Quiet mode: prints nothing
75
- * - Human mode: green "✓ message"
76
- */
77
- export function formatSuccess(message: string, opts?: FormatOptions): void {
78
- if (opts?.quiet) return;
79
-
80
- if (opts?.json) {
81
- process.stdout.write(JSON.stringify({ success: true, message }) + "\n");
82
- return;
83
- }
84
-
85
- process.stdout.write(`${theme.success(icons.success)} ${message}\n`);
86
- }
87
-
88
- /**
89
- * Output an error message.
90
- * - JSON mode: {"success":false,"error":"...","code":1,"details":"..."}
91
- * - Quiet mode: prints only the error message (no icon)
92
- * - Human mode: red "✗ message" + optional details indented
93
- */
94
- export function formatError(error: Error | string, opts?: FormatOptions): void {
95
- const message = typeof error === "string" ? error : error.message;
96
- const cliError = error instanceof CLIError ? error : null;
97
-
98
- if (opts?.json) {
99
- const output: Record<string, unknown> = {
100
- success: false,
101
- error: message,
102
- code: cliError?.code ?? ExitCode.ERROR,
103
- };
104
- if (cliError?.details) {
105
- output.details = cliError.details;
106
- }
107
- process.stdout.write(JSON.stringify(output) + "\n");
108
- return;
109
- }
110
-
111
- if (opts?.quiet) {
112
- process.stdout.write(`${message}\n`);
113
- return;
114
- }
115
-
116
- process.stdout.write(`${theme.error(icons.error)} ${message}\n`);
117
-
118
- if (cliError?.details) {
119
- process.stdout.write(` ${theme.dim(cliError.details)}\n`);
120
- }
121
- }
122
-
123
- /**
124
- * Output tabular data with enhanced box-drawing borders.
125
- * - JSON mode: JSON array of objects
126
- * - Quiet mode: prints nothing
127
- * - Human mode: themed box-drawn table with column headers
128
- */
129
- export function formatTable(
130
- rows: Record<string, string>[],
131
- opts?: FormatOptions
132
- ): void {
133
- if (opts?.quiet) return;
134
-
135
- if (opts?.json) {
136
- process.stdout.write(JSON.stringify(rows, null, 2) + "\n");
137
- return;
138
- }
139
-
140
- if (rows.length === 0) {
141
- process.stdout.write(`${theme.dim("(empty)")}\n`);
142
- return;
143
- }
144
-
145
- // Collect all keys in display order (first row defines column order)
146
- const keys = Object.keys(rows[0]);
147
-
148
- // Calculate column widths
149
- const widths: Record<string, number> = {};
150
- for (const key of keys) {
151
- widths[key] = stripAnsi(key).length;
152
- for (const row of rows) {
153
- const value = row[key] ?? "";
154
- const stripped = stripAnsi(value).length;
155
- widths[key] = Math.max(widths[key], stripped);
156
- }
157
- }
158
-
159
- // Build border segments
160
- const topParts = keys.map((k) => "─".repeat(widths[k] + 2));
161
- const topBorder = `┌${topParts.join("┬")}┐`;
162
- const sepBorder = `├${topParts.join("┼")}┤`;
163
- const botBorder = `└${topParts.join("┴")}┘`;
164
-
165
- // Header row with themed styling
166
- const headerCells = keys.map((k) =>
167
- theme.bold(k.padEnd(widths[k])).toString()
168
- );
169
- const headerRow = `│ ${headerCells.join(" │ ")} │`;
170
-
171
- // Data rows
172
- const dataRows = rows.map((row) => {
173
- const cells = keys.map((k) => {
174
- const value = row[k] ?? "";
175
- return value.padEnd(widths[k]);
176
- });
177
- return `│ ${cells.join(" │ ")} │`;
178
- });
179
-
180
- process.stdout.write(
181
- `${theme.dim(topBorder)}\n` +
182
- `${headerRow}\n` +
183
- `${theme.dim(sepBorder)}\n` +
184
- `${dataRows.join("\n")}\n` +
185
- `${theme.dim(botBorder)}\n`
186
- );
187
- }
188
-
189
- /**
190
- * Output arbitrary data as JSON.
191
- * - JSON / Quiet mode: JSON stringified (compact or pretty)
192
- * - Human mode: JSON with 2-space indent
193
- */
194
- export function formatJson(data: unknown, opts?: FormatOptions): void {
195
- const json = opts?.quiet
196
- ? JSON.stringify(data)
197
- : JSON.stringify(data, null, 2);
198
-
199
- process.stdout.write(`${json}\n`);
200
- }
201
-
202
- /**
203
- * Output key-value pairs.
204
- * - JSON mode: JSON object
205
- * - Quiet mode: prints nothing
206
- * - Human mode: "label: value" with themed labels
207
- */
208
- export function formatKeyValue(
209
- pairs: Record<string, string>,
210
- opts?: FormatOptions
211
- ): void {
212
- if (opts?.quiet) return;
213
-
214
- if (opts?.json) {
215
- process.stdout.write(JSON.stringify(pairs, null, 2) + "\n");
216
- return;
217
- }
218
-
219
- const maxKeyLen = Math.max(
220
- ...Object.keys(pairs).map((k) => stripAnsi(k).length)
221
- );
222
-
223
- for (const [key, value] of Object.entries(pairs)) {
224
- const paddedKey = key.padEnd(maxKeyLen);
225
- process.stdout.write(
226
- ` ${theme.key(paddedKey)} ${theme.dim(":")} ${value}\n`
227
- );
228
- }
229
- }
230
-
231
- /**
232
- * Output a section header with decorative separator.
233
- */
234
- export function formatHeader(text: string, opts?: FormatOptions): void {
235
- if (opts?.quiet) return;
236
- if (opts?.json) return;
237
-
238
- process.stdout.write(`\n${theme.heading(text)}\n`);
239
- process.stdout.write(`${hr()}\n`);
240
- }
241
-
242
- /**
243
- * Output a bullet-point list with optional icon per item.
244
- */
245
- export function formatList(
246
- items: string[],
247
- opts?: { icon?: string; json?: boolean; quiet?: boolean }
248
- ): void {
249
- if (opts?.quiet) return;
250
-
251
- if (opts?.json) {
252
- process.stdout.write(JSON.stringify(items, null, 2) + "\n");
253
- return;
254
- }
255
-
256
- const icon = opts?.icon ?? icons.arrow;
257
- for (const item of items) {
258
- process.stdout.write(` ${icon} ${item}\n`);
259
- }
260
- }
261
-
262
- /**
263
- * Build the format options for output, reading global --json / --quiet flags.
264
- * Uses `optsWithGlobals()` to include options inherited from the top-level program.
265
- */
266
- export function getFormatOptions(cmd: Command): FormatOptions {
267
- const opts = cmd.optsWithGlobals();
268
- return { json: Boolean(opts.json), quiet: Boolean(opts.quiet) };
269
- }
@@ -1,94 +0,0 @@
1
- /**
2
- * Hoox CLI theme — ansis color palette, icons, and visual primitives.
3
- * Used by formatters and commands for consistent terminal output.
4
- */
5
-
6
- import ansis from "ansis";
7
-
8
- // ── Color primitives ──────────────────────────────────────────────
9
- const green = ansis.green;
10
- const red = ansis.red;
11
- const yellow = ansis.yellow;
12
- const blue = ansis.blue;
13
- const cyan = ansis.cyan;
14
- const magenta = ansis.magenta;
15
- const dim = ansis.dim;
16
- const bold = ansis.bold;
17
- const italic = ansis.italic;
18
- const underline = ansis.underline;
19
- const gray = ansis.gray;
20
- const white = ansis.white;
21
- const black = ansis.black;
22
- const bgGreen = ansis.bgGreen;
23
- const bgRed = ansis.bgRed;
24
- const bgYellow = ansis.bgYellow;
25
- const bgBlue = ansis.bgBlue;
26
-
27
- // ── Semantic theme ────────────────────────────────────────────────
28
- export const theme = {
29
- success: green,
30
- error: red,
31
- warning: yellow,
32
- info: blue,
33
- accent: cyan,
34
- highlight: magenta,
35
- dim,
36
- bold,
37
- italic,
38
- underline,
39
- gray,
40
- white,
41
- black,
42
- heading: bold.cyan,
43
- label: dim,
44
- subtle: gray,
45
- muted: dim.gray,
46
- value: bold.white,
47
- key: dim.yellow,
48
-
49
- // Status-specific
50
- statusOk: bgGreen.black(" OK "),
51
- statusWarn: bgYellow.black(" WARN "),
52
- statusError: bgRed.white(" FAIL "),
53
- statusInfo: bgBlue.white(" INFO "),
54
-
55
- // Decorations
56
- separator: dim("─"),
57
- pipe: dim("│"),
58
- corner: dim("┌┐└┘"),
59
- };
60
-
61
- // ── Icons ─────────────────────────────────────────────────────────
62
- export const icons = {
63
- success: "✓",
64
- error: "✗",
65
- warning: "!",
66
- info: "i",
67
- arrow: "->",
68
- spinner: ["-", "\\", "|", "/"],
69
- dot: "●",
70
- check: "v",
71
- cross: "x",
72
- };
73
-
74
- // ── Display helpers ───────────────────────────────────────────────
75
-
76
- /** Strip ANSI escape codes from a string. */
77
- export function stripAnsi(str: string): string {
78
- return ansis.strip(str);
79
- }
80
-
81
- /** Render a horizontal rule using the separator character. */
82
- export function hr(width: number = 60): string {
83
- return theme.separator.repeat(width);
84
- }
85
-
86
- /** Format a key-value pair for aligned output. */
87
- export function kv(key: string, value: string): string {
88
- return `${theme.key(key)} ${theme.dim(":")} ${theme.value(value)}`;
89
- }
90
-
91
- /** Format a labeled value. */
92
- export function tagged(label: string, value: string): string {
93
- return `${theme.key(label)} ${theme.dim(":")} ${theme.value(value)}`;
94
- }