@entro314labs/ai-changelog-generator 3.1.1 → 3.2.1
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/CHANGELOG.md +412 -875
- package/README.md +8 -3
- package/ai-changelog-mcp.sh +0 -0
- package/ai-changelog.sh +0 -0
- package/bin/ai-changelog-dxt.js +9 -9
- package/bin/ai-changelog-mcp.js +19 -17
- package/bin/ai-changelog.js +6 -6
- package/package.json +80 -48
- package/src/ai-changelog-generator.js +91 -81
- package/src/application/orchestrators/changelog.orchestrator.js +791 -516
- package/src/application/services/application.service.js +137 -128
- package/src/cli.js +76 -57
- package/src/domains/ai/ai-analysis.service.js +289 -209
- package/src/domains/analysis/analysis.engine.js +328 -192
- package/src/domains/changelog/changelog.service.js +1174 -783
- package/src/domains/changelog/workspace-changelog.service.js +487 -249
- package/src/domains/git/git-repository.analyzer.js +348 -258
- package/src/domains/git/git.service.js +132 -112
- package/src/infrastructure/cli/cli.controller.js +390 -274
- package/src/infrastructure/config/configuration.manager.js +220 -190
- package/src/infrastructure/interactive/interactive-staging.service.js +154 -135
- package/src/infrastructure/interactive/interactive-workflow.service.js +200 -159
- package/src/infrastructure/mcp/mcp-server.service.js +208 -207
- package/src/infrastructure/metrics/metrics.collector.js +140 -123
- package/src/infrastructure/providers/core/base-provider.js +87 -40
- package/src/infrastructure/providers/implementations/anthropic.js +101 -99
- package/src/infrastructure/providers/implementations/azure.js +124 -101
- package/src/infrastructure/providers/implementations/bedrock.js +136 -126
- package/src/infrastructure/providers/implementations/dummy.js +23 -23
- package/src/infrastructure/providers/implementations/google.js +123 -114
- package/src/infrastructure/providers/implementations/huggingface.js +94 -87
- package/src/infrastructure/providers/implementations/lmstudio.js +75 -60
- package/src/infrastructure/providers/implementations/mock.js +69 -73
- package/src/infrastructure/providers/implementations/ollama.js +89 -66
- package/src/infrastructure/providers/implementations/openai.js +88 -89
- package/src/infrastructure/providers/implementations/vertex.js +227 -197
- package/src/infrastructure/providers/provider-management.service.js +245 -207
- package/src/infrastructure/providers/provider-manager.service.js +145 -125
- package/src/infrastructure/providers/utils/base-provider-helpers.js +308 -302
- package/src/infrastructure/providers/utils/model-config.js +220 -195
- package/src/infrastructure/providers/utils/provider-utils.js +105 -100
- package/src/infrastructure/validation/commit-message-validation.service.js +259 -161
- package/src/shared/constants/colors.js +453 -180
- package/src/shared/utils/cli-demo.js +285 -0
- package/src/shared/utils/cli-entry-utils.js +257 -249
- package/src/shared/utils/cli-ui.js +447 -0
- package/src/shared/utils/diff-processor.js +513 -0
- package/src/shared/utils/error-classes.js +125 -156
- package/src/shared/utils/json-utils.js +93 -89
- package/src/shared/utils/utils.js +1117 -945
- package/types/index.d.ts +353 -344
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
// ANSI escape codes for colors and styles
|
|
7
|
+
import process from 'node:process'
|
|
8
|
+
|
|
7
9
|
const ansiColors = {
|
|
8
10
|
// Reset
|
|
9
11
|
reset: '\x1b[0m',
|
|
10
|
-
|
|
12
|
+
|
|
11
13
|
// Text colors
|
|
12
14
|
black: '\x1b[30m',
|
|
13
15
|
red: '\x1b[31m',
|
|
@@ -19,7 +21,7 @@ const ansiColors = {
|
|
|
19
21
|
white: '\x1b[37m',
|
|
20
22
|
gray: '\x1b[90m',
|
|
21
23
|
grey: '\x1b[90m',
|
|
22
|
-
|
|
24
|
+
|
|
23
25
|
// Bright colors
|
|
24
26
|
redBright: '\x1b[91m',
|
|
25
27
|
greenBright: '\x1b[92m',
|
|
@@ -28,175 +30,178 @@ const ansiColors = {
|
|
|
28
30
|
magentaBright: '\x1b[95m',
|
|
29
31
|
cyanBright: '\x1b[96m',
|
|
30
32
|
whiteBright: '\x1b[97m',
|
|
31
|
-
|
|
33
|
+
|
|
32
34
|
// Styles
|
|
33
35
|
bold: '\x1b[1m',
|
|
34
36
|
dim: '\x1b[2m',
|
|
35
37
|
italic: '\x1b[3m',
|
|
36
38
|
underline: '\x1b[4m',
|
|
37
39
|
inverse: '\x1b[7m',
|
|
38
|
-
strikethrough: '\x1b[9m'
|
|
39
|
-
}
|
|
40
|
+
strikethrough: '\x1b[9m',
|
|
41
|
+
}
|
|
40
42
|
|
|
41
43
|
/**
|
|
42
44
|
* Color utility class
|
|
43
45
|
*/
|
|
44
46
|
class Colors {
|
|
45
47
|
constructor() {
|
|
46
|
-
this.enabled = this.shouldEnableColors()
|
|
47
|
-
this.setupColors()
|
|
48
|
+
this.enabled = this.shouldEnableColors()
|
|
49
|
+
this.setupColors()
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
shouldEnableColors() {
|
|
51
53
|
// Don't use colors if explicitly disabled
|
|
52
54
|
if (process.env.NO_COLOR || process.env.NODE_DISABLE_COLORS) {
|
|
53
|
-
return false
|
|
55
|
+
return false
|
|
54
56
|
}
|
|
55
|
-
|
|
57
|
+
|
|
56
58
|
// Don't use colors if not in a TTY (unless forced)
|
|
57
59
|
if (process.stdout && process.stdout.isTTY === false) {
|
|
58
|
-
return false
|
|
60
|
+
return false
|
|
59
61
|
}
|
|
60
|
-
|
|
62
|
+
|
|
61
63
|
// Don't use colors in dumb terminals
|
|
62
64
|
if (process.env.TERM === 'dumb') {
|
|
63
|
-
return false
|
|
65
|
+
return false
|
|
64
66
|
}
|
|
65
|
-
|
|
67
|
+
|
|
66
68
|
// Enable colors by default
|
|
67
|
-
return true
|
|
69
|
+
return true
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
colorize(color, text) {
|
|
71
|
-
if (!this.enabled
|
|
72
|
-
|
|
73
|
+
if (!(this.enabled && text)) {
|
|
74
|
+
return text
|
|
75
|
+
}
|
|
76
|
+
return `${ansiColors[color] || ''}${text}${ansiColors.reset}`
|
|
73
77
|
}
|
|
74
78
|
|
|
75
79
|
setupColors() {
|
|
76
80
|
if (!this.enabled) {
|
|
77
|
-
this.setupFallbackColors()
|
|
78
|
-
return
|
|
81
|
+
this.setupFallbackColors()
|
|
82
|
+
return
|
|
79
83
|
}
|
|
80
84
|
|
|
81
85
|
// Status colors
|
|
82
|
-
this.success = (text) => this.colorize('green', text)
|
|
83
|
-
this.error = (text) => this.colorize('red', text)
|
|
84
|
-
this.warning = (text) => this.colorize('yellow', text)
|
|
85
|
-
this.info = (text) => this.colorize('blue', text)
|
|
86
|
-
this.secondary = (text) => this.colorize('gray', text)
|
|
87
|
-
this.highlight = (text) => this.colorize('cyan', text)
|
|
88
|
-
this.bold = (text) => this.colorize('bold', text)
|
|
89
|
-
this.dim = (text) => this.colorize('dim', text)
|
|
86
|
+
this.success = (text) => this.colorize('green', text)
|
|
87
|
+
this.error = (text) => this.colorize('red', text)
|
|
88
|
+
this.warning = (text) => this.colorize('yellow', text)
|
|
89
|
+
this.info = (text) => this.colorize('blue', text)
|
|
90
|
+
this.secondary = (text) => this.colorize('gray', text)
|
|
91
|
+
this.highlight = (text) => this.colorize('cyan', text)
|
|
92
|
+
this.bold = (text) => this.colorize('bold', text)
|
|
93
|
+
this.dim = (text) => this.colorize('dim', text)
|
|
90
94
|
|
|
91
95
|
// Semantic colors for changelog
|
|
92
|
-
this.feature = (text) => this.colorize('greenBright', text)
|
|
93
|
-
this.fix = (text) => this.colorize('redBright', text)
|
|
94
|
-
this.security = (text) => this.colorize('magentaBright', text)
|
|
95
|
-
this.breaking = (text) => this.colorize('bold', this.colorize('red', text))
|
|
96
|
-
this.docs = (text) => this.colorize('blueBright', text)
|
|
97
|
-
this.style = (text) => this.colorize('magenta', text)
|
|
98
|
-
this.refactor = (text) => this.colorize('yellow', text)
|
|
99
|
-
this.perf = (text) => this.colorize('cyan', text)
|
|
100
|
-
this.test = (text) => this.colorize('blue', text)
|
|
101
|
-
this.chore = (text) => this.colorize('gray', text)
|
|
96
|
+
this.feature = (text) => this.colorize('greenBright', text)
|
|
97
|
+
this.fix = (text) => this.colorize('redBright', text)
|
|
98
|
+
this.security = (text) => this.colorize('magentaBright', text)
|
|
99
|
+
this.breaking = (text) => this.colorize('bold', this.colorize('red', text))
|
|
100
|
+
this.docs = (text) => this.colorize('blueBright', text)
|
|
101
|
+
this.style = (text) => this.colorize('magenta', text)
|
|
102
|
+
this.refactor = (text) => this.colorize('yellow', text)
|
|
103
|
+
this.perf = (text) => this.colorize('cyan', text)
|
|
104
|
+
this.test = (text) => this.colorize('blue', text)
|
|
105
|
+
this.chore = (text) => this.colorize('gray', text)
|
|
102
106
|
|
|
103
107
|
// UI elements
|
|
104
|
-
this.header = (text) => this.colorize('underline', this.colorize('bold', text))
|
|
105
|
-
this.subheader = (text) => this.colorize('bold', text)
|
|
106
|
-
this.label = (text) => this.colorize('cyan', text)
|
|
107
|
-
this.value = (text) => this.colorize('white', text)
|
|
108
|
-
this.code = (text) => this.colorize('inverse', text)
|
|
109
|
-
this.file = (text) => this.colorize('yellowBright', text)
|
|
110
|
-
this.path = (text) => this.colorize('green', text)
|
|
111
|
-
this.hash = (text) => this.colorize('magenta', text)
|
|
108
|
+
this.header = (text) => this.colorize('underline', this.colorize('bold', text))
|
|
109
|
+
this.subheader = (text) => this.colorize('bold', text)
|
|
110
|
+
this.label = (text) => this.colorize('cyan', text)
|
|
111
|
+
this.value = (text) => this.colorize('white', text)
|
|
112
|
+
this.code = (text) => this.colorize('inverse', text)
|
|
113
|
+
this.file = (text) => this.colorize('yellowBright', text)
|
|
114
|
+
this.path = (text) => this.colorize('green', text)
|
|
115
|
+
this.hash = (text) => this.colorize('magenta', text)
|
|
112
116
|
|
|
113
117
|
// Metrics and stats
|
|
114
|
-
this.metric = (text) => this.colorize('cyan', text)
|
|
115
|
-
this.number = (text) => this.colorize('yellowBright', text)
|
|
116
|
-
this.percentage = (text) => this.colorize('green', text)
|
|
118
|
+
this.metric = (text) => this.colorize('cyan', text)
|
|
119
|
+
this.number = (text) => this.colorize('yellowBright', text)
|
|
120
|
+
this.percentage = (text) => this.colorize('green', text)
|
|
117
121
|
|
|
118
122
|
// Risk levels
|
|
119
|
-
this.riskLow = (text) => this.colorize('green', text)
|
|
120
|
-
this.riskMedium = (text) => this.colorize('yellow', text)
|
|
121
|
-
this.riskHigh = (text) => this.colorize('red', text)
|
|
122
|
-
this.riskCritical = (text) =>
|
|
123
|
+
this.riskLow = (text) => this.colorize('green', text)
|
|
124
|
+
this.riskMedium = (text) => this.colorize('yellow', text)
|
|
125
|
+
this.riskHigh = (text) => this.colorize('red', text)
|
|
126
|
+
this.riskCritical = (text) =>
|
|
127
|
+
this.colorize('inverse', this.colorize('bold', this.colorize('red', text)))
|
|
123
128
|
|
|
124
129
|
// Impact levels
|
|
125
|
-
this.impactMinimal = (text) => this.colorize('gray', text)
|
|
126
|
-
this.impactLow = (text) => this.colorize('blue', text)
|
|
127
|
-
this.impactMedium = (text) => this.colorize('yellow', text)
|
|
128
|
-
this.impactHigh = (text) => this.colorize('red', text)
|
|
129
|
-
this.impactCritical = (text) => this.colorize('bold', this.colorize('red', text))
|
|
130
|
+
this.impactMinimal = (text) => this.colorize('gray', text)
|
|
131
|
+
this.impactLow = (text) => this.colorize('blue', text)
|
|
132
|
+
this.impactMedium = (text) => this.colorize('yellow', text)
|
|
133
|
+
this.impactHigh = (text) => this.colorize('red', text)
|
|
134
|
+
this.impactCritical = (text) => this.colorize('bold', this.colorize('red', text))
|
|
130
135
|
}
|
|
131
136
|
|
|
132
137
|
setupFallbackColors() {
|
|
133
138
|
// Fallback to identity functions when colors are disabled
|
|
134
|
-
const identity = (text) => text
|
|
135
|
-
|
|
139
|
+
const identity = (text) => text
|
|
140
|
+
|
|
136
141
|
// Status colors
|
|
137
|
-
this.success = identity
|
|
138
|
-
this.error = identity
|
|
139
|
-
this.warning = identity
|
|
140
|
-
this.info = identity
|
|
141
|
-
this.secondary = identity
|
|
142
|
-
this.highlight = identity
|
|
143
|
-
this.bold = identity
|
|
144
|
-
this.dim = identity
|
|
142
|
+
this.success = identity
|
|
143
|
+
this.error = identity
|
|
144
|
+
this.warning = identity
|
|
145
|
+
this.info = identity
|
|
146
|
+
this.secondary = identity
|
|
147
|
+
this.highlight = identity
|
|
148
|
+
this.bold = identity
|
|
149
|
+
this.dim = identity
|
|
145
150
|
|
|
146
151
|
// Semantic colors for changelog
|
|
147
|
-
this.feature = identity
|
|
148
|
-
this.fix = identity
|
|
149
|
-
this.security = identity
|
|
150
|
-
this.breaking = identity
|
|
151
|
-
this.docs = identity
|
|
152
|
-
this.style = identity
|
|
153
|
-
this.refactor = identity
|
|
154
|
-
this.perf = identity
|
|
155
|
-
this.test = identity
|
|
156
|
-
this.chore = identity
|
|
152
|
+
this.feature = identity
|
|
153
|
+
this.fix = identity
|
|
154
|
+
this.security = identity
|
|
155
|
+
this.breaking = identity
|
|
156
|
+
this.docs = identity
|
|
157
|
+
this.style = identity
|
|
158
|
+
this.refactor = identity
|
|
159
|
+
this.perf = identity
|
|
160
|
+
this.test = identity
|
|
161
|
+
this.chore = identity
|
|
157
162
|
|
|
158
163
|
// UI elements
|
|
159
|
-
this.header = identity
|
|
160
|
-
this.subheader = identity
|
|
161
|
-
this.label = identity
|
|
162
|
-
this.value = identity
|
|
163
|
-
this.code = identity
|
|
164
|
-
this.file = identity
|
|
165
|
-
this.path = identity
|
|
166
|
-
this.hash = identity
|
|
164
|
+
this.header = identity
|
|
165
|
+
this.subheader = identity
|
|
166
|
+
this.label = identity
|
|
167
|
+
this.value = identity
|
|
168
|
+
this.code = identity
|
|
169
|
+
this.file = identity
|
|
170
|
+
this.path = identity
|
|
171
|
+
this.hash = identity
|
|
167
172
|
|
|
168
173
|
// Metrics and stats
|
|
169
|
-
this.metric = identity
|
|
170
|
-
this.number = identity
|
|
171
|
-
this.percentage = identity
|
|
174
|
+
this.metric = identity
|
|
175
|
+
this.number = identity
|
|
176
|
+
this.percentage = identity
|
|
172
177
|
|
|
173
178
|
// Risk levels
|
|
174
|
-
this.riskLow = identity
|
|
175
|
-
this.riskMedium = identity
|
|
176
|
-
this.riskHigh = identity
|
|
177
|
-
this.riskCritical = identity
|
|
179
|
+
this.riskLow = identity
|
|
180
|
+
this.riskMedium = identity
|
|
181
|
+
this.riskHigh = identity
|
|
182
|
+
this.riskCritical = identity
|
|
178
183
|
|
|
179
184
|
// Impact levels
|
|
180
|
-
this.impactMinimal = identity
|
|
181
|
-
this.impactLow = identity
|
|
182
|
-
this.impactMedium = identity
|
|
183
|
-
this.impactHigh = identity
|
|
184
|
-
this.impactCritical = identity
|
|
185
|
+
this.impactMinimal = identity
|
|
186
|
+
this.impactLow = identity
|
|
187
|
+
this.impactMedium = identity
|
|
188
|
+
this.impactHigh = identity
|
|
189
|
+
this.impactCritical = identity
|
|
185
190
|
}
|
|
186
191
|
|
|
187
192
|
disable() {
|
|
188
|
-
this.enabled = false
|
|
189
|
-
this.setupFallbackColors()
|
|
193
|
+
this.enabled = false
|
|
194
|
+
this.setupFallbackColors()
|
|
190
195
|
}
|
|
191
196
|
|
|
192
197
|
enable() {
|
|
193
|
-
this.enabled = true
|
|
194
|
-
this.setupColors()
|
|
198
|
+
this.enabled = true
|
|
199
|
+
this.setupColors()
|
|
195
200
|
}
|
|
196
201
|
|
|
197
202
|
// Utility methods for common patterns
|
|
198
203
|
emoji(text) {
|
|
199
|
-
return text
|
|
204
|
+
return text // Emojis work without colors
|
|
200
205
|
}
|
|
201
206
|
|
|
202
207
|
status(type, message) {
|
|
@@ -204,11 +209,11 @@ class Colors {
|
|
|
204
209
|
success: this.success,
|
|
205
210
|
error: this.error,
|
|
206
211
|
warning: this.warning,
|
|
207
|
-
info: this.info
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const color = colorMap[type] || this.info
|
|
211
|
-
return color(message)
|
|
212
|
+
info: this.info,
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const color = colorMap[type] || this.info
|
|
216
|
+
return color(message)
|
|
212
217
|
}
|
|
213
218
|
|
|
214
219
|
commitType(type) {
|
|
@@ -222,10 +227,10 @@ class Colors {
|
|
|
222
227
|
refactor: this.refactor,
|
|
223
228
|
perf: this.perf,
|
|
224
229
|
test: this.test,
|
|
225
|
-
chore: this.chore
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
return (colorMap[type] || this.secondary)(type)
|
|
230
|
+
chore: this.chore,
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return (colorMap[type] || this.secondary)(type)
|
|
229
234
|
}
|
|
230
235
|
|
|
231
236
|
risk(level) {
|
|
@@ -233,10 +238,10 @@ class Colors {
|
|
|
233
238
|
low: this.riskLow,
|
|
234
239
|
medium: this.riskMedium,
|
|
235
240
|
high: this.riskHigh,
|
|
236
|
-
critical: this.riskCritical
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
return (colorMap[level] || this.secondary)(level.toUpperCase())
|
|
241
|
+
critical: this.riskCritical,
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return (colorMap[level] || this.secondary)(level.toUpperCase())
|
|
240
245
|
}
|
|
241
246
|
|
|
242
247
|
impact(level) {
|
|
@@ -245,148 +250,416 @@ class Colors {
|
|
|
245
250
|
low: this.impactLow,
|
|
246
251
|
medium: this.impactMedium,
|
|
247
252
|
high: this.impactHigh,
|
|
248
|
-
critical: this.impactCritical
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
return (colorMap[level] || this.secondary)(level)
|
|
253
|
+
critical: this.impactCritical,
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return (colorMap[level] || this.secondary)(level)
|
|
252
257
|
}
|
|
253
258
|
|
|
254
259
|
// Diff highlighting
|
|
255
260
|
diffAdd(text) {
|
|
256
|
-
return this.success(`+ ${text}`)
|
|
261
|
+
return this.success(`+ ${text}`)
|
|
257
262
|
}
|
|
258
263
|
|
|
259
264
|
diffRemove(text) {
|
|
260
|
-
return this.error(`- ${text}`)
|
|
265
|
+
return this.error(`- ${text}`)
|
|
261
266
|
}
|
|
262
267
|
|
|
263
268
|
diffContext(text) {
|
|
264
|
-
return this.dim(` ${text}`)
|
|
269
|
+
return this.dim(` ${text}`)
|
|
265
270
|
}
|
|
266
271
|
|
|
267
272
|
// Progress indicators
|
|
268
273
|
progress(current, total, label = '') {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
274
|
+
if (total <= 0) {
|
|
275
|
+
return `${this.info('[░░░░░░░░░░░░░░░░░░░░]')} ${this.percentage('0%')} ${label}`
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const percentage = Math.max(0, Math.min(100, Math.round((current / total) * 100)))
|
|
279
|
+
const filledBars = Math.max(0, Math.round(percentage / 5))
|
|
280
|
+
const emptyBars = Math.max(0, 20 - filledBars)
|
|
281
|
+
const bar = '█'.repeat(filledBars) + '░'.repeat(emptyBars)
|
|
282
|
+
return `${this.info(`[${bar}]`)} ${this.percentage(`${percentage}%`)} ${label}`
|
|
272
283
|
}
|
|
273
284
|
|
|
274
285
|
// Helper to get actual visible length of string (without ANSI codes)
|
|
275
286
|
getVisibleLength(str) {
|
|
276
287
|
// Remove ANSI escape sequences to get actual visible length
|
|
277
|
-
return str.replace(/\x1b\[[0-9;]*m/g, '').length
|
|
288
|
+
return str.replace(/\x1b\[[0-9;]*m/g, '').length
|
|
278
289
|
}
|
|
279
290
|
|
|
280
291
|
// Box drawing for sections with dynamic width calculation
|
|
281
292
|
box(title, content, minWidth = 60) {
|
|
282
|
-
const lines = content.split('\n')
|
|
283
|
-
const titleVisibleLength = this.getVisibleLength(title)
|
|
284
|
-
|
|
293
|
+
const lines = content.split('\n')
|
|
294
|
+
const titleVisibleLength = this.getVisibleLength(title)
|
|
295
|
+
|
|
285
296
|
// Calculate required width based on content
|
|
286
297
|
const maxContentLength = Math.max(
|
|
287
298
|
titleVisibleLength + 4, // title + padding
|
|
288
|
-
...lines.map(line => this.getVisibleLength(line) + 4), // content + padding
|
|
299
|
+
...lines.map((line) => this.getVisibleLength(line) + 4), // content + padding
|
|
289
300
|
minWidth
|
|
290
|
-
)
|
|
291
|
-
|
|
292
|
-
const width = Math.min(maxContentLength, 80)
|
|
293
|
-
|
|
294
|
-
const topBorder = '
|
|
295
|
-
const bottomBorder = '
|
|
296
|
-
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
const width = Math.min(maxContentLength, 80) // Cap at 80 chars for readability
|
|
304
|
+
|
|
305
|
+
const topBorder = `┌${'─'.repeat(width - 2)}┐`
|
|
306
|
+
const bottomBorder = `└${'─'.repeat(width - 2)}┘`
|
|
307
|
+
|
|
297
308
|
// Title line with proper padding accounting for ANSI codes
|
|
298
|
-
const titlePadding = width - titleVisibleLength - 3
|
|
299
|
-
const titleLine = `│ ${this.header(title)}${' '.repeat(Math.max(0, titlePadding))}
|
|
300
|
-
|
|
301
|
-
const contentLines = lines.map(line => {
|
|
302
|
-
const visibleLength = this.getVisibleLength(line)
|
|
303
|
-
const padding = width - visibleLength - 4
|
|
304
|
-
return `│ ${line}${' '.repeat(Math.max(0, padding))}
|
|
305
|
-
})
|
|
309
|
+
const titlePadding = width - titleVisibleLength - 3
|
|
310
|
+
const titleLine = `│ ${this.header(title)}${' '.repeat(Math.max(0, titlePadding))}│`
|
|
311
|
+
|
|
312
|
+
const contentLines = lines.map((line) => {
|
|
313
|
+
const visibleLength = this.getVisibleLength(line)
|
|
314
|
+
const padding = width - visibleLength - 4
|
|
315
|
+
return `│ ${line}${' '.repeat(Math.max(0, padding))} │`
|
|
316
|
+
})
|
|
306
317
|
|
|
307
318
|
return [
|
|
308
319
|
this.secondary(topBorder),
|
|
309
320
|
titleLine,
|
|
310
|
-
this.secondary('
|
|
321
|
+
this.secondary(`├${'─'.repeat(width - 2)}┤`),
|
|
311
322
|
...contentLines,
|
|
312
|
-
this.secondary(bottomBorder)
|
|
313
|
-
].join('\n')
|
|
323
|
+
this.secondary(bottomBorder),
|
|
324
|
+
].join('\n')
|
|
314
325
|
}
|
|
315
326
|
|
|
316
327
|
// Quick access to common formatted messages
|
|
317
328
|
successMessage(message) {
|
|
318
|
-
return `${this.success('✅')} ${message}
|
|
329
|
+
return `${this.success('✅')} ${message}`
|
|
319
330
|
}
|
|
320
331
|
|
|
321
332
|
errorMessage(message) {
|
|
322
|
-
return `${this.error('❌')} ${message}
|
|
333
|
+
return `${this.error('❌')} ${message}`
|
|
323
334
|
}
|
|
324
335
|
|
|
325
336
|
warningMessage(message) {
|
|
326
|
-
return `${this.warning('⚠️')} ${message}
|
|
337
|
+
return `${this.warning('⚠️')} ${message}`
|
|
327
338
|
}
|
|
328
339
|
|
|
329
340
|
infoMessage(message) {
|
|
330
|
-
return `${this.info('ℹ️')} ${message}
|
|
341
|
+
return `${this.info('ℹ️')} ${message}`
|
|
331
342
|
}
|
|
332
343
|
|
|
333
344
|
processingMessage(message) {
|
|
334
|
-
return `${this.highlight('🔍')} ${message}
|
|
345
|
+
return `${this.highlight('🔍')} ${message}`
|
|
335
346
|
}
|
|
336
347
|
|
|
337
348
|
aiMessage(message) {
|
|
338
|
-
return `${this.highlight('🤖')} ${message}
|
|
349
|
+
return `${this.highlight('🤖')} ${message}`
|
|
339
350
|
}
|
|
340
351
|
|
|
341
352
|
metricsMessage(message) {
|
|
342
|
-
return `${this.metric('📊')} ${message}
|
|
353
|
+
return `${this.metric('📊')} ${message}`
|
|
343
354
|
}
|
|
344
355
|
|
|
345
356
|
separator(char = '─', length = 50) {
|
|
346
|
-
return this.dim(char.repeat(length))
|
|
357
|
+
return this.dim(char.repeat(length))
|
|
347
358
|
}
|
|
348
359
|
|
|
349
360
|
sectionHeader(text) {
|
|
350
|
-
return this.header(text)
|
|
361
|
+
return this.header(text)
|
|
351
362
|
}
|
|
352
363
|
|
|
353
364
|
// Format file lists with syntax highlighting
|
|
354
365
|
formatFileList(files, maxDisplay = 10) {
|
|
355
|
-
const displayed = files.slice(0, maxDisplay)
|
|
356
|
-
const result = displayed.map(file => {
|
|
357
|
-
const ext = file.split('.').pop()?.toLowerCase()
|
|
358
|
-
let color = this.file
|
|
359
|
-
|
|
366
|
+
const displayed = files.slice(0, maxDisplay)
|
|
367
|
+
const result = displayed.map((file) => {
|
|
368
|
+
const ext = file.split('.').pop()?.toLowerCase()
|
|
369
|
+
let color = this.file
|
|
370
|
+
|
|
360
371
|
// Color by file type
|
|
361
|
-
if (['ts', 'tsx', 'js', 'jsx'].includes(ext))
|
|
362
|
-
|
|
363
|
-
else if (['
|
|
364
|
-
|
|
365
|
-
else if (['
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
372
|
+
if (['ts', 'tsx', 'js', 'jsx'].includes(ext)) {
|
|
373
|
+
color = this.feature
|
|
374
|
+
} else if (['css', 'scss', 'sass'].includes(ext)) {
|
|
375
|
+
color = this.style
|
|
376
|
+
} else if (['md', 'txt'].includes(ext)) {
|
|
377
|
+
color = this.docs
|
|
378
|
+
} else if (['json', 'yaml', 'yml'].includes(ext)) {
|
|
379
|
+
color = this.warning
|
|
380
|
+
} else if (['sql'].includes(ext)) {
|
|
381
|
+
color = this.fix
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return ` - ${color(file)}`
|
|
385
|
+
})
|
|
369
386
|
|
|
370
387
|
if (files.length > maxDisplay) {
|
|
371
|
-
result.push(` ${this.dim(`... and ${files.length - maxDisplay} more`)}`)
|
|
388
|
+
result.push(` ${this.dim(`... and ${files.length - maxDisplay} more`)}`)
|
|
372
389
|
}
|
|
373
390
|
|
|
374
|
-
return result.join('\n')
|
|
391
|
+
return result.join('\n')
|
|
375
392
|
}
|
|
376
393
|
|
|
377
394
|
// Format metrics table
|
|
378
395
|
formatMetrics(metrics) {
|
|
379
|
-
const entries = Object.entries(metrics)
|
|
380
|
-
const maxKeyLength = Math.max(...entries.map(([k]) => k.length))
|
|
381
|
-
|
|
382
|
-
return entries
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
396
|
+
const entries = Object.entries(metrics)
|
|
397
|
+
const maxKeyLength = Math.max(...entries.map(([k]) => k.length))
|
|
398
|
+
|
|
399
|
+
return entries
|
|
400
|
+
.map(([key, value]) => {
|
|
401
|
+
const paddedKey = key.padEnd(maxKeyLength)
|
|
402
|
+
return `${this.label(paddedKey)}: ${this.value(value)}`
|
|
403
|
+
})
|
|
404
|
+
.join('\n')
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Unicode symbols for cross-platform compatibility
|
|
408
|
+
get symbols() {
|
|
409
|
+
return {
|
|
410
|
+
success: '✓',
|
|
411
|
+
error: '✗',
|
|
412
|
+
warning: '⚠',
|
|
413
|
+
info: 'ℹ',
|
|
414
|
+
arrow: '→',
|
|
415
|
+
bullet: '•',
|
|
416
|
+
check: '✓',
|
|
417
|
+
cross: '✗',
|
|
418
|
+
star: '★',
|
|
419
|
+
heart: '♥',
|
|
420
|
+
diamond: '♦',
|
|
421
|
+
circle: '●',
|
|
422
|
+
square: '■',
|
|
423
|
+
triangle: '▲',
|
|
424
|
+
play: '▶',
|
|
425
|
+
pause: '⏸',
|
|
426
|
+
stop: '⏹',
|
|
427
|
+
refresh: '↻',
|
|
428
|
+
sync: '⟲',
|
|
429
|
+
upload: '↑',
|
|
430
|
+
download: '↓',
|
|
431
|
+
plus: '+',
|
|
432
|
+
minus: '-',
|
|
433
|
+
multiply: '×',
|
|
434
|
+
divide: '÷',
|
|
435
|
+
equals: '=',
|
|
436
|
+
pipe: '|',
|
|
437
|
+
hash: '#',
|
|
438
|
+
at: '@',
|
|
439
|
+
dollar: '$',
|
|
440
|
+
percent: '%',
|
|
441
|
+
ampersand: '&',
|
|
442
|
+
question: '?',
|
|
443
|
+
exclamation: '!',
|
|
444
|
+
ellipsis: '…',
|
|
445
|
+
middot: '·',
|
|
446
|
+
section: '§',
|
|
447
|
+
paragraph: '¶',
|
|
448
|
+
copyright: '©',
|
|
449
|
+
registered: '®',
|
|
450
|
+
trademark: '™',
|
|
451
|
+
degree: '°',
|
|
452
|
+
plusminus: '±',
|
|
453
|
+
micro: 'µ',
|
|
454
|
+
alpha: 'α',
|
|
455
|
+
beta: 'β',
|
|
456
|
+
gamma: 'γ',
|
|
457
|
+
delta: 'δ',
|
|
458
|
+
lambda: 'λ',
|
|
459
|
+
mu: 'μ',
|
|
460
|
+
pi: 'π',
|
|
461
|
+
sigma: 'σ',
|
|
462
|
+
tau: 'τ',
|
|
463
|
+
phi: 'φ',
|
|
464
|
+
omega: 'ω',
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Enhanced status messages with symbols
|
|
469
|
+
statusSymbol(type, message) {
|
|
470
|
+
const symbolMap = {
|
|
471
|
+
success: this.success(this.symbols.success),
|
|
472
|
+
error: this.error(this.symbols.error),
|
|
473
|
+
warning: this.warning(this.symbols.warning),
|
|
474
|
+
info: this.info(this.symbols.info),
|
|
475
|
+
processing: this.highlight(this.symbols.refresh),
|
|
476
|
+
ai: this.highlight('🤖'),
|
|
477
|
+
metrics: this.metric('📊'),
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const symbol = symbolMap[type] || symbolMap.info
|
|
481
|
+
return `${symbol} ${message}`
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Gradient text support (will work with gradient-string if available)
|
|
485
|
+
async gradient(text, colors = ['#FF6B6B', '#4ECDC4']) {
|
|
486
|
+
try {
|
|
487
|
+
// Try to use gradient-string if available
|
|
488
|
+
const { default: gradient } = await import('gradient-string')
|
|
489
|
+
return gradient(colors)(text)
|
|
490
|
+
} catch {
|
|
491
|
+
// Fallback to regular coloring
|
|
492
|
+
return this.highlight(text)
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Spinner-like text animation frames
|
|
497
|
+
get spinnerFrames() {
|
|
498
|
+
return ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// Loading dots animation frames
|
|
502
|
+
get dotsFrames() {
|
|
503
|
+
return [' ', '. ', '.. ', '...']
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// Enhanced box with options
|
|
507
|
+
boxed(content, options = {}) {
|
|
508
|
+
const {
|
|
509
|
+
title = '',
|
|
510
|
+
padding = 1,
|
|
511
|
+
margin = 0,
|
|
512
|
+
borderStyle = 'single',
|
|
513
|
+
borderColor = 'secondary',
|
|
514
|
+
titleColor = 'header',
|
|
515
|
+
} = options
|
|
516
|
+
|
|
517
|
+
const borders = {
|
|
518
|
+
single: {
|
|
519
|
+
top: '─',
|
|
520
|
+
bottom: '─',
|
|
521
|
+
left: '│',
|
|
522
|
+
right: '│',
|
|
523
|
+
topLeft: '┌',
|
|
524
|
+
topRight: '┐',
|
|
525
|
+
bottomLeft: '└',
|
|
526
|
+
bottomRight: '┘',
|
|
527
|
+
},
|
|
528
|
+
double: {
|
|
529
|
+
top: '═',
|
|
530
|
+
bottom: '═',
|
|
531
|
+
left: '║',
|
|
532
|
+
right: '║',
|
|
533
|
+
topLeft: '╔',
|
|
534
|
+
topRight: '╗',
|
|
535
|
+
bottomLeft: '╚',
|
|
536
|
+
bottomRight: '╝',
|
|
537
|
+
},
|
|
538
|
+
rounded: {
|
|
539
|
+
top: '─',
|
|
540
|
+
bottom: '─',
|
|
541
|
+
left: '│',
|
|
542
|
+
right: '│',
|
|
543
|
+
topLeft: '╭',
|
|
544
|
+
topRight: '╮',
|
|
545
|
+
bottomLeft: '╰',
|
|
546
|
+
bottomRight: '╯',
|
|
547
|
+
},
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
const border = borders[borderStyle] || borders.single
|
|
551
|
+
const lines = content.split('\n')
|
|
552
|
+
const maxContentLength = Math.max(...lines.map((line) => this.getVisibleLength(line)))
|
|
553
|
+
const titleLength = title ? this.getVisibleLength(title) + 2 : 0
|
|
554
|
+
const width = Math.max(maxContentLength, titleLength) + padding * 2
|
|
555
|
+
|
|
556
|
+
const colorBorder = this[borderColor] || this.secondary
|
|
557
|
+
const colorTitle = this[titleColor] || this.header
|
|
558
|
+
|
|
559
|
+
const result = []
|
|
560
|
+
|
|
561
|
+
// Top margin
|
|
562
|
+
if (margin > 0) {
|
|
563
|
+
result.push(
|
|
564
|
+
''
|
|
565
|
+
.repeat(margin)
|
|
566
|
+
.split('')
|
|
567
|
+
.map(() => '')
|
|
568
|
+
.join('\n')
|
|
569
|
+
)
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Top border
|
|
573
|
+
if (title) {
|
|
574
|
+
const titlePadding = Math.max(0, width - titleLength)
|
|
575
|
+
result.push(
|
|
576
|
+
colorBorder(border.topLeft + border.top.repeat(2)) +
|
|
577
|
+
` ${colorTitle(title)} ` +
|
|
578
|
+
colorBorder(border.top.repeat(titlePadding - 2) + border.topRight)
|
|
579
|
+
)
|
|
580
|
+
} else {
|
|
581
|
+
result.push(colorBorder(border.topLeft + border.top.repeat(width) + border.topRight))
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// Content
|
|
585
|
+
lines.forEach((line) => {
|
|
586
|
+
const contentPadding = Math.max(0, width - this.getVisibleLength(line))
|
|
587
|
+
result.push(
|
|
588
|
+
colorBorder(border.left) +
|
|
589
|
+
' '.repeat(padding) +
|
|
590
|
+
line +
|
|
591
|
+
' '.repeat(contentPadding - padding) +
|
|
592
|
+
colorBorder(border.right)
|
|
593
|
+
)
|
|
594
|
+
})
|
|
595
|
+
|
|
596
|
+
// Bottom border
|
|
597
|
+
result.push(colorBorder(border.bottomLeft + border.bottom.repeat(width) + border.bottomRight))
|
|
598
|
+
|
|
599
|
+
// Bottom margin
|
|
600
|
+
if (margin > 0) {
|
|
601
|
+
result.push(
|
|
602
|
+
''
|
|
603
|
+
.repeat(margin)
|
|
604
|
+
.split('')
|
|
605
|
+
.map(() => '')
|
|
606
|
+
.join('\n')
|
|
607
|
+
)
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
return result.join('\n')
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// Table formatting helper
|
|
614
|
+
table(data, options = {}) {
|
|
615
|
+
if (!Array.isArray(data) || data.length === 0) {
|
|
616
|
+
return ''
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
const { headers = Object.keys(data[0]), align = 'left', padding = 1 } = options
|
|
620
|
+
const rows = data.map((row) => headers.map((header) => String(row[header] || '')))
|
|
621
|
+
|
|
622
|
+
// Calculate column widths
|
|
623
|
+
const colWidths = headers.map((header, i) =>
|
|
624
|
+
Math.max(header.length, ...rows.map((row) => this.getVisibleLength(row[i])))
|
|
625
|
+
)
|
|
626
|
+
|
|
627
|
+
const formatRow = (row, isHeader = false) => {
|
|
628
|
+
const cells = row.map((cell, i) => {
|
|
629
|
+
const width = colWidths[i]
|
|
630
|
+
const visibleLength = this.getVisibleLength(String(cell))
|
|
631
|
+
const paddingNeeded = width - visibleLength
|
|
632
|
+
|
|
633
|
+
if (align === 'right') {
|
|
634
|
+
return ' '.repeat(paddingNeeded) + cell
|
|
635
|
+
}
|
|
636
|
+
if (align === 'center') {
|
|
637
|
+
const leftPad = Math.floor(paddingNeeded / 2)
|
|
638
|
+
const rightPad = paddingNeeded - leftPad
|
|
639
|
+
return ' '.repeat(leftPad) + cell + ' '.repeat(rightPad)
|
|
640
|
+
}
|
|
641
|
+
return cell + ' '.repeat(paddingNeeded)
|
|
642
|
+
})
|
|
643
|
+
|
|
644
|
+
const colorFunc = isHeader ? this.header : this.secondary
|
|
645
|
+
return colorFunc('│') + cells.map((cell) => ` ${cell} `).join(colorFunc('│')) + colorFunc('│')
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
const separator = this.secondary(`├${colWidths.map((w) => '─'.repeat(w + 2)).join('┼')}┤`)
|
|
649
|
+
const topBorder = this.secondary(`┌${colWidths.map((w) => '─'.repeat(w + 2)).join('┬')}┐`)
|
|
650
|
+
const bottomBorder = this.secondary(`└${colWidths.map((w) => '─'.repeat(w + 2)).join('┴')}┘`)
|
|
651
|
+
|
|
652
|
+
return [
|
|
653
|
+
topBorder,
|
|
654
|
+
formatRow(headers, true),
|
|
655
|
+
separator,
|
|
656
|
+
...rows.map((row) => formatRow(row)),
|
|
657
|
+
bottomBorder,
|
|
658
|
+
].join('\n')
|
|
386
659
|
}
|
|
387
660
|
}
|
|
388
661
|
|
|
389
662
|
// Export singleton instance
|
|
390
|
-
const colors = new Colors()
|
|
663
|
+
const colors = new Colors()
|
|
391
664
|
|
|
392
|
-
export default colors
|
|
665
|
+
export default colors
|