ralphctl 0.1.0 → 0.1.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 (130) hide show
  1. package/README.md +58 -24
  2. package/dist/add-HGJCLWED.mjs +14 -0
  3. package/dist/add-MRGCS3US.mjs +14 -0
  4. package/dist/chunk-6PYTKGB5.mjs +316 -0
  5. package/dist/chunk-7TG3EAQ2.mjs +20 -0
  6. package/dist/chunk-EKMZZRWI.mjs +521 -0
  7. package/dist/chunk-JON4GCLR.mjs +59 -0
  8. package/dist/chunk-LOR7QBXX.mjs +3683 -0
  9. package/dist/chunk-MNMQC36F.mjs +556 -0
  10. package/dist/chunk-MRKOFVTM.mjs +537 -0
  11. package/dist/chunk-NTWO2LXB.mjs +52 -0
  12. package/dist/chunk-QBXHAXHI.mjs +562 -0
  13. package/dist/chunk-WGHJI3OI.mjs +214 -0
  14. package/dist/cli.mjs +4245 -0
  15. package/dist/create-MG7E7PLQ.mjs +10 -0
  16. package/dist/handle-UG5M2OON.mjs +22 -0
  17. package/dist/multiline-OHSNFCRG.mjs +40 -0
  18. package/dist/project-NT3L4FTB.mjs +28 -0
  19. package/dist/resolver-WSFWKACM.mjs +153 -0
  20. package/dist/sprint-4VHDLGFN.mjs +37 -0
  21. package/dist/wizard-LRELAN2J.mjs +196 -0
  22. package/package.json +19 -28
  23. package/CHANGELOG.md +0 -94
  24. package/bin/ralphctl +0 -13
  25. package/src/ai/executor.ts +0 -973
  26. package/src/ai/lifecycle.ts +0 -45
  27. package/src/ai/parser.ts +0 -40
  28. package/src/ai/permissions.ts +0 -207
  29. package/src/ai/process-manager.ts +0 -248
  30. package/src/ai/prompts/index.ts +0 -89
  31. package/src/ai/rate-limiter.ts +0 -89
  32. package/src/ai/runner.ts +0 -478
  33. package/src/ai/session.ts +0 -319
  34. package/src/ai/task-context.ts +0 -270
  35. package/src/cli-metadata.ts +0 -7
  36. package/src/cli.ts +0 -65
  37. package/src/commands/completion/index.ts +0 -33
  38. package/src/commands/config/config.ts +0 -58
  39. package/src/commands/config/index.ts +0 -33
  40. package/src/commands/dashboard/dashboard.ts +0 -5
  41. package/src/commands/dashboard/index.ts +0 -6
  42. package/src/commands/doctor/doctor.ts +0 -271
  43. package/src/commands/doctor/index.ts +0 -25
  44. package/src/commands/progress/index.ts +0 -25
  45. package/src/commands/progress/log.ts +0 -64
  46. package/src/commands/progress/show.ts +0 -14
  47. package/src/commands/project/add.ts +0 -336
  48. package/src/commands/project/index.ts +0 -104
  49. package/src/commands/project/list.ts +0 -31
  50. package/src/commands/project/remove.ts +0 -43
  51. package/src/commands/project/repo.ts +0 -118
  52. package/src/commands/project/show.ts +0 -49
  53. package/src/commands/sprint/close.ts +0 -180
  54. package/src/commands/sprint/context.ts +0 -109
  55. package/src/commands/sprint/create.ts +0 -60
  56. package/src/commands/sprint/current.ts +0 -75
  57. package/src/commands/sprint/delete.ts +0 -72
  58. package/src/commands/sprint/health.ts +0 -229
  59. package/src/commands/sprint/ideate.ts +0 -496
  60. package/src/commands/sprint/index.ts +0 -226
  61. package/src/commands/sprint/list.ts +0 -86
  62. package/src/commands/sprint/plan-utils.ts +0 -207
  63. package/src/commands/sprint/plan.ts +0 -549
  64. package/src/commands/sprint/refine.ts +0 -359
  65. package/src/commands/sprint/requirements.ts +0 -58
  66. package/src/commands/sprint/show.ts +0 -140
  67. package/src/commands/sprint/start.ts +0 -119
  68. package/src/commands/sprint/switch.ts +0 -20
  69. package/src/commands/task/add.ts +0 -316
  70. package/src/commands/task/import.ts +0 -150
  71. package/src/commands/task/index.ts +0 -123
  72. package/src/commands/task/list.ts +0 -145
  73. package/src/commands/task/next.ts +0 -45
  74. package/src/commands/task/remove.ts +0 -47
  75. package/src/commands/task/reorder.ts +0 -45
  76. package/src/commands/task/show.ts +0 -111
  77. package/src/commands/task/status.ts +0 -99
  78. package/src/commands/ticket/add.ts +0 -265
  79. package/src/commands/ticket/edit.ts +0 -166
  80. package/src/commands/ticket/index.ts +0 -114
  81. package/src/commands/ticket/list.ts +0 -128
  82. package/src/commands/ticket/refine-utils.ts +0 -89
  83. package/src/commands/ticket/refine.ts +0 -268
  84. package/src/commands/ticket/remove.ts +0 -48
  85. package/src/commands/ticket/show.ts +0 -74
  86. package/src/completion/handle.ts +0 -30
  87. package/src/completion/resolver.ts +0 -241
  88. package/src/interactive/dashboard.ts +0 -268
  89. package/src/interactive/escapable.ts +0 -81
  90. package/src/interactive/file-browser.ts +0 -153
  91. package/src/interactive/index.ts +0 -429
  92. package/src/interactive/menu.ts +0 -403
  93. package/src/interactive/selectors.ts +0 -273
  94. package/src/interactive/wizard.ts +0 -221
  95. package/src/providers/claude.ts +0 -53
  96. package/src/providers/copilot.ts +0 -86
  97. package/src/providers/index.ts +0 -43
  98. package/src/providers/types.ts +0 -85
  99. package/src/schemas/index.ts +0 -130
  100. package/src/store/config.ts +0 -74
  101. package/src/store/progress.ts +0 -230
  102. package/src/store/project.ts +0 -276
  103. package/src/store/sprint.ts +0 -229
  104. package/src/store/task.ts +0 -443
  105. package/src/store/ticket.ts +0 -178
  106. package/src/theme/index.ts +0 -215
  107. package/src/theme/ui.ts +0 -872
  108. package/src/utils/detect-scripts.ts +0 -247
  109. package/src/utils/editor-input.ts +0 -41
  110. package/src/utils/editor.ts +0 -37
  111. package/src/utils/exit-codes.ts +0 -27
  112. package/src/utils/file-lock.ts +0 -135
  113. package/src/utils/git.ts +0 -185
  114. package/src/utils/ids.ts +0 -37
  115. package/src/utils/issue-fetch.ts +0 -244
  116. package/src/utils/json-extract.ts +0 -62
  117. package/src/utils/multiline.ts +0 -61
  118. package/src/utils/path-selector.ts +0 -236
  119. package/src/utils/paths.ts +0 -108
  120. package/src/utils/provider.ts +0 -34
  121. package/src/utils/requirements-export.ts +0 -63
  122. package/src/utils/storage.ts +0 -107
  123. package/tsconfig.json +0 -25
  124. /package/{src/ai → dist}/prompts/ideate-auto.md +0 -0
  125. /package/{src/ai → dist}/prompts/ideate.md +0 -0
  126. /package/{src/ai → dist}/prompts/plan-auto.md +0 -0
  127. /package/{src/ai → dist}/prompts/plan-common.md +0 -0
  128. /package/{src/ai → dist}/prompts/plan-interactive.md +0 -0
  129. /package/{src/ai → dist}/prompts/task-execution.md +0 -0
  130. /package/{src/ai → dist}/prompts/ticket-refine.md +0 -0
@@ -0,0 +1,562 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/theme/index.ts
4
+ import { bold, cyan, dim, gray, green, magenta, red, yellow } from "colorette";
5
+ import gradient from "gradient-string";
6
+ import { cyan as cyan2, green as green2, red as red2, yellow as yellow2, blue, gray as gray2, bold as bold2, dim as dim2, isColorSupported } from "colorette";
7
+ var colors = {
8
+ // Semantic colors
9
+ success: green,
10
+ error: red,
11
+ warning: yellow,
12
+ info: cyan,
13
+ muted: gray,
14
+ highlight: yellow,
15
+ accent: bold,
16
+ subtle: dim,
17
+ // Ralph-specific
18
+ primary: yellow,
19
+ secondary: magenta
20
+ };
21
+ var success = (text) => colors.success(text);
22
+ var error = (text) => colors.error(text);
23
+ var warning = (text) => colors.warning(text);
24
+ var info = (text) => colors.info(text);
25
+ var muted = (text) => colors.muted(text);
26
+ var highlight = (text) => colors.highlight(text);
27
+ var gradients = {
28
+ /** Gold → Orange → Hot Pink → Orchid → Violet (Ralph's signature donut warmth) */
29
+ donut: gradient(["#FFD700", "#FFA500", "#FF69B4", "#DA70D6", "#9400D3"], {
30
+ interpolation: "hsv",
31
+ hsvSpin: "short"
32
+ }),
33
+ /** Green → Dark Cyan (success/completion) */
34
+ success: gradient(["#00FF00", "#00CED1"]),
35
+ /** Orange Red → Gold (warning/attention) */
36
+ warning: gradient(["#FF4500", "#FFD700"])
37
+ };
38
+ var BANNER = `
39
+ \u{1F369} \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u{1F369}
40
+ \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2551
41
+ \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
42
+ \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
43
+ \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
44
+ \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D
45
+ `;
46
+ var banner = {
47
+ art: BANNER,
48
+ tagline: "I'm helping with your sprints!"
49
+ };
50
+ var RALPH_QUOTES = [
51
+ "I'm helping!",
52
+ "Me fail English? That's unpossible!",
53
+ "Go banana!",
54
+ "Hi, Super Nintendo Chalmers!",
55
+ "I bent my wookie.",
56
+ "My cat's breath smells like cat food.",
57
+ "I'm learnding!",
58
+ "The doctor said I wouldn't have so many nose bleeds if I kept my finger outta there.",
59
+ "I found a moonrock in my nose!",
60
+ "That's where I saw the leprechaun. He told me to burn things.",
61
+ "My daddy's gonna put you in jail!",
62
+ "I'm a unitard!",
63
+ "I ate the purple berries...",
64
+ "Tastes like burning!",
65
+ "My parents won't let me use scissors.",
66
+ "I dress myself!",
67
+ "Principal Skinner, I got carsick in your office.",
68
+ "I'm Idaho!",
69
+ "Mrs. Krabappel and Principal Skinner were in the closet making babies!",
70
+ "Even my boogers are spicy!",
71
+ "It smells like hot dogs.",
72
+ "I sleep in a drawer!",
73
+ "I picked the red one!",
74
+ "The pointy kitty took it!",
75
+ "When I grow up, I want to be a principal or a caterpillar."
76
+ ];
77
+ function getRandomQuote() {
78
+ const index = Math.floor(Math.random() * RALPH_QUOTES.length);
79
+ return RALPH_QUOTES[index] ?? "";
80
+ }
81
+ var QUOTES_BY_CATEGORY = {
82
+ error: [
83
+ "My tummy hurts!",
84
+ "Tastes like burning!",
85
+ "I ate the purple berries...",
86
+ "The doctor said I wouldn't have so many nose bleeds if I kept my finger outta there.",
87
+ "My parents won't let me use scissors.",
88
+ "Principal Skinner, I got carsick in your office.",
89
+ "I eated the purple berries. They taste like... burning."
90
+ ],
91
+ success: [
92
+ "I'm helping!",
93
+ "Go banana!",
94
+ "I'm learnding!",
95
+ "I'm a unitard!",
96
+ "I dress myself!",
97
+ "I picked the red one!",
98
+ "I found a moonrock in my nose!",
99
+ "Yay! I'm a helper!"
100
+ ],
101
+ farewell: [
102
+ "Bye bye! My cat's breath smells like cat food!",
103
+ "When I grow up, I want to be a principal or a caterpillar.",
104
+ "I sleep in a drawer!",
105
+ "I'm Idaho!",
106
+ "The pointy kitty took it!"
107
+ ],
108
+ idle: [
109
+ "Hi, Super Nintendo Chalmers!",
110
+ "I bent my wookie.",
111
+ "My cat's breath smells like cat food.",
112
+ "It smells like hot dogs.",
113
+ "That's where I saw the leprechaun. He told me to burn things.",
114
+ "Me fail English? That's unpossible!",
115
+ "Even my boogers are spicy!",
116
+ "Mrs. Krabappel and Principal Skinner were in the closet making babies!"
117
+ ]
118
+ };
119
+ function getQuoteForContext(category) {
120
+ const quotes = QUOTES_BY_CATEGORY[category];
121
+ const index = Math.floor(Math.random() * quotes.length);
122
+ return quotes[index] ?? "";
123
+ }
124
+ var statusEmoji = {
125
+ todo: "\u{1F4DD}",
126
+ in_progress: "\u{1F3C3}",
127
+ done: "\u2705",
128
+ blocked: "\u{1F6AB}",
129
+ draft: "\u{1F4CB}",
130
+ active: "\u{1F3AF}",
131
+ closed: "\u{1F389}"
132
+ };
133
+ function getStatusEmoji(status) {
134
+ if (status in statusEmoji) {
135
+ return statusEmoji[status];
136
+ }
137
+ return status;
138
+ }
139
+
140
+ // src/theme/ui.ts
141
+ import ora from "ora";
142
+ var emoji = {
143
+ donut: "\u{1F369}"
144
+ };
145
+ var icons = {
146
+ // Entities
147
+ sprint: ">",
148
+ ticket: "#",
149
+ task: "*",
150
+ project: "@",
151
+ // Actions
152
+ edit: ">",
153
+ // Status indicators
154
+ success: "+",
155
+ error: "x",
156
+ warning: "!",
157
+ info: "i",
158
+ tip: "?",
159
+ active: "*",
160
+ inactive: "o",
161
+ bullet: "-"
162
+ };
163
+ var INDENT = " ";
164
+ var log = {
165
+ /** Info message with icon */
166
+ info(message) {
167
+ console.log(`${INDENT}${colors.info(icons.info)} ${message}`);
168
+ },
169
+ /** Success message with icon */
170
+ success(message) {
171
+ console.log(`${INDENT}${colors.success(icons.success)} ${message}`);
172
+ },
173
+ /** Warning message with icon */
174
+ warn(message) {
175
+ console.log(`${INDENT}${colors.warning(icons.warning)} ${message}`);
176
+ },
177
+ /** Error message with icon */
178
+ error(message) {
179
+ console.log(`${INDENT}${colors.error(icons.error)} ${message}`);
180
+ },
181
+ /** Dimmed/muted message */
182
+ dim(message) {
183
+ console.log(`${INDENT}${colors.muted(message)}`);
184
+ },
185
+ /** List item with bullet */
186
+ item(message) {
187
+ console.log(`${INDENT}${INDENT}${colors.muted(icons.bullet)} ${message}`);
188
+ },
189
+ /** Success list item */
190
+ itemSuccess(message) {
191
+ console.log(`${INDENT}${INDENT}${colors.success(icons.success)} ${message}`);
192
+ },
193
+ /** Error list item */
194
+ itemError(message, detail) {
195
+ console.log(`${INDENT}${INDENT}${colors.error(icons.error)} ${message}`);
196
+ if (detail) {
197
+ console.log(`${INDENT}${INDENT} ${colors.muted(detail)}`);
198
+ }
199
+ },
200
+ /** Raw text with indent */
201
+ raw(message, indentLevel = 1) {
202
+ const prefix = INDENT.repeat(indentLevel);
203
+ console.log(`${prefix}${message}`);
204
+ },
205
+ /** Newline for spacing */
206
+ newline() {
207
+ console.log("");
208
+ }
209
+ };
210
+ function printHeader(title, icon) {
211
+ const displayIcon = icon ?? emoji.donut;
212
+ console.log("");
213
+ console.log(` ${displayIcon} ${colors.highlight(title)}`);
214
+ console.log(colors.muted(` ${"\u2500".repeat(40)}`));
215
+ console.log("");
216
+ }
217
+ function printSeparator(width = 40) {
218
+ console.log(`${INDENT}${colors.muted("\u2500".repeat(width))}`);
219
+ }
220
+ var boxChars = {
221
+ /** Light box-drawing (default) */
222
+ light: {
223
+ topLeft: "\u250C",
224
+ topRight: "\u2510",
225
+ bottomLeft: "\u2514",
226
+ bottomRight: "\u2518",
227
+ horizontal: "\u2500",
228
+ vertical: "\u2502",
229
+ teeRight: "\u251C",
230
+ teeLeft: "\u2524",
231
+ teeDown: "\u252C",
232
+ teeUp: "\u2534",
233
+ cross: "\u253C"
234
+ },
235
+ /** Rounded corners */
236
+ rounded: {
237
+ topLeft: "\u256D",
238
+ topRight: "\u256E",
239
+ bottomLeft: "\u2570",
240
+ bottomRight: "\u256F",
241
+ horizontal: "\u2500",
242
+ vertical: "\u2502",
243
+ teeRight: "\u251C",
244
+ teeLeft: "\u2524",
245
+ teeDown: "\u252C",
246
+ teeUp: "\u2534",
247
+ cross: "\u253C"
248
+ },
249
+ /** Heavy box-drawing */
250
+ heavy: {
251
+ topLeft: "\u250F",
252
+ topRight: "\u2513",
253
+ bottomLeft: "\u2517",
254
+ bottomRight: "\u251B",
255
+ horizontal: "\u2501",
256
+ vertical: "\u2503",
257
+ teeRight: "\u2523",
258
+ teeLeft: "\u252B",
259
+ teeDown: "\u2533",
260
+ teeUp: "\u253B",
261
+ cross: "\u254B"
262
+ }
263
+ };
264
+ var ANSI_REGEX = /\x1B(?:\[[0-9;]*[A-Za-z]|\][^\x07]*\x07|\([A-Z])/g;
265
+ function stripAnsi(s) {
266
+ return s.replace(ANSI_REGEX, "");
267
+ }
268
+ function sanitizeForDisplay(s) {
269
+ return s.replace(ANSI_REGEX, "");
270
+ }
271
+ var MIN_BOX_WIDTH = 20;
272
+ var DEFAULT_TERMINAL_WIDTH = 80;
273
+ function getTerminalWidth() {
274
+ return process.stdout.columns || DEFAULT_TERMINAL_WIDTH;
275
+ }
276
+ function wrapLine(line, maxWidth) {
277
+ const visible = stripAnsi(line);
278
+ if (visible.length <= maxWidth) return [line];
279
+ const indentMatch = /^(\s*)/.exec(visible);
280
+ const indent = indentMatch?.[1] ?? "";
281
+ const indentLen = indent.length;
282
+ const wrapWidth = maxWidth - indentLen;
283
+ if (wrapWidth <= 0) return [line];
284
+ const words = visible.trimStart().split(/(\s+)/);
285
+ const wrapped = [];
286
+ let current = "";
287
+ for (const word of words) {
288
+ if (current.length + word.length <= wrapWidth) {
289
+ current += word;
290
+ } else if (current.length === 0) {
291
+ for (let i = 0; i < word.length; i += wrapWidth) {
292
+ wrapped.push(indent + word.slice(i, i + wrapWidth));
293
+ }
294
+ } else {
295
+ wrapped.push(indent + current.trimEnd());
296
+ current = word.trimStart();
297
+ }
298
+ }
299
+ if (current.trimEnd().length > 0) {
300
+ wrapped.push(indent + current.trimEnd());
301
+ }
302
+ return wrapped.length > 0 ? wrapped : [line];
303
+ }
304
+ var DETAIL_LABEL_WIDTH = 14;
305
+ function horizontalLine(width, style = "light") {
306
+ return boxChars[style].horizontal.repeat(width);
307
+ }
308
+ function renderCard(title, lines, options = {}) {
309
+ const { style = "rounded", colorFn = colors.muted } = options;
310
+ const chars = boxChars[style];
311
+ const termWidth = getTerminalWidth();
312
+ const maxInnerWidth = Math.max(MIN_BOX_WIDTH, termWidth - 4);
313
+ const safeTitle = sanitizeForDisplay(title);
314
+ const titleWidth = Math.min(safeTitle.length, maxInnerWidth - 2);
315
+ const wrappedLines = lines.flatMap((l) => wrapLine(l, maxInnerWidth - 2));
316
+ const contentWidths = wrappedLines.map((l) => stripAnsi(l).length);
317
+ const innerWidth = Math.min(Math.max(...contentWidths, titleWidth, MIN_BOX_WIDTH) + 2, maxInnerWidth);
318
+ const result = [];
319
+ result.push(colorFn(chars.topLeft + chars.horizontal.repeat(innerWidth) + chars.topRight));
320
+ const titlePad = " ".repeat(Math.max(0, innerWidth - titleWidth - 2));
321
+ result.push(colorFn(chars.vertical) + " " + colors.highlight(safeTitle) + titlePad + " " + colorFn(chars.vertical));
322
+ result.push(colorFn(chars.teeRight + chars.horizontal.repeat(innerWidth) + chars.teeLeft));
323
+ for (const line of wrappedLines) {
324
+ const visibleLen = stripAnsi(line).length;
325
+ const rightPad = " ".repeat(Math.max(0, innerWidth - visibleLen - 2));
326
+ result.push(colorFn(chars.vertical) + " " + line + rightPad + " " + colorFn(chars.vertical));
327
+ }
328
+ result.push(colorFn(chars.bottomLeft + chars.horizontal.repeat(innerWidth) + chars.bottomRight));
329
+ return result.join("\n");
330
+ }
331
+ function showBanner() {
332
+ if (isColorSupported) {
333
+ console.log(gradients.donut.multiline(banner.art));
334
+ } else {
335
+ console.log(banner.art);
336
+ }
337
+ const quote = getRandomQuote();
338
+ console.log(colors.muted(` "${quote}"
339
+ `));
340
+ }
341
+ function field(label, value, labelWidth = 12) {
342
+ const paddedLabel = (label + ":").padEnd(labelWidth);
343
+ return `${INDENT}${colors.muted(paddedLabel)} ${value}`;
344
+ }
345
+ function labelValue(label, value, labelWidth = DETAIL_LABEL_WIDTH) {
346
+ return field(label, value, labelWidth).trimStart();
347
+ }
348
+ function fieldMultiline(label, value, labelWidth = 12) {
349
+ const lines = value.split("\n");
350
+ const paddedLabel = (label + ":").padEnd(labelWidth);
351
+ const indent = INDENT + " ".repeat(labelWidth + 1);
352
+ if (lines.length === 1) {
353
+ return `${INDENT}${colors.muted(paddedLabel)} ${value}`;
354
+ }
355
+ const firstLine = lines[0] ?? "";
356
+ const result = [];
357
+ result.push(`${INDENT}${colors.muted(paddedLabel)} ${firstLine}`);
358
+ for (let i = 1; i < lines.length; i++) {
359
+ const line = lines[i] ?? "";
360
+ result.push(`${indent}${line}`);
361
+ }
362
+ return result.join("\n");
363
+ }
364
+ function formatTaskStatus(status) {
365
+ const emoji2 = getStatusEmoji(status);
366
+ const labels = {
367
+ todo: "To Do",
368
+ in_progress: "In Progress",
369
+ done: "Done"
370
+ };
371
+ const statusColors = {
372
+ todo: colors.muted,
373
+ in_progress: colors.warning,
374
+ done: colors.success
375
+ };
376
+ const colorFn = statusColors[status] ?? colors.muted;
377
+ return colorFn(`${emoji2} ${labels[status] ?? status}`);
378
+ }
379
+ function formatSprintStatus(status) {
380
+ const emoji2 = getStatusEmoji(status);
381
+ const labels = {
382
+ draft: "Draft",
383
+ active: "Active",
384
+ closed: "Closed"
385
+ };
386
+ const statusColors = {
387
+ draft: colors.warning,
388
+ active: colors.success,
389
+ closed: colors.muted
390
+ };
391
+ const colorFn = statusColors[status] ?? colors.muted;
392
+ return colorFn(`${emoji2} ${labels[status] ?? status}`);
393
+ }
394
+ function badge(text, type = "muted") {
395
+ const colorFn = colors[type];
396
+ return colorFn(`[${text}]`);
397
+ }
398
+ function printCountSummary(label, done, total) {
399
+ const percent = total > 0 ? Math.round(done / total * 100) : 0;
400
+ const color = percent === 100 ? colors.success : percent > 50 ? colors.warning : colors.muted;
401
+ printSeparator();
402
+ console.log(`${INDENT}${label} ${color(`${String(done)}/${String(total)} (${String(percent)}%)`)}`);
403
+ }
404
+ function showSuccess(message, details) {
405
+ console.log(`
406
+ ${INDENT}${colors.success(icons.success)} ${colors.success(message)}`);
407
+ if (details) {
408
+ console.log(details.map(([label, value]) => field(label, value)).join("\n"));
409
+ }
410
+ }
411
+ function showError(message) {
412
+ console.log(`
413
+ ${INDENT}${colors.error(icons.error)} ${colors.error(message)}`);
414
+ }
415
+ function showInfo(message) {
416
+ console.log(`${INDENT}${colors.info(icons.info)} ${colors.info(message)}`);
417
+ }
418
+ function showWarning(message) {
419
+ console.log(`${INDENT}${colors.warning(icons.warning)} ${colors.warning(message)}`);
420
+ }
421
+ function showTip(message) {
422
+ console.log(`${INDENT}${colors.muted(icons.tip + " " + message)}`);
423
+ }
424
+ function showEmpty(what, hint) {
425
+ console.log(`
426
+ ${INDENT}${colors.muted(icons.inactive)} ${colors.muted(`No ${what} yet.`)}`);
427
+ if (hint) {
428
+ console.log(`${INDENT} ${colors.muted(icons.tip + " " + hint)}
429
+ `);
430
+ }
431
+ }
432
+ function showNextStep(command, description) {
433
+ const desc = description ? ` ${colors.muted("- " + description)}` : "";
434
+ console.log(`${INDENT}${colors.muted("\u2192")} ${colors.highlight(command)}${desc}`);
435
+ }
436
+ function showNextSteps(steps) {
437
+ for (const [command, description] of steps) {
438
+ showNextStep(command, description);
439
+ }
440
+ }
441
+ function showRandomQuote() {
442
+ const quote = getRandomQuote();
443
+ console.log(colors.muted(` "${quote}"`));
444
+ }
445
+ function createSpinner(text) {
446
+ return ora({
447
+ text,
448
+ color: "yellow",
449
+ prefixText: INDENT,
450
+ // Disable stdin-discarder: it puts stdin in raw mode, which swallows
451
+ // Ctrl+C (byte 0x03) instead of letting the OS deliver a real SIGINT.
452
+ discardStdin: false,
453
+ spinner: {
454
+ interval: 80,
455
+ frames: Array(8).fill(emoji.donut).map((d, i) => i % 2 === 0 ? colors.highlight(d) : colors.muted(d))
456
+ }
457
+ });
458
+ }
459
+ function isTTY() {
460
+ if (!process.stdout.isTTY || process.env["NO_COLOR"]) return false;
461
+ return true;
462
+ }
463
+ function terminalBell() {
464
+ if (isTTY()) {
465
+ process.stdout.write("\x07");
466
+ }
467
+ }
468
+ function clearScreen() {
469
+ if (isTTY()) {
470
+ process.stdout.write("\x1B[2J\x1B[0f");
471
+ }
472
+ }
473
+ function progressBar(done, total, options = {}) {
474
+ const { width = 20, filled = "\u2588", empty = "\u2591", showPercent = true } = options;
475
+ if (total === 0 || width <= 0) return colors.muted("\u2500".repeat(Math.max(0, width)));
476
+ const filledCount = Math.round(done / total * width);
477
+ const emptyCount = width - filledCount;
478
+ const percent = Math.round(done / total * 100);
479
+ const bar = colors.success(filled.repeat(filledCount)) + colors.muted(empty.repeat(emptyCount));
480
+ if (!showPercent) return bar;
481
+ const label = percent === 100 ? colors.success(`${String(percent)}%`) : colors.muted(`${String(percent)}%`);
482
+ return `${bar} ${label}`;
483
+ }
484
+ function renderTable(columns, rows, options = {}) {
485
+ const { style = "rounded", indent = 2, colorFn = colors.muted } = options;
486
+ const chars = boxChars[style];
487
+ const pad = " ".repeat(indent);
488
+ const colWidths = columns.map((col, i) => {
489
+ const headerWidth = col.header.length;
490
+ const dataWidth = Math.max(0, ...rows.map((row) => stripAnsi(row[i] ?? "").length));
491
+ return Math.max(headerWidth, dataWidth, col.minWidth ?? 0);
492
+ });
493
+ const result = [];
494
+ const topLine = colWidths.map((w) => chars.horizontal.repeat(w + 2)).join(chars.teeDown);
495
+ result.push(pad + colorFn(chars.topLeft + topLine + chars.topRight));
496
+ const headerCells = columns.map((col, i) => {
497
+ const w = colWidths[i] ?? 0;
498
+ return " " + colors.highlight(col.header.padEnd(w)) + " ";
499
+ });
500
+ result.push(pad + colorFn(chars.vertical) + headerCells.join(colorFn(chars.vertical)) + colorFn(chars.vertical));
501
+ const sepLine = colWidths.map((w) => chars.horizontal.repeat(w + 2)).join(chars.cross);
502
+ result.push(pad + colorFn(chars.teeRight + sepLine + chars.teeLeft));
503
+ for (const row of rows) {
504
+ const cells = columns.map((col, i) => {
505
+ const w = colWidths[i] ?? 0;
506
+ const cell = row[i] ?? "";
507
+ const visibleLen = stripAnsi(cell).length;
508
+ const padding = Math.max(0, w - visibleLen);
509
+ const coloredCell = col.color ? col.color(cell) : cell;
510
+ if (col.align === "right") {
511
+ return " " + " ".repeat(padding) + coloredCell + " ";
512
+ }
513
+ return " " + coloredCell + " ".repeat(padding) + " ";
514
+ });
515
+ result.push(pad + colorFn(chars.vertical) + cells.join(colorFn(chars.vertical)) + colorFn(chars.vertical));
516
+ }
517
+ const bottomLine = colWidths.map((w) => chars.horizontal.repeat(w + 2)).join(chars.teeUp);
518
+ result.push(pad + colorFn(chars.bottomLeft + bottomLine + chars.bottomRight));
519
+ return result.join("\n");
520
+ }
521
+
522
+ export {
523
+ colors,
524
+ success,
525
+ error,
526
+ warning,
527
+ info,
528
+ muted,
529
+ highlight,
530
+ getQuoteForContext,
531
+ emoji,
532
+ icons,
533
+ log,
534
+ printHeader,
535
+ printSeparator,
536
+ boxChars,
537
+ DETAIL_LABEL_WIDTH,
538
+ horizontalLine,
539
+ renderCard,
540
+ showBanner,
541
+ field,
542
+ labelValue,
543
+ fieldMultiline,
544
+ formatTaskStatus,
545
+ formatSprintStatus,
546
+ badge,
547
+ printCountSummary,
548
+ showSuccess,
549
+ showError,
550
+ showInfo,
551
+ showWarning,
552
+ showTip,
553
+ showEmpty,
554
+ showNextStep,
555
+ showNextSteps,
556
+ showRandomQuote,
557
+ createSpinner,
558
+ terminalBell,
559
+ clearScreen,
560
+ progressBar,
561
+ renderTable
562
+ };