prjct-cli 0.6.0 → 0.7.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.
Files changed (83) hide show
  1. package/CHANGELOG.md +67 -6
  2. package/CLAUDE.md +442 -36
  3. package/README.md +47 -54
  4. package/bin/prjct +174 -240
  5. package/core/agentic/command-executor.js +113 -0
  6. package/core/agentic/context-builder.js +85 -0
  7. package/core/agentic/prompt-builder.js +86 -0
  8. package/core/agentic/template-loader.js +104 -0
  9. package/core/agentic/tool-registry.js +117 -0
  10. package/core/command-registry.js +109 -65
  11. package/core/commands.js +2213 -2173
  12. package/core/domain/agent-generator.js +118 -0
  13. package/core/domain/analyzer.js +211 -0
  14. package/core/domain/architect-session.js +300 -0
  15. package/core/{agents → infrastructure/agents}/claude-agent.js +16 -13
  16. package/core/{author-detector.js → infrastructure/author-detector.js} +3 -1
  17. package/core/{capability-installer.js → infrastructure/capability-installer.js} +3 -6
  18. package/core/{command-installer.js → infrastructure/command-installer.js} +5 -3
  19. package/core/{config-manager.js → infrastructure/config-manager.js} +4 -4
  20. package/core/{editors-config.js → infrastructure/editors-config.js} +2 -10
  21. package/core/{migrator.js → infrastructure/migrator.js} +34 -19
  22. package/core/{path-manager.js → infrastructure/path-manager.js} +20 -44
  23. package/core/{session-manager.js → infrastructure/session-manager.js} +45 -105
  24. package/core/{update-checker.js → infrastructure/update-checker.js} +67 -67
  25. package/core/{animations-simple.js → utils/animations.js} +3 -23
  26. package/core/utils/date-helper.js +238 -0
  27. package/core/utils/file-helper.js +327 -0
  28. package/core/utils/jsonl-helper.js +206 -0
  29. package/core/{project-capabilities.js → utils/project-capabilities.js} +21 -22
  30. package/core/utils/session-helper.js +277 -0
  31. package/core/{version.js → utils/version.js} +1 -1
  32. package/package.json +4 -12
  33. package/templates/agents/AGENTS.md +101 -27
  34. package/templates/analysis/analyze.md +84 -0
  35. package/templates/commands/analyze.md +9 -2
  36. package/templates/commands/bug.md +79 -0
  37. package/templates/commands/build.md +5 -2
  38. package/templates/commands/cleanup.md +5 -2
  39. package/templates/commands/design.md +5 -2
  40. package/templates/commands/done.md +4 -2
  41. package/templates/commands/feature.md +113 -0
  42. package/templates/commands/fix.md +41 -10
  43. package/templates/commands/git.md +7 -2
  44. package/templates/commands/help.md +2 -2
  45. package/templates/commands/idea.md +14 -5
  46. package/templates/commands/init.md +62 -7
  47. package/templates/commands/next.md +4 -2
  48. package/templates/commands/now.md +4 -2
  49. package/templates/commands/progress.md +27 -5
  50. package/templates/commands/recap.md +39 -10
  51. package/templates/commands/roadmap.md +19 -5
  52. package/templates/commands/ship.md +118 -16
  53. package/templates/commands/status.md +4 -2
  54. package/templates/commands/sync.md +19 -15
  55. package/templates/commands/task.md +4 -2
  56. package/templates/commands/test.md +5 -2
  57. package/templates/commands/workflow.md +4 -2
  58. package/core/agent-generator.js +0 -525
  59. package/core/analyzer.js +0 -600
  60. package/core/animations.js +0 -277
  61. package/core/ascii-graphics.js +0 -433
  62. package/core/git-integration.js +0 -401
  63. package/core/task-schema.js +0 -342
  64. package/core/workflow-engine.js +0 -213
  65. package/core/workflow-prompts.js +0 -192
  66. package/core/workflow-rules.js +0 -147
  67. package/scripts/post-install.js +0 -121
  68. package/scripts/preuninstall.js +0 -94
  69. package/scripts/verify-installation.sh +0 -158
  70. package/templates/agents/be.template.md +0 -27
  71. package/templates/agents/coordinator.template.md +0 -34
  72. package/templates/agents/data.template.md +0 -27
  73. package/templates/agents/devops.template.md +0 -27
  74. package/templates/agents/fe.template.md +0 -27
  75. package/templates/agents/mobile.template.md +0 -27
  76. package/templates/agents/qa.template.md +0 -27
  77. package/templates/agents/scribe.template.md +0 -29
  78. package/templates/agents/security.template.md +0 -27
  79. package/templates/agents/ux.template.md +0 -27
  80. package/templates/commands/context.md +0 -36
  81. package/templates/commands/stuck.md +0 -36
  82. package/templates/examples/natural-language-examples.md +0 -532
  83. /package/core/{agent-detector.js → infrastructure/agent-detector.js} +0 -0
@@ -1,277 +0,0 @@
1
- /**
2
- * Cool animations and visual effects for prjct
3
- * Using Catppuccin color palette
4
- */
5
-
6
- const chalk = require('chalk')
7
-
8
- if (!chalk.supportsColor) {
9
- chalk.level = 3 // Full RGB color support
10
- process.env.FORCE_COLOR = '3'
11
- }
12
-
13
- const catppuccin = {
14
-
15
- rosewater: '#f5e0dc',
16
- flamingo: '#f2cdcd',
17
- pink: '#f5c2e7',
18
- mauve: '#cba6f7',
19
- red: '#f38ba8',
20
- maroon: '#eba0ac',
21
- peach: '#fab387',
22
- yellow: '#f9e2af',
23
- green: '#a6e3a1',
24
- teal: '#94e2d5',
25
- sky: '#89dceb',
26
- sapphire: '#74c7ec',
27
- blue: '#89b4fa',
28
- lavender: '#b4befe',
29
-
30
- text: '#cdd6f4',
31
- subtext1: '#bac2de',
32
- subtext0: '#a6adc8',
33
- overlay2: '#9399b2',
34
- overlay1: '#7f849c',
35
- overlay0: '#6c7086',
36
- surface2: '#585b70',
37
- surface1: '#45475a',
38
- surface0: '#313244',
39
- base: '#1e1e2e',
40
- mantle: '#181825',
41
- crust: '#11111b',
42
- }
43
-
44
- const colors = {
45
- success: chalk.hex(catppuccin.green),
46
- error: chalk.hex(catppuccin.red),
47
- warning: chalk.hex(catppuccin.yellow),
48
- info: chalk.hex(catppuccin.blue),
49
- ship: chalk.hex(catppuccin.sapphire),
50
- celebrate: chalk.hex(catppuccin.pink),
51
- focus: chalk.hex(catppuccin.teal),
52
- idea: chalk.hex(catppuccin.yellow),
53
- progress: chalk.hex(catppuccin.lavender),
54
- task: chalk.hex(catppuccin.mauve),
55
- primary: chalk.hex(catppuccin.mauve),
56
- secondary: chalk.hex(catppuccin.sky),
57
- text: chalk.hex(catppuccin.text),
58
- subtext: chalk.hex(catppuccin.subtext1),
59
- dim: chalk.hex(catppuccin.overlay1),
60
- }
61
-
62
- const frames = {
63
- rocket: [
64
- ' 🚀 ',
65
- ' 🚀 ',
66
- ' 🚀 ',
67
- ' 🚀 ',
68
- ' 🚀 ',
69
- '🚀 ',
70
- ],
71
- sparkles: [
72
- '✨ ・ 。゚☆: *.☽ .* :☆゚. ✨',
73
- '・ 。゚☆: *.☽ .* :☆゚. ✨ ・',
74
- '。゚☆: *.☽ .* :☆゚. ✨ ・ 。゚',
75
- '☆: *.☽ .* :☆゚. ✨ ・ 。゚☆:',
76
- ],
77
- loading: [
78
- '⠋',
79
- '⠙',
80
- '⠹',
81
- '⠸',
82
- '⠼',
83
- '⠴',
84
- '⠦',
85
- '⠧',
86
- '⠇',
87
- '⠏',
88
- ],
89
- progress: [
90
- '[ ]',
91
- '[▓ ]',
92
- '[▓▓ ]',
93
- '[▓▓▓ ]',
94
- '[▓▓▓▓ ]',
95
- '[▓▓▓▓▓ ]',
96
- '[▓▓▓▓▓▓ ]',
97
- '[▓▓▓▓▓▓▓ ]',
98
- '[▓▓▓▓▓▓▓▓ ]',
99
- '[▓▓▓▓▓▓▓▓▓ ]',
100
- '[▓▓▓▓▓▓▓▓▓▓]',
101
- ],
102
- celebration: [
103
- '🎉',
104
- '🎊',
105
- '✨',
106
- '🌟',
107
- '⭐',
108
- '💫',
109
- '🎆',
110
- '🎇',
111
- ],
112
- }
113
-
114
- const banners = {
115
- ship: `
116
- ╔════════════════════════════════════════════╗
117
- ║ 🚀 ${colors.ship.bold('S H I P P E D !')} 🚀 ║
118
- ╚════════════════════════════════════════════╝`,
119
-
120
- success: `
121
- ✨ ${colors.success.bold('Success!')} ✨`,
122
-
123
- error: `
124
- ❌ ${colors.error.bold('Error')} ❌`,
125
-
126
- welcome: `
127
- ${colors.primary('╔══════════════════════════════════════════════════╗')}
128
- ${colors.primary('║')} ${colors.text.bold('🚀 prjct')}${colors.primary('/')}${colors.secondary.bold('cli')} ${colors.primary('║')}
129
- ${colors.primary('║')} ${colors.dim('Ship faster with zero friction')} ${colors.primary('║')}
130
- ${colors.primary('╚══════════════════════════════════════════════════╝')}`,
131
-
132
- cleanup: `
133
- ${colors.focus('🧹 ✨ Cleanup Magic ✨ 🧹')}`,
134
-
135
- focus: `
136
- ${colors.focus('━━━━━━━━━━━━━━━━━━━━━━━')}
137
- ${colors.focus.bold(' 🎯 FOCUS MODE 🎯 ')}
138
- ${colors.focus('━━━━━━━━━━━━━━━━━━━━━━━')}`,
139
- }
140
-
141
- async function animate(frames, duration = 100) {
142
- for (const frame of frames) {
143
- process.stdout.write('\r' + frame)
144
- await sleep(duration)
145
- }
146
- process.stdout.write('\r' + ' '.repeat(30) + '\r')
147
- }
148
-
149
- async function typeWriter(text, delay = 30) {
150
- for (let i = 0; i <= text.length; i++) {
151
- process.stdout.write('\r' + text.slice(0, i) + (i < text.length ? '▋' : ''))
152
- await sleep(delay)
153
- }
154
- process.stdout.write('\n')
155
- }
156
-
157
- async function progressBar(duration = 1000, label = 'Processing') {
158
- const steps = 20
159
- const stepDuration = duration / steps
160
-
161
- for (let i = 0; i <= steps; i++) {
162
- const percent = Math.round((i / steps) * 100)
163
- const filled = '▓'.repeat(i)
164
- const empty = '░'.repeat(steps - i)
165
- const bar = `${colors.dim(label)} [${colors.primary(filled)}${colors.dim(empty)}] ${colors.text(percent + '%')}`
166
- process.stdout.write('\r' + bar)
167
- await sleep(stepDuration)
168
- }
169
- process.stdout.write('\n')
170
- }
171
-
172
- async function sparkle(message) {
173
- const sparkles = ['✨', '⭐', '💫', '🌟']
174
- let output = ''
175
-
176
- for (let i = 0; i < 3; i++) {
177
- const spark = sparkles[Math.floor(Math.random() * sparkles.length)]
178
- output = `${spark} ${message} ${spark}`
179
- process.stdout.write('\r' + output)
180
- await sleep(200)
181
- process.stdout.write('\r' + ' '.repeat(output.length))
182
- await sleep(100)
183
- }
184
-
185
- console.log(output)
186
- }
187
-
188
- function sleep(ms) {
189
- return new Promise(resolve => setTimeout(resolve, ms))
190
- }
191
-
192
- function formatShip(feature, count) {
193
- const banner = banners.ship
194
- const stats = `
195
- ${colors.text('Feature:')} ${colors.ship.bold(feature)}
196
- ${colors.text('Total shipped:')} ${colors.success.bold(count)}
197
- ${colors.text('Velocity:')} ${colors.celebrate('🔥 On fire!')}
198
- `
199
-
200
- return banner + stats
201
- }
202
-
203
- function formatFocus(task, timestamp) {
204
- const banner = banners.focus
205
- const info = `
206
- ${colors.text('Current task:')} ${colors.focus.bold(task)}
207
- ${colors.dim('Started:')} ${colors.subtext(timestamp)}
208
- `
209
-
210
- return banner + info
211
- }
212
-
213
- function formatSuccess(message) {
214
- return `${colors.success('✅')} ${colors.text(message)}`
215
- }
216
-
217
- function formatError(message) {
218
- return `${colors.error('❌')} ${colors.text(message)}`
219
- }
220
-
221
- function formatIdea(idea) {
222
- return `
223
- ${colors.idea('💡 Idea captured!')}
224
- ${colors.text('―'.repeat(30))}
225
- ${colors.subtext(idea)}
226
- ${colors.text('―'.repeat(30))}
227
- ${colors.dim('Added to your ideas backlog')}
228
- `
229
- }
230
-
231
- function formatCleanup(filesRemoved, tasksArchived, spaceFeed) {
232
- return `
233
- ${banners.cleanup}
234
-
235
- ${colors.text('🗑️ Files removed:')} ${colors.success.bold(filesRemoved)}
236
- ${colors.text('📦 Tasks archived:')} ${colors.success.bold(tasksArchived)}
237
- ${colors.text('💾 Space freed:')} ${colors.success.bold(spaceFeed + ' MB')}
238
-
239
- ${colors.celebrate('✨ Your project is clean and lean!')}
240
- `
241
- }
242
-
243
- function formatRecap(data) {
244
- const divider = colors.primary('━'.repeat(40))
245
-
246
- return `
247
- ${divider}
248
- ${colors.primary.bold('📊 PROJECT RECAP')}
249
- ${divider}
250
-
251
- ${colors.text('🎯 Current focus:')} ${data.currentTask || colors.dim('No active task')}
252
- ${colors.text('🚀 Shipped this week:')} ${colors.success.bold(data.shippedCount)}
253
- ${colors.text('📝 Queued tasks:')} ${colors.info.bold(data.queuedCount)}
254
- ${colors.text('💡 Ideas captured:')} ${colors.idea.bold(data.ideasCount)}
255
-
256
- ${divider}
257
- ${colors.dim('Keep shipping! 🚀')}
258
- `
259
- }
260
-
261
- module.exports = {
262
- colors,
263
- frames,
264
- banners,
265
- animate,
266
- typeWriter,
267
- progressBar,
268
- sparkle,
269
- formatShip,
270
- formatFocus,
271
- formatSuccess,
272
- formatError,
273
- formatIdea,
274
- formatCleanup,
275
- formatRecap,
276
- catppuccin,
277
- }
@@ -1,433 +0,0 @@
1
- /**
2
- * ASCII Graphics Utilities
3
- *
4
- * Creates visual dashboards and progress bars in terminal
5
- * using ASCII characters with Catppuccin Mocha color scheme.
6
- *
7
- * @version 0.6.0
8
- */
9
-
10
- const chalk = require('chalk')
11
-
12
- /**
13
- * Catppuccin Mocha Color Palette
14
- * https://github.com/catppuccin/catppuccin
15
- */
16
- const CATPPUCCIN = {
17
- // Base colors
18
- base: '#1e1e2e',
19
- mantle: '#181825',
20
- crust: '#11111b',
21
-
22
- // Text colors
23
- text: '#cdd6f4',
24
- subtext1: '#bac2de',
25
- subtext0: '#a6adc8',
26
-
27
- // Overlay colors
28
- overlay2: '#9399b2',
29
- overlay1: '#7f849c',
30
- overlay0: '#6c7086',
31
- surface2: '#585b70',
32
- surface1: '#45475a',
33
- surface0: '#313244',
34
-
35
- // Accent colors
36
- rosewater: '#f5e0dc',
37
- flamingo: '#f2cdcd',
38
- pink: '#f5c2e7',
39
- mauve: '#cba6f7',
40
- red: '#f38ba8',
41
- maroon: '#eba0ac',
42
- peach: '#fab387',
43
- yellow: '#f9e2af',
44
- green: '#a6e3a1',
45
- teal: '#94e2d5',
46
- sky: '#89dceb',
47
- sapphire: '#74c7ec',
48
- blue: '#89b4fa',
49
- lavender: '#b4befe',
50
- }
51
-
52
- /**
53
- * Semantic color mapping
54
- */
55
- const COLORS = {
56
- // Status colors
57
- success: chalk.hex(CATPPUCCIN.green),
58
- warning: chalk.hex(CATPPUCCIN.yellow),
59
- error: chalk.hex(CATPPUCCIN.red),
60
- info: chalk.hex(CATPPUCCIN.blue),
61
-
62
- // UI colors
63
- primary: chalk.hex(CATPPUCCIN.mauve),
64
- secondary: chalk.hex(CATPPUCCIN.lavender),
65
- accent: chalk.hex(CATPPUCCIN.peach),
66
- muted: chalk.hex(CATPPUCCIN.overlay0),
67
-
68
- // Progress colors
69
- progress: chalk.hex(CATPPUCCIN.teal),
70
- complete: chalk.hex(CATPPUCCIN.green),
71
- pending: chalk.hex(CATPPUCCIN.overlay1),
72
-
73
- // Text colors
74
- bold: chalk.hex(CATPPUCCIN.text).bold,
75
- dim: chalk.hex(CATPPUCCIN.overlay0),
76
- highlight: chalk.hex(CATPPUCCIN.sapphire),
77
- }
78
-
79
- /**
80
- * Box Drawing Characters
81
- */
82
- const BOX = {
83
- topLeft: '┌',
84
- topRight: '┐',
85
- bottomLeft: '└',
86
- bottomRight: '┘',
87
- horizontal: '─',
88
- vertical: '│',
89
- verticalRight: '├',
90
- verticalLeft: '┤',
91
- cross: '┼',
92
- }
93
-
94
- /**
95
- * Progress Bar Characters
96
- */
97
- const PROGRESS = {
98
- filled: '█',
99
- empty: '░',
100
- partial: ['▏', '▎', '▍', '▌', '▋', '▊', '▉'],
101
- }
102
-
103
- /**
104
- * ASCII Graphics Generator
105
- */
106
- class ASCIIGraphics {
107
- /**
108
- * Create a status dashboard
109
- */
110
- static createDashboard(data) {
111
- const width = 50
112
- const lines = []
113
-
114
- // Top border
115
- lines.push(
116
- COLORS.primary(
117
- `${BOX.topLeft}${BOX.horizontal} Project Status ${BOX.horizontal.repeat(width - 17)}${BOX.topRight}`,
118
- ),
119
- )
120
-
121
- // Sprint Progress
122
- const sprintProgress = data.sprintProgress || 0
123
- lines.push(
124
- `${COLORS.primary(BOX.vertical)} ${COLORS.bold('Sprint Progress')} ${this.createProgressBar(sprintProgress, 10)} ${COLORS.highlight(Math.round(sprintProgress) + '%')} ${' '.repeat(width - 38)}${COLORS.primary(BOX.vertical)}`,
125
- )
126
-
127
- // Tasks Complete
128
- const tasksComplete = data.tasksComplete || 0
129
- const tasksTotal = data.tasksTotal || 0
130
- lines.push(
131
- `${COLORS.primary(BOX.vertical)} ${COLORS.bold('Tasks Complete')} ${COLORS.complete(tasksComplete)}/${COLORS.info(tasksTotal)}${' '.repeat(width - 28)}${COLORS.primary(BOX.vertical)}`,
132
- )
133
-
134
- // Ideas in Backlog
135
- const ideasCount = data.ideasCount || 0
136
- lines.push(
137
- `${COLORS.primary(BOX.vertical)} ${COLORS.bold('Ideas in Backlog')} ${COLORS.accent(ideasCount)}${' '.repeat(width - 28)}${COLORS.primary(BOX.vertical)}`,
138
- )
139
-
140
- // Days Since Ship
141
- const daysSinceShip = data.daysSinceShip || 0
142
- const shipColor =
143
- daysSinceShip > 7 ? COLORS.error : daysSinceShip > 3 ? COLORS.warning : COLORS.success
144
- lines.push(
145
- `${COLORS.primary(BOX.vertical)} ${COLORS.bold('Days Since Ship')} ${shipColor(daysSinceShip)}${' '.repeat(width - 28)}${COLORS.primary(BOX.vertical)}`,
146
- )
147
-
148
- // Middle separator
149
- lines.push(
150
- COLORS.primary(
151
- `${BOX.verticalRight}${BOX.horizontal} Current Focus ${BOX.horizontal.repeat(width - 17)}${BOX.verticalLeft}`,
152
- ),
153
- )
154
-
155
- // Current Task
156
- const currentTask = data.currentTask || 'No active task'
157
- const taskTime = data.taskTime || ''
158
- lines.push(
159
- `${COLORS.primary(BOX.vertical)} ${COLORS.highlight('→')} ${COLORS.bold(this.truncate(currentTask, width - 5))}${' '.repeat(Math.max(0, width - currentTask.length - 4))}${COLORS.primary(BOX.vertical)}`,
160
- )
161
-
162
- if (taskTime) {
163
- lines.push(
164
- `${COLORS.primary(BOX.vertical)} ${COLORS.dim(`Started: ${taskTime}`)}${' '.repeat(Math.max(0, width - taskTime.length - 13))}${COLORS.primary(BOX.vertical)}`,
165
- )
166
- }
167
-
168
- // Bottom border
169
- lines.push(COLORS.primary(`${BOX.bottomLeft}${BOX.horizontal.repeat(width)}${BOX.bottomRight}`))
170
-
171
- return lines.join('\n')
172
- }
173
-
174
- /**
175
- * Create a progress bar
176
- */
177
- static createProgressBar(percentage, width = 20) {
178
- const filled = Math.floor((percentage / 100) * width)
179
- const empty = width - filled
180
-
181
- return COLORS.progress(PROGRESS.filled.repeat(filled)) + COLORS.pending(PROGRESS.empty.repeat(empty))
182
- }
183
-
184
- /**
185
- * Create a horizontal bar chart
186
- */
187
- static createBarChart(data, maxWidth = 30) {
188
- const lines = []
189
- const maxValue = Math.max(...data.map((d) => d.value))
190
-
191
- for (const item of data) {
192
- const barWidth = Math.round((item.value / maxValue) * maxWidth)
193
- const bar = PROGRESS.filled.repeat(barWidth)
194
- const label = item.label.padEnd(15)
195
- const value = String(item.value).padStart(3)
196
-
197
- lines.push(`${label} ${COLORS.progress(bar)} ${COLORS.bold(value)}`)
198
- }
199
-
200
- return lines.join('\n')
201
- }
202
-
203
- /**
204
- * Create a vertical progress indicator
205
- */
206
- static createVerticalProgress(percentage, height = 10) {
207
- const filled = Math.floor((percentage / 100) * height)
208
- const empty = height - filled
209
-
210
- const lines = []
211
- lines.push(COLORS.primary('┌─┐'))
212
-
213
- for (let i = 0; i < empty; i++) {
214
- lines.push(`${COLORS.primary('│')}${COLORS.pending(PROGRESS.empty)}${COLORS.primary('│')}`)
215
- }
216
-
217
- for (let i = 0; i < filled; i++) {
218
- lines.push(`${COLORS.primary('│')}${COLORS.progress(PROGRESS.filled)}${COLORS.primary('│')}`)
219
- }
220
-
221
- lines.push(COLORS.primary('└─┘'))
222
- lines.push(` ${COLORS.highlight(percentage + '%')}`)
223
-
224
- return lines.join('\n')
225
- }
226
-
227
- /**
228
- * Create a timeline view
229
- */
230
- static createTimeline(events) {
231
- const lines = []
232
-
233
- for (let i = 0; i < events.length; i++) {
234
- const event = events[i]
235
- const isLast = i === events.length - 1
236
-
237
- // Event marker
238
- const marker = event.completed ? COLORS.complete('●') : COLORS.pending('○')
239
- const connector = isLast ? ' ' : COLORS.muted('│')
240
-
241
- lines.push(`${marker} ${COLORS.bold(event.title)}`)
242
-
243
- if (event.description) {
244
- lines.push(`${connector} ${COLORS.dim(event.description)}`)
245
- }
246
-
247
- if (event.time) {
248
- lines.push(`${connector} ${COLORS.info(event.time)}`)
249
- }
250
-
251
- if (!isLast) {
252
- lines.push(connector)
253
- }
254
- }
255
-
256
- return lines.join('\n')
257
- }
258
-
259
- /**
260
- * Create a table
261
- */
262
- static createTable(headers, rows) {
263
- const columnWidths = headers.map((h, i) => {
264
- const maxRowWidth = Math.max(...rows.map((r) => String(r[i] || '').length))
265
- return Math.max(h.length, maxRowWidth) + 2
266
- })
267
-
268
- const lines = []
269
-
270
- // Top border
271
- const topBorder =
272
- BOX.topLeft +
273
- columnWidths.map((w) => BOX.horizontal.repeat(w)).join(BOX.cross) +
274
- BOX.topRight
275
- lines.push(topBorder)
276
-
277
- // Headers
278
- const headerRow =
279
- BOX.vertical +
280
- headers
281
- .map((h, i) => chalk.bold(h.padEnd(columnWidths[i])))
282
- .join(BOX.vertical) +
283
- BOX.vertical
284
- lines.push(headerRow)
285
-
286
- // Header separator
287
- const headerSep =
288
- BOX.verticalRight +
289
- columnWidths.map((w) => BOX.horizontal.repeat(w)).join(BOX.cross) +
290
- BOX.verticalLeft
291
- lines.push(headerSep)
292
-
293
- // Rows
294
- for (const row of rows) {
295
- const rowStr =
296
- BOX.vertical +
297
- row
298
- .map((cell, i) => String(cell || '').padEnd(columnWidths[i]))
299
- .join(BOX.vertical) +
300
- BOX.vertical
301
- lines.push(rowStr)
302
- }
303
-
304
- // Bottom border
305
- const bottomBorder =
306
- BOX.bottomLeft +
307
- columnWidths.map((w) => BOX.horizontal.repeat(w)).join(BOX.cross) +
308
- BOX.bottomRight
309
- lines.push(bottomBorder)
310
-
311
- return lines.join('\n')
312
- }
313
-
314
- /**
315
- * Create a sparkline (mini chart)
316
- */
317
- static createSparkline(values) {
318
- const chars = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█']
319
- const max = Math.max(...values)
320
- const min = Math.min(...values)
321
- const range = max - min
322
-
323
- if (range === 0) {
324
- return chars[0].repeat(values.length)
325
- }
326
-
327
- return values
328
- .map((v) => {
329
- const normalized = (v - min) / range
330
- const index = Math.floor(normalized * (chars.length - 1))
331
- return chars[index]
332
- })
333
- .join('')
334
- }
335
-
336
- /**
337
- * Create a gauge/meter
338
- */
339
- static createGauge(value, max, label = '') {
340
- const percentage = (value / max) * 100
341
- const width = 30
342
-
343
- const filled = Math.floor((percentage / 100) * width)
344
- const empty = width - filled
345
-
346
- let color = COLORS.success
347
- if (percentage > 75) color = COLORS.error
348
- else if (percentage > 50) color = COLORS.warning
349
-
350
- const bar = color(PROGRESS.filled.repeat(filled)) + COLORS.pending(PROGRESS.empty.repeat(empty))
351
-
352
- const labelStr = label ? `${COLORS.bold(label.padEnd(15))} ` : ''
353
- const valueStr = `${COLORS.info(value)}/${COLORS.muted(max)} (${COLORS.highlight(Math.round(percentage) + '%')})`
354
-
355
- return `${labelStr}[${bar}] ${valueStr}`
356
- }
357
-
358
- /**
359
- * Create ASCII art number (for big stats)
360
- */
361
- static createBigNumber(num) {
362
- const digits = {
363
- 0: ['███', '█ █', '█ █', '█ █', '███'],
364
- 1: [' █', ' █', ' █', ' █', ' █'],
365
- 2: ['███', ' █', '███', '█ ', '███'],
366
- 3: ['███', ' █', '███', ' █', '███'],
367
- 4: ['█ █', '█ █', '███', ' █', ' █'],
368
- 5: ['███', '█ ', '███', ' █', '███'],
369
- 6: ['███', '█ ', '███', '█ █', '███'],
370
- 7: ['███', ' █', ' █', ' █', ' █'],
371
- 8: ['███', '█ █', '███', '█ █', '███'],
372
- 9: ['███', '█ █', '███', ' █', '███'],
373
- }
374
-
375
- const numStr = String(num)
376
- const lines = ['', '', '', '', '']
377
-
378
- for (const char of numStr) {
379
- if (digits[char]) {
380
- for (let i = 0; i < 5; i++) {
381
- lines[i] += digits[char][i] + ' '
382
- }
383
- }
384
- }
385
-
386
- return lines.join('\n')
387
- }
388
-
389
- /**
390
- * Truncate text to fit width
391
- */
392
- static truncate(text, maxWidth) {
393
- if (text.length <= maxWidth) {
394
- return text
395
- }
396
- return text.substring(0, maxWidth - 3) + '...'
397
- }
398
-
399
- /**
400
- * Create a loading spinner frame
401
- */
402
- static getSpinnerFrame(index) {
403
- const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
404
- return frames[index % frames.length]
405
- }
406
-
407
- /**
408
- * Create a status indicator
409
- */
410
- static statusIndicator(status) {
411
- const indicators = {
412
- success: COLORS.success('✓'),
413
- error: COLORS.error('✗'),
414
- warning: COLORS.warning('⚠'),
415
- info: COLORS.info('ℹ'),
416
- pending: COLORS.pending('○'),
417
- active: COLORS.highlight('●'),
418
- }
419
-
420
- return indicators[status] || indicators.info
421
- }
422
-
423
- /**
424
- * Create a divider line
425
- */
426
- static divider(width = 50, char = '─') {
427
- return COLORS.muted(char.repeat(width))
428
- }
429
- }
430
-
431
- module.exports = ASCIIGraphics
432
- module.exports.COLORS = COLORS
433
- module.exports.CATPPUCCIN = CATPPUCCIN