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.
- package/README.md +58 -24
- package/dist/add-HGJCLWED.mjs +14 -0
- package/dist/add-MRGCS3US.mjs +14 -0
- package/dist/chunk-6PYTKGB5.mjs +316 -0
- package/dist/chunk-7TG3EAQ2.mjs +20 -0
- package/dist/chunk-EKMZZRWI.mjs +521 -0
- package/dist/chunk-JON4GCLR.mjs +59 -0
- package/dist/chunk-LOR7QBXX.mjs +3683 -0
- package/dist/chunk-MNMQC36F.mjs +556 -0
- package/dist/chunk-MRKOFVTM.mjs +537 -0
- package/dist/chunk-NTWO2LXB.mjs +52 -0
- package/dist/chunk-QBXHAXHI.mjs +562 -0
- package/dist/chunk-WGHJI3OI.mjs +214 -0
- package/dist/cli.mjs +4245 -0
- package/dist/create-MG7E7PLQ.mjs +10 -0
- package/dist/handle-UG5M2OON.mjs +22 -0
- package/dist/multiline-OHSNFCRG.mjs +40 -0
- package/dist/project-NT3L4FTB.mjs +28 -0
- package/dist/resolver-WSFWKACM.mjs +153 -0
- package/dist/sprint-4VHDLGFN.mjs +37 -0
- package/dist/wizard-LRELAN2J.mjs +196 -0
- package/package.json +19 -28
- package/CHANGELOG.md +0 -94
- package/bin/ralphctl +0 -13
- package/src/ai/executor.ts +0 -973
- package/src/ai/lifecycle.ts +0 -45
- package/src/ai/parser.ts +0 -40
- package/src/ai/permissions.ts +0 -207
- package/src/ai/process-manager.ts +0 -248
- package/src/ai/prompts/index.ts +0 -89
- package/src/ai/rate-limiter.ts +0 -89
- package/src/ai/runner.ts +0 -478
- package/src/ai/session.ts +0 -319
- package/src/ai/task-context.ts +0 -270
- package/src/cli-metadata.ts +0 -7
- package/src/cli.ts +0 -65
- package/src/commands/completion/index.ts +0 -33
- package/src/commands/config/config.ts +0 -58
- package/src/commands/config/index.ts +0 -33
- package/src/commands/dashboard/dashboard.ts +0 -5
- package/src/commands/dashboard/index.ts +0 -6
- package/src/commands/doctor/doctor.ts +0 -271
- package/src/commands/doctor/index.ts +0 -25
- package/src/commands/progress/index.ts +0 -25
- package/src/commands/progress/log.ts +0 -64
- package/src/commands/progress/show.ts +0 -14
- package/src/commands/project/add.ts +0 -336
- package/src/commands/project/index.ts +0 -104
- package/src/commands/project/list.ts +0 -31
- package/src/commands/project/remove.ts +0 -43
- package/src/commands/project/repo.ts +0 -118
- package/src/commands/project/show.ts +0 -49
- package/src/commands/sprint/close.ts +0 -180
- package/src/commands/sprint/context.ts +0 -109
- package/src/commands/sprint/create.ts +0 -60
- package/src/commands/sprint/current.ts +0 -75
- package/src/commands/sprint/delete.ts +0 -72
- package/src/commands/sprint/health.ts +0 -229
- package/src/commands/sprint/ideate.ts +0 -496
- package/src/commands/sprint/index.ts +0 -226
- package/src/commands/sprint/list.ts +0 -86
- package/src/commands/sprint/plan-utils.ts +0 -207
- package/src/commands/sprint/plan.ts +0 -549
- package/src/commands/sprint/refine.ts +0 -359
- package/src/commands/sprint/requirements.ts +0 -58
- package/src/commands/sprint/show.ts +0 -140
- package/src/commands/sprint/start.ts +0 -119
- package/src/commands/sprint/switch.ts +0 -20
- package/src/commands/task/add.ts +0 -316
- package/src/commands/task/import.ts +0 -150
- package/src/commands/task/index.ts +0 -123
- package/src/commands/task/list.ts +0 -145
- package/src/commands/task/next.ts +0 -45
- package/src/commands/task/remove.ts +0 -47
- package/src/commands/task/reorder.ts +0 -45
- package/src/commands/task/show.ts +0 -111
- package/src/commands/task/status.ts +0 -99
- package/src/commands/ticket/add.ts +0 -265
- package/src/commands/ticket/edit.ts +0 -166
- package/src/commands/ticket/index.ts +0 -114
- package/src/commands/ticket/list.ts +0 -128
- package/src/commands/ticket/refine-utils.ts +0 -89
- package/src/commands/ticket/refine.ts +0 -268
- package/src/commands/ticket/remove.ts +0 -48
- package/src/commands/ticket/show.ts +0 -74
- package/src/completion/handle.ts +0 -30
- package/src/completion/resolver.ts +0 -241
- package/src/interactive/dashboard.ts +0 -268
- package/src/interactive/escapable.ts +0 -81
- package/src/interactive/file-browser.ts +0 -153
- package/src/interactive/index.ts +0 -429
- package/src/interactive/menu.ts +0 -403
- package/src/interactive/selectors.ts +0 -273
- package/src/interactive/wizard.ts +0 -221
- package/src/providers/claude.ts +0 -53
- package/src/providers/copilot.ts +0 -86
- package/src/providers/index.ts +0 -43
- package/src/providers/types.ts +0 -85
- package/src/schemas/index.ts +0 -130
- package/src/store/config.ts +0 -74
- package/src/store/progress.ts +0 -230
- package/src/store/project.ts +0 -276
- package/src/store/sprint.ts +0 -229
- package/src/store/task.ts +0 -443
- package/src/store/ticket.ts +0 -178
- package/src/theme/index.ts +0 -215
- package/src/theme/ui.ts +0 -872
- package/src/utils/detect-scripts.ts +0 -247
- package/src/utils/editor-input.ts +0 -41
- package/src/utils/editor.ts +0 -37
- package/src/utils/exit-codes.ts +0 -27
- package/src/utils/file-lock.ts +0 -135
- package/src/utils/git.ts +0 -185
- package/src/utils/ids.ts +0 -37
- package/src/utils/issue-fetch.ts +0 -244
- package/src/utils/json-extract.ts +0 -62
- package/src/utils/multiline.ts +0 -61
- package/src/utils/path-selector.ts +0 -236
- package/src/utils/paths.ts +0 -108
- package/src/utils/provider.ts +0 -34
- package/src/utils/requirements-export.ts +0 -63
- package/src/utils/storage.ts +0 -107
- package/tsconfig.json +0 -25
- /package/{src/ai → dist}/prompts/ideate-auto.md +0 -0
- /package/{src/ai → dist}/prompts/ideate.md +0 -0
- /package/{src/ai → dist}/prompts/plan-auto.md +0 -0
- /package/{src/ai → dist}/prompts/plan-common.md +0 -0
- /package/{src/ai → dist}/prompts/plan-interactive.md +0 -0
- /package/{src/ai → dist}/prompts/task-execution.md +0 -0
- /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
|
+
};
|