mells-cli 1.0.0
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/Europass-CV-1.pdf +0 -0
- package/README.md +43 -0
- package/Resume.pdf +0 -0
- package/bin/index.js +161 -0
- package/commands/handlers.js +75 -0
- package/commands/resume.js +16 -0
- package/data/content.js +115 -0
- package/mells_cli_full_prd.pdf +155 -0
- package/package.json +29 -0
- package/test-cli.js +31 -0
- package/utils/banner.js +116 -0
- package/utils/display.js +50 -0
|
Binary file
|
package/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Mells CLI
|
|
2
|
+
|
|
3
|
+
Interactive command-line portfolio tool.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Run directly with npx (no installation required):
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx mells-cli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or install globally:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g mells-cli
|
|
17
|
+
mells-cli
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
Once launched, type `help` to see available commands.
|
|
23
|
+
|
|
24
|
+
## Commands
|
|
25
|
+
|
|
26
|
+
- `help` - Show available commands
|
|
27
|
+
- `about` - Learn about me
|
|
28
|
+
- `skills` - View technical skills
|
|
29
|
+
- `projects` - See featured projects
|
|
30
|
+
- `journey` - My CLI & DevOps journey
|
|
31
|
+
- `why-hire-me` - Why you should hire me
|
|
32
|
+
- `resume` - Open resume in browser
|
|
33
|
+
- `contact` - Get contact information
|
|
34
|
+
- `clear` - Clear the screen
|
|
35
|
+
- `exit` - Exit the CLI
|
|
36
|
+
|
|
37
|
+
## Requirements
|
|
38
|
+
|
|
39
|
+
- Node.js >= 18.0.0
|
|
40
|
+
|
|
41
|
+
## License
|
|
42
|
+
|
|
43
|
+
MIT
|
package/Resume.pdf
ADDED
|
Binary file
|
package/bin/index.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import readline from 'readline';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
import {
|
|
7
|
+
helpCommand,
|
|
8
|
+
aboutCommand,
|
|
9
|
+
skillsCommand,
|
|
10
|
+
projectsCommand,
|
|
11
|
+
journeyCommand,
|
|
12
|
+
whyHireMeCommand,
|
|
13
|
+
contactCommand,
|
|
14
|
+
clearCommand,
|
|
15
|
+
easterEgg
|
|
16
|
+
} from '../commands/handlers.js';
|
|
17
|
+
import { resumeCommand } from '../commands/resume.js';
|
|
18
|
+
import { printHeader, printError, sleep, typeWriter } from '../utils/display.js';
|
|
19
|
+
import { showAnimatedBanner, showQuietBanner } from '../utils/banner.js';
|
|
20
|
+
|
|
21
|
+
const showWelcome = async () => {
|
|
22
|
+
console.clear();
|
|
23
|
+
|
|
24
|
+
// Copilot style spinner
|
|
25
|
+
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
26
|
+
let i = 0;
|
|
27
|
+
|
|
28
|
+
const interval = setInterval(() => {
|
|
29
|
+
process.stdout.write(`\r ${chalk.cyan(frames[i++ % frames.length])} Initializing...`);
|
|
30
|
+
}, 80);
|
|
31
|
+
|
|
32
|
+
await sleep(1500);
|
|
33
|
+
clearInterval(interval);
|
|
34
|
+
process.stdout.write('\r' + ' '.repeat(50) + '\r\n');
|
|
35
|
+
|
|
36
|
+
await showAnimatedBanner();
|
|
37
|
+
|
|
38
|
+
console.log(chalk.white(' Type ') + chalk.green.bold('help') + chalk.white(' to see available commands'));
|
|
39
|
+
console.log(chalk.dim(' Type ') + chalk.green.bold('exit') + chalk.dim(' to quit\n'));
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const processCommand = async (input) => {
|
|
43
|
+
const command = input.trim().toLowerCase();
|
|
44
|
+
|
|
45
|
+
if (!command) return true;
|
|
46
|
+
|
|
47
|
+
const spinner = ora({ text: 'Processing...', color: 'cyan', spinner: 'dots' });
|
|
48
|
+
|
|
49
|
+
// Command suggestions for typos
|
|
50
|
+
const suggestions = {
|
|
51
|
+
'skill': 'skills',
|
|
52
|
+
'project': 'projects',
|
|
53
|
+
'hire': 'why-hire-me',
|
|
54
|
+
'quit': 'exit',
|
|
55
|
+
'cls': 'clear',
|
|
56
|
+
'ls': 'help',
|
|
57
|
+
'info': 'about',
|
|
58
|
+
'cv': 'resume'
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
if (suggestions[command]) {
|
|
62
|
+
console.log(chalk.yellow(`\n💡 Did you mean "${suggestions[command]}"? Running it...\n`));
|
|
63
|
+
await sleep(400);
|
|
64
|
+
return await processCommand(suggestions[command]);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
switch (command) {
|
|
68
|
+
case 'help':
|
|
69
|
+
await helpCommand();
|
|
70
|
+
break;
|
|
71
|
+
case 'about':
|
|
72
|
+
await aboutCommand();
|
|
73
|
+
break;
|
|
74
|
+
case 'skills':
|
|
75
|
+
await skillsCommand();
|
|
76
|
+
break;
|
|
77
|
+
case 'projects':
|
|
78
|
+
await projectsCommand();
|
|
79
|
+
break;
|
|
80
|
+
case 'journey':
|
|
81
|
+
await journeyCommand();
|
|
82
|
+
break;
|
|
83
|
+
case 'why-hire-me':
|
|
84
|
+
await whyHireMeCommand();
|
|
85
|
+
break;
|
|
86
|
+
case 'resume':
|
|
87
|
+
await resumeCommand();
|
|
88
|
+
break;
|
|
89
|
+
case 'contact':
|
|
90
|
+
await contactCommand();
|
|
91
|
+
break;
|
|
92
|
+
case 'clear':
|
|
93
|
+
clearCommand();
|
|
94
|
+
break;
|
|
95
|
+
case 'exit':
|
|
96
|
+
case 'quit':
|
|
97
|
+
spinner.start('Shutting down...');
|
|
98
|
+
await sleep(600);
|
|
99
|
+
spinner.succeed(chalk.green('Thanks for visiting!'));
|
|
100
|
+
console.log(chalk.dim(' Good luck with your search.\n'));
|
|
101
|
+
return false;
|
|
102
|
+
case 'whoami':
|
|
103
|
+
case 'sudo hire-mells':
|
|
104
|
+
case 'coffee':
|
|
105
|
+
await easterEgg(command);
|
|
106
|
+
break;
|
|
107
|
+
default:
|
|
108
|
+
printError(`Command not found: "${input}". Type "help" for available commands.`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return true;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const startCLI = async () => {
|
|
115
|
+
await showWelcome();
|
|
116
|
+
|
|
117
|
+
const rl = readline.createInterface({
|
|
118
|
+
input: process.stdin,
|
|
119
|
+
output: process.stdout,
|
|
120
|
+
prompt: chalk.green.bold('❯ '),
|
|
121
|
+
terminal: process.stdin.isTTY
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
let processing = false;
|
|
125
|
+
|
|
126
|
+
rl.prompt();
|
|
127
|
+
|
|
128
|
+
rl.on('line', async (input) => {
|
|
129
|
+
if (processing) return;
|
|
130
|
+
processing = true;
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
const shouldContinue = await processCommand(input);
|
|
134
|
+
|
|
135
|
+
if (shouldContinue) {
|
|
136
|
+
rl.prompt();
|
|
137
|
+
} else {
|
|
138
|
+
rl.close();
|
|
139
|
+
}
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error(chalk.red('Error:'), error);
|
|
142
|
+
rl.prompt();
|
|
143
|
+
} finally {
|
|
144
|
+
processing = false;
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
rl.on('close', () => {
|
|
149
|
+
process.exit(0);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
rl.on('SIGINT', () => {
|
|
153
|
+
console.log(chalk.yellow('\n\n Use "exit" to quit gracefully.\n'));
|
|
154
|
+
rl.prompt();
|
|
155
|
+
});
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
startCLI().catch(error => {
|
|
159
|
+
console.error(chalk.red('✖ Error starting CLI:'), error);
|
|
160
|
+
process.exit(1);
|
|
161
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { content } from '../data/content.js';
|
|
4
|
+
import { printHeader, printSection, printSuccess, printBox, sleep, typeWriter } from '../utils/display.js';
|
|
5
|
+
|
|
6
|
+
export const helpCommand = async () => {
|
|
7
|
+
printHeader('Available Commands');
|
|
8
|
+
|
|
9
|
+
const commands = [
|
|
10
|
+
['help', 'Show this help message'],
|
|
11
|
+
['about', 'Learn about me'],
|
|
12
|
+
['skills', 'View my technical skills'],
|
|
13
|
+
['projects', 'See my featured projects'],
|
|
14
|
+
['journey', 'My CLI & DevOps journey'],
|
|
15
|
+
['why-hire-me', 'Why you should hire me'],
|
|
16
|
+
['resume', 'Open my resume'],
|
|
17
|
+
['contact', 'Get my contact info'],
|
|
18
|
+
['clear', 'Clear the screen'],
|
|
19
|
+
['exit', 'Exit the CLI']
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
for (const [cmd, desc] of commands) {
|
|
23
|
+
console.log(chalk.cyan(' ' + cmd.padEnd(15)) + chalk.white('→ ' + desc));
|
|
24
|
+
await sleep(50);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
console.log(chalk.dim.italic('\n 💡 Hint: Try some hidden commands too...\n'));
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const aboutCommand = async () => {
|
|
31
|
+
await printSection('About Me', content.about);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const skillsCommand = async () => {
|
|
35
|
+
await printSection('Technical Skills', content.skills);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const projectsCommand = async () => {
|
|
39
|
+
await printSection('Featured Projects', content.projects);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const journeyCommand = async () => {
|
|
43
|
+
await printSection('My Journey', content.journey);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const whyHireMeCommand = async () => {
|
|
47
|
+
await printSection('Why Hire Me', content.whyHireMe);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const contactCommand = async () => {
|
|
51
|
+
console.log('\n' + chalk.cyan.bold('Contact Information'));
|
|
52
|
+
console.log(chalk.cyan('─'.repeat(60)) + '\n');
|
|
53
|
+
|
|
54
|
+
const lines = content.contact.trim().split('\n');
|
|
55
|
+
for (const line of lines) {
|
|
56
|
+
await typeWriter(chalk.white(line), 10);
|
|
57
|
+
console.log();
|
|
58
|
+
await sleep(30);
|
|
59
|
+
}
|
|
60
|
+
console.log();
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const clearCommand = () => {
|
|
64
|
+
console.clear();
|
|
65
|
+
printSuccess('Screen cleared!');
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const easterEgg = async (command) => {
|
|
69
|
+
const egg = content.easterEggs[command];
|
|
70
|
+
if (egg) {
|
|
71
|
+
console.log(chalk.magenta.bold(`\n${egg}\n`));
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import open from 'open';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { content } from '../data/content.js';
|
|
4
|
+
import { printError } from '../utils/display.js';
|
|
5
|
+
|
|
6
|
+
export const resumeCommand = async () => {
|
|
7
|
+
console.log(chalk.cyan('\n⏳ Opening resume in browser...'));
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
await open(content.resume.url);
|
|
11
|
+
console.log(chalk.green('✔ Resume opened successfully!\n'));
|
|
12
|
+
} catch (error) {
|
|
13
|
+
console.log(chalk.red('✖ Failed to open resume\n'));
|
|
14
|
+
printError('Could not open browser. Please visit: ' + content.resume.url);
|
|
15
|
+
}
|
|
16
|
+
};
|
package/data/content.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
export const content = {
|
|
2
|
+
about: `
|
|
3
|
+
I'm Melbin Kuriakose, a Computer Science graduate with hands-on DevOps
|
|
4
|
+
training. I specialize in Linux system administration, cloud platforms
|
|
5
|
+
(AWS, GCP), and infrastructure automation. Currently pursuing a
|
|
6
|
+
Postgraduate Diploma in DevOps & Cloud Computing.
|
|
7
|
+
|
|
8
|
+
✔ Strong foundation in Linux and system administration
|
|
9
|
+
✔ Hands-on with Docker, Kubernetes, Terraform, and Ansible
|
|
10
|
+
✔ CI/CD pipeline implementation using Jenkins and GitHub
|
|
11
|
+
✔ Seeking Linux System Administrator roles
|
|
12
|
+
`,
|
|
13
|
+
|
|
14
|
+
skills: `
|
|
15
|
+
Technical Skills:
|
|
16
|
+
|
|
17
|
+
Languages & Scripting
|
|
18
|
+
• Python, Bash, Shell Scripting
|
|
19
|
+
|
|
20
|
+
Cloud & Infrastructure
|
|
21
|
+
• AWS, GCP, Terraform, Ansible
|
|
22
|
+
• VPC, EC2, ECS, RDS, ALB
|
|
23
|
+
|
|
24
|
+
CI/CD & Automation
|
|
25
|
+
• Jenkins, GitHub, n8n Automation, MCP Servers
|
|
26
|
+
|
|
27
|
+
Containerization & Orchestration
|
|
28
|
+
• Docker, Kubernetes
|
|
29
|
+
|
|
30
|
+
Monitoring & Observability
|
|
31
|
+
• Prometheus, Grafana, Alertmanager
|
|
32
|
+
|
|
33
|
+
Soft Skills
|
|
34
|
+
• Leadership, Creative Thinking, Teamwork
|
|
35
|
+
• Communication, Time Management
|
|
36
|
+
`,
|
|
37
|
+
|
|
38
|
+
projects: `
|
|
39
|
+
Featured Projects:
|
|
40
|
+
|
|
41
|
+
1. Infrastructure Automation with Terraform
|
|
42
|
+
→ Built highly available AWS infrastructure (VPC, EC2, ECS, RDS, ALB)
|
|
43
|
+
→ Multi-AZ deployment for fault tolerance
|
|
44
|
+
→ Secured with private subnets, NAT, security groups
|
|
45
|
+
→ Auto-scaling and least-privilege access
|
|
46
|
+
GitHub: github.com/mellowbricks
|
|
47
|
+
|
|
48
|
+
2. Ansible Node.js Deployment
|
|
49
|
+
→ Automated Ubuntu server provisioning with Ansible
|
|
50
|
+
→ IaC-driven deployment for Node.js applications
|
|
51
|
+
→ Consistent, repeatable development environments
|
|
52
|
+
→ Reduced configuration drift and manual errors
|
|
53
|
+
GitHub: github.com/mellowbricks
|
|
54
|
+
|
|
55
|
+
3. Jenkins Automated Deployment
|
|
56
|
+
→ CI/CD pipeline for Java applications
|
|
57
|
+
→ Nexus artifact management + SSH-based EC2 deployment
|
|
58
|
+
→ Reduced deployment time from 15 min to under 2 min
|
|
59
|
+
→ Zero-touch automation with post-build triggers
|
|
60
|
+
GitHub: github.com/mellowbricks
|
|
61
|
+
|
|
62
|
+
4. Phishing & Session Hijacking Analysis (Evilginx2)
|
|
63
|
+
→ Security research using Evilginx2 on Kali Linux
|
|
64
|
+
→ Man-in-the-Middle attack simulation
|
|
65
|
+
→ Educational cybersecurity analysis
|
|
66
|
+
`,
|
|
67
|
+
|
|
68
|
+
journey: `
|
|
69
|
+
My DevOps Journey:
|
|
70
|
+
|
|
71
|
+
→ Started with Computer Science fundamentals (B.Sc. CGPA: 8.30)
|
|
72
|
+
→ Linux Admin Intern at CoreXtech IT Services (Apr-Jul 2025)
|
|
73
|
+
→ Completed DevOps & Cloud Computing training (Apr-Nov 2025)
|
|
74
|
+
→ Built production-grade infrastructure with Terraform & Ansible
|
|
75
|
+
→ Implemented CI/CD pipelines reducing deployment time by 87%
|
|
76
|
+
→ Graphics Team Member & E-Publicity Head at PCACS College
|
|
77
|
+
→ Completed Deloitte & Accenture job simulations (Forage)
|
|
78
|
+
→ Now building this interactive CLI portfolio!
|
|
79
|
+
`,
|
|
80
|
+
|
|
81
|
+
whyHireMe: `
|
|
82
|
+
Why Hire Me?
|
|
83
|
+
|
|
84
|
+
✔ Proven Track Record → Reduced deployment time by 87% through automation
|
|
85
|
+
✔ Cloud Native → Hands-on with AWS, GCP, Terraform, Kubernetes
|
|
86
|
+
✔ Security Minded → Implemented least-privilege access & secure architectures
|
|
87
|
+
✔ Fast Learner → Completed intensive DevOps training with real projects
|
|
88
|
+
✔ Team Leader → Led college publicity team, boosted participation by 20%
|
|
89
|
+
✔ Problem Solver → Built fault-tolerant, auto-scaling infrastructure
|
|
90
|
+
|
|
91
|
+
I don't just write scripts—I build reliable, scalable cloud infrastructure.
|
|
92
|
+
`,
|
|
93
|
+
|
|
94
|
+
contact: `
|
|
95
|
+
Let's Connect:
|
|
96
|
+
|
|
97
|
+
• Email: melbinmk04@gmail.com
|
|
98
|
+
• LinkedIn: linkedin.com/in/melbinkuriakose
|
|
99
|
+
• GitHub: github.com/mellowbricks
|
|
100
|
+
• Portfolio: mellowbricks.co.in
|
|
101
|
+
• Phone: +91 8976345285
|
|
102
|
+
• Location: Thane, Maharashtra
|
|
103
|
+
`,
|
|
104
|
+
|
|
105
|
+
resume: {
|
|
106
|
+
url: 'https://www.mellowbricks.co.in/Resume.pdf',
|
|
107
|
+
message: 'Opening resume in your browser...'
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
easterEggs: {
|
|
111
|
+
whoami: '→ Melbin Kuriakose | Future Linux SysAdmin & DevOps Engineer 🚀',
|
|
112
|
+
'sudo hire-mells': '✔ Access granted! Initiating hiring process... Welcome aboard! 🎉',
|
|
113
|
+
coffee: '☕ Brewing motivation... 100% ready to automate all the things!'
|
|
114
|
+
}
|
|
115
|
+
};
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
%PDF-1.4
|
|
2
|
+
%���� ReportLab Generated PDF document http://www.reportlab.com
|
|
3
|
+
1 0 obj
|
|
4
|
+
<<
|
|
5
|
+
/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 5 0 R /F5 7 0 R /F6 9 0 R
|
|
6
|
+
>>
|
|
7
|
+
endobj
|
|
8
|
+
2 0 obj
|
|
9
|
+
<<
|
|
10
|
+
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
|
|
11
|
+
>>
|
|
12
|
+
endobj
|
|
13
|
+
3 0 obj
|
|
14
|
+
<<
|
|
15
|
+
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
|
|
16
|
+
>>
|
|
17
|
+
endobj
|
|
18
|
+
4 0 obj
|
|
19
|
+
<<
|
|
20
|
+
/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font
|
|
21
|
+
>>
|
|
22
|
+
endobj
|
|
23
|
+
5 0 obj
|
|
24
|
+
<<
|
|
25
|
+
/BaseFont /Helvetica-BoldOblique /Encoding /WinAnsiEncoding /Name /F4 /Subtype /Type1 /Type /Font
|
|
26
|
+
>>
|
|
27
|
+
endobj
|
|
28
|
+
6 0 obj
|
|
29
|
+
<<
|
|
30
|
+
/Contents 15 0 R /MediaBox [ 0 0 612 792 ] /Parent 14 0 R /Resources <<
|
|
31
|
+
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
|
32
|
+
>> /Rotate 0 /Trans <<
|
|
33
|
+
|
|
34
|
+
>>
|
|
35
|
+
/Type /Page
|
|
36
|
+
>>
|
|
37
|
+
endobj
|
|
38
|
+
7 0 obj
|
|
39
|
+
<<
|
|
40
|
+
/BaseFont /Symbol /Name /F5 /Subtype /Type1 /Type /Font
|
|
41
|
+
>>
|
|
42
|
+
endobj
|
|
43
|
+
8 0 obj
|
|
44
|
+
<<
|
|
45
|
+
/Contents 16 0 R /MediaBox [ 0 0 612 792 ] /Parent 14 0 R /Resources <<
|
|
46
|
+
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
|
47
|
+
>> /Rotate 0 /Trans <<
|
|
48
|
+
|
|
49
|
+
>>
|
|
50
|
+
/Type /Page
|
|
51
|
+
>>
|
|
52
|
+
endobj
|
|
53
|
+
9 0 obj
|
|
54
|
+
<<
|
|
55
|
+
/BaseFont /ZapfDingbats /Name /F6 /Subtype /Type1 /Type /Font
|
|
56
|
+
>>
|
|
57
|
+
endobj
|
|
58
|
+
10 0 obj
|
|
59
|
+
<<
|
|
60
|
+
/Contents 17 0 R /MediaBox [ 0 0 612 792 ] /Parent 14 0 R /Resources <<
|
|
61
|
+
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
|
62
|
+
>> /Rotate 0 /Trans <<
|
|
63
|
+
|
|
64
|
+
>>
|
|
65
|
+
/Type /Page
|
|
66
|
+
>>
|
|
67
|
+
endobj
|
|
68
|
+
11 0 obj
|
|
69
|
+
<<
|
|
70
|
+
/Contents 18 0 R /MediaBox [ 0 0 612 792 ] /Parent 14 0 R /Resources <<
|
|
71
|
+
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
|
72
|
+
>> /Rotate 0 /Trans <<
|
|
73
|
+
|
|
74
|
+
>>
|
|
75
|
+
/Type /Page
|
|
76
|
+
>>
|
|
77
|
+
endobj
|
|
78
|
+
12 0 obj
|
|
79
|
+
<<
|
|
80
|
+
/PageMode /UseNone /Pages 14 0 R /Type /Catalog
|
|
81
|
+
>>
|
|
82
|
+
endobj
|
|
83
|
+
13 0 obj
|
|
84
|
+
<<
|
|
85
|
+
/Author (\(anonymous\)) /CreationDate (D:20260222103249+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260222103249+00'00') /Producer (ReportLab PDF Library - www.reportlab.com)
|
|
86
|
+
/Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False
|
|
87
|
+
>>
|
|
88
|
+
endobj
|
|
89
|
+
14 0 obj
|
|
90
|
+
<<
|
|
91
|
+
/Count 4 /Kids [ 6 0 R 8 0 R 10 0 R 11 0 R ] /Type /Pages
|
|
92
|
+
>>
|
|
93
|
+
endobj
|
|
94
|
+
15 0 obj
|
|
95
|
+
<<
|
|
96
|
+
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1126
|
|
97
|
+
>>
|
|
98
|
+
stream
|
|
99
|
+
GauI59lJcG&;KZL'm!Nn?.c/Jad0T[D:75A=X9K8kf"?m@OsDMl&q:eo[iP.ri"jJEiR$oJ0Bk'^(6"f%#7*(+4>Q.$K05=hL?e9-tJB(iZO!B)QpB(D\lsYcQn4tSDp.q(,dnR]YN.?@3p<!_Gr"Ge?;3<?Xk/$P:?KujY6O$Pbj'Ef:QFP[moO<GAnqDe^B7G-thL:o@cFF@WZs@\TmiYZEKAg%[3Yf&F*X#]ECn*/m_!,n85[TZg$!"N^s./CLPdlD"d.rMDT3L%!r`2:(m.<2NgdhNm]+'0Ek?7@2O3//V*/e@Oe"7;@-H>iuW=l2)q^2E!:<ub#3./(r8ounN@965]-IPdicB.8o4^8rE_r5=-GXc@R2HNYVQ&PQ3,Yn\m?/f!8"0F@cc[%=crRrKTST@&bP0p]!]Ol2!rNmD,&(6"s,e@eEW*$dA0ca('RG1[p('u[AfO7,/7]D[:2N[#gt:q"H@EM?u#qRq@8cTRgCb0$]^R"e",?0SBccDHKhY$<f"a(-*%b6`%+L5!h!Fp#X$`8OK8=O5;<)D`3WG=)!mGSqQW9GYC\i`1;[LON=Tnl1I.dV=luM\r?k91lSdc`l/-*"S^@H:d]Bg(P&W6oQe\5&Y8M+AOp(1\^#A0kYg-piCbjf\!V[pjm_tT;i5ENQ/k)TOU0p:HoD3'1hd9#VU[b/&X`1s9;$RUPc@]tZ/TY_Fkkn$lp40j8fR/Gie-e`,^r#&<=/o^)+cN!`bS^%:oXs+i2_M0X""`%fW+b#t'rAgA^1d*@N#QUcI),p&:-=8gnXKD11rn5VeD]gDSdm3J."I8EV/Xd<csGg^B0)$9A8j$dIU59tOV>B(P-YdKK2GfH%[!BMH7N.N:.FEaao7Sq@\Mg>b*HZQ6dlP@A@VPMKlpn,FO.bn8m:QZC4mZ_cI'isBVr[]YfDPtYKeT;*QmHeQ.Bit3.R'7Qha6B9,p`T]b"WL]h7.+)"D%'h*I."fj?1@HJ:QMFm)TH>]f@Is0/'PH3Y<#G=Y2P'*%u$)+&OnVf>&4nR(&IV6m:d4^6Qp&dMQj#hEGsiabs^Tli<L@aDuADK,T)ERPb1X^(<c6Gdp$,^g?n.t^[nF9HROkH#>B\G-Rd*Ah"~>endstream
|
|
100
|
+
endobj
|
|
101
|
+
16 0 obj
|
|
102
|
+
<<
|
|
103
|
+
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1140
|
|
104
|
+
>>
|
|
105
|
+
stream
|
|
106
|
+
GatUrD3*C1&BE]".H\lHD3(uJ,b\*d[<oO;Pu&Q?WH9do3Z2Q108Z7-P@aR`*@K3?&M[K6U_"N$s3Ka*N5l0a5(7S7"i,m2>[70a#;BA4+,qJ3:#ThFGSp4fR7RaB2l=]LYJ6*X-cmd@j*uI(\59bE<JB*BVV/b#B'JE-rdOl:3/19;Z\%g98?H-3Io#%B48A7jbjJp5asnL5UcXjZnK`"m+-*@H&@>WJ2Qp7fctM828HS?@a0GcLMLrsor$$S<1[^?cc^2CW#!ke4dYLLXU/O;Ack"ISs%&'n/\r[T77Q0G?,9DpNf,sE[n`Hd"3:#*KbOE(>([KJ6F]u5l#M)#iXR?S4e/an]S*m>(dfq"o,J?d3$ReL#W>bk"V$6KPu_&4/"n6D]st]Y;q2c.C'Zq'F[f`m3GO2ZetUk.i@fTV>ai9k:?E$&`B@I:efrF7$Z/pZ"&gB/-fS!NNHtjCX,YPC+dYR+SDHN-<,Ql;E3aXh/G@D&/[`/[?CCh1F^T-_T2.A>>![ji7Iau;m3M=a'1S2W$1Clr-#QpP)m8qIL]K])\^ISL_k1=(9$o9Y0PZ0%43L.DOY]jqSCt!PY?+'NlN2:B:QC]5TP@B"%;;_W>uT/Xer`BuO"A^ml91=\;=4[BKXk,&KA&oHOX!_a*8sMQR<G!X"V^^YVTBa_)``Sd7J3pW4./-M+mP0t4@c8QUA!/WNTf6#%`=&b%$+1n*uB8oQ?Npo*Bjtn`(^fN"@7+g&4,&j`$;d$VEt_nbN6&?T.ohs!sFf/M545sTTQptNXo_)nTafhi*EjqX.%=-.f?csWV45p>RH=*H\Xo-!ke+@@-UG%Xq]Npi3ucs)_ki=mieZQFQ.YfnUpI]I'+4m`8@KlTQ*?>)AJ(?2g24L"_e3eI%k15DVagg^AWEKm0<R+LRLP::i#!2DQp<OLZ:[FIKuq;8AYe6CU(Q_D/;3,MWN-)1[mIs]^n^,gX9gm.>h^b%,GNW/!a%njFZnLa'nnQ7p)^]8h_RO/Q5.;j_9gP"!fp)>`\b,)8V9C(b%GfTC!5??c<Mqr=e;cGVF#P#PA9bW=2;k#2[&d;l@3N>^^XYpf,HpUX$rnBcT40o=p-6^<j5+3E=3F:d[*VH3>$cq?%*0hZ;&EN`Q~>endstream
|
|
107
|
+
endobj
|
|
108
|
+
17 0 obj
|
|
109
|
+
<<
|
|
110
|
+
/Filter [ /ASCII85Decode /FlateDecode ] /Length 951
|
|
111
|
+
>>
|
|
112
|
+
stream
|
|
113
|
+
GatUr9lJc?%#46H'tat=L=WWjmp_V9CPQ?;RNWPpfu7s\<37a!P2%A`I['5A88;q=$Ni]3LjC/*'PRV$5,A"u$@koFQsL@I'UEr.I)h$(2-_a]^F,g])9!/t'GmOhD/=5K5r\AI'D`L3/oJ!'21(k;A?"']Q)FJ]Pf5Z<Ej77sW)G+mlt15Ef3Hh[pTT$qjThRU".o\G0_(.fYr1rO?6Zbns$6:bE&)(S5boT6kFcoHF^;gRoMR4j`EHD!U0\8c[tVb=6UEN'9h=7j,d@Gm=['&`_a;O-e$W^eBc?_:#>sD90mGXDX>uTtCctR*g'cqpi;6:)!dP=rT%Ib.$C9l?/Bdj2O"n<T&!mPFRf?^h_MX7#k*0_R#;s@].go%l>)eZI2:"cQY#Cld1$+i14V#(rZc@1*d('hq\NA#4f]D>LmWJ6T'i*c(XG)B^2.*\[$YD!EJ0b*iDIhAf^WXA_'Yu`J.a4@&\?]/;TCcprnTaGnMSZ=<F/GY3GV1mTLhHc4HlIam'rs)kORksH?&=5iC]2P+[)IK:E3qD1_>sldC-?&\E(6:T.5KmDY[CTc\_``p;cm]geJu>,.>ZSOiKDTNra#Z\C*lB<K?>IC2#]s$"(Pj2P("0"*m\jOnf<r0'7_Z=@Ah*"MaqPR]P%9i*05+0<:Vol.nkl`:Sn\cV1:DGR'OU4"#/4D<@,S7c`G:4?")I76F4fe^[F-PFrD`'po5cTf(TV.;&)pXX.66f4UBcMn3#K\gGFmNLh"'Sfj>U9lG-D:VE.t>Hh4[S@UooYot5_T9'c\h;Sd.Tdl^Q2YrJlbdjZ^`+E5@7/FW,ieoR%N?^8QJ3-VLJZb"!kcTm_`hf1!F%+,&B31f)pXmgoti&IhtD>1C]"9SqZ)c_E[!uKT%.W(%RB%Y7ojJeCEmq3_.&r_jem.r6R=t"XT.H!q-aiD`m0_l*_am9!~>endstream
|
|
114
|
+
endobj
|
|
115
|
+
18 0 obj
|
|
116
|
+
<<
|
|
117
|
+
/Filter [ /ASCII85Decode /FlateDecode ] /Length 244
|
|
118
|
+
>>
|
|
119
|
+
stream
|
|
120
|
+
Gat=d0b8ji&;BlXMDqPl8eEMT)P(+i20!SL&/7K9U_*R?/J8^+@3ColnFH!qG=UWFU.p[pYi_>A.&[ZJJd/O]I#fj,:I885MnhI4R]Y^0>'V?9N.-uWXSel=9oABp7Mj2a2Hi_HXp/UBi[WDLjC@Zapr#H.[XLL#.WD&12&_?:-Dm(8FHJ!#b@R7"E:DPW2J-.RU.TPWHcaH+E\NlN-UQd6`[e+X&1Wm\]tOIB7CrZC)hB2Y]`~>endstream
|
|
121
|
+
endobj
|
|
122
|
+
xref
|
|
123
|
+
0 19
|
|
124
|
+
0000000000 65535 f
|
|
125
|
+
0000000073 00000 n
|
|
126
|
+
0000000154 00000 n
|
|
127
|
+
0000000261 00000 n
|
|
128
|
+
0000000373 00000 n
|
|
129
|
+
0000000478 00000 n
|
|
130
|
+
0000000597 00000 n
|
|
131
|
+
0000000792 00000 n
|
|
132
|
+
0000000869 00000 n
|
|
133
|
+
0000001064 00000 n
|
|
134
|
+
0000001147 00000 n
|
|
135
|
+
0000001343 00000 n
|
|
136
|
+
0000001539 00000 n
|
|
137
|
+
0000001609 00000 n
|
|
138
|
+
0000001893 00000 n
|
|
139
|
+
0000001973 00000 n
|
|
140
|
+
0000003191 00000 n
|
|
141
|
+
0000004423 00000 n
|
|
142
|
+
0000005465 00000 n
|
|
143
|
+
trailer
|
|
144
|
+
<<
|
|
145
|
+
/ID
|
|
146
|
+
[<fd51dcc6bed0ec449a0ffaef4eb144c6><fd51dcc6bed0ec449a0ffaef4eb144c6>]
|
|
147
|
+
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
|
|
148
|
+
|
|
149
|
+
/Info 13 0 R
|
|
150
|
+
/Root 12 0 R
|
|
151
|
+
/Size 19
|
|
152
|
+
>>
|
|
153
|
+
startxref
|
|
154
|
+
5800
|
|
155
|
+
%%EOF
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mells-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Interactive DevOps portfolio CLI - Meet Melbin Kuriakose",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "bin/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"mells-cli": "./bin/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node bin/index.js"
|
|
12
|
+
},
|
|
13
|
+
"keywords": ["cli", "portfolio", "devops", "linux", "interactive", "terminal"],
|
|
14
|
+
"author": "Melbin Kuriakose <melbinmk04@gmail.com>",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/mellowbricks/mells-cli"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://mellowbricks.co.in",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=18.0.0"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"chalk": "^5.3.0",
|
|
26
|
+
"ora": "^8.0.1",
|
|
27
|
+
"open": "^10.0.3"
|
|
28
|
+
}
|
|
29
|
+
}
|
package/test-cli.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import readline from 'readline';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
|
|
6
|
+
const rl = readline.createInterface({
|
|
7
|
+
input: process.stdin,
|
|
8
|
+
output: process.stdout,
|
|
9
|
+
prompt: chalk.green('❯ ')
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
console.log('Test CLI - type commands');
|
|
13
|
+
rl.prompt();
|
|
14
|
+
|
|
15
|
+
rl.on('line', async (input) => {
|
|
16
|
+
const cmd = input.trim();
|
|
17
|
+
|
|
18
|
+
if (cmd === 'test') {
|
|
19
|
+
console.log('Test output');
|
|
20
|
+
} else if (cmd === 'exit') {
|
|
21
|
+
console.log('Goodbye');
|
|
22
|
+
rl.close();
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
rl.prompt();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
rl.on('close', () => {
|
|
30
|
+
process.exit(0);
|
|
31
|
+
});
|
package/utils/banner.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { sleep } from './display.js';
|
|
3
|
+
|
|
4
|
+
const bannerFrames = [
|
|
5
|
+
{
|
|
6
|
+
line1: '███╗ ███╗███████╗██╗ ██╗ ███████╗',
|
|
7
|
+
line2: '████╗ ████║██╔════╝██║ ██║ ██╔════╝',
|
|
8
|
+
line3: '██╔████╔██║█████╗ ██║ ██║ ███████╗',
|
|
9
|
+
line4: '██║╚██╔╝██║██╔══╝ ██║ ██║ ╚════██║',
|
|
10
|
+
line5: '██║ ╚═╝ ██║███████╗███████╗███████╗███████║',
|
|
11
|
+
line6: '╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝╚══════╝'
|
|
12
|
+
}
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const animatedBanner = [
|
|
16
|
+
// Frame 1: Lines slide in from left
|
|
17
|
+
[
|
|
18
|
+
'█ ',
|
|
19
|
+
'██ ',
|
|
20
|
+
'███ ',
|
|
21
|
+
'████ ',
|
|
22
|
+
'█████ ',
|
|
23
|
+
'██████ '
|
|
24
|
+
],
|
|
25
|
+
// Frame 2
|
|
26
|
+
[
|
|
27
|
+
'███╗ ',
|
|
28
|
+
'████╗ ',
|
|
29
|
+
'██╔██ ',
|
|
30
|
+
'██║╚█ ',
|
|
31
|
+
'██║ ╚ ',
|
|
32
|
+
'╚═╝ '
|
|
33
|
+
],
|
|
34
|
+
// Frame 3
|
|
35
|
+
[
|
|
36
|
+
'███╗ ███╗ ',
|
|
37
|
+
'████╗ ████║ ',
|
|
38
|
+
'██╔████╔██║ ',
|
|
39
|
+
'██║╚██╔╝██║ ',
|
|
40
|
+
'██║ ╚═╝ ██║ ',
|
|
41
|
+
'╚═╝ ╚═╝ '
|
|
42
|
+
],
|
|
43
|
+
// Frame 4
|
|
44
|
+
[
|
|
45
|
+
'███╗ ███╗███████╗ ',
|
|
46
|
+
'████╗ ████║██╔════╝ ',
|
|
47
|
+
'██╔████╔██║█████╗ ',
|
|
48
|
+
'██║╚██╔╝██║██╔══╝ ',
|
|
49
|
+
'██║ ╚═╝ ██║███████╗ ',
|
|
50
|
+
'╚═╝ ╚═╝╚══════╝ '
|
|
51
|
+
],
|
|
52
|
+
// Frame 5
|
|
53
|
+
[
|
|
54
|
+
'███╗ ███╗███████╗██╗ ██╗ ███████╗ ',
|
|
55
|
+
'████╗ ████║██╔════╝██║ ██║ ██╔════╝ ',
|
|
56
|
+
'██╔████╔██║█████╗ ██║ ██║ ███████╗ ',
|
|
57
|
+
'██║╚██╔╝██║██╔══╝ ██║ ██║ ╚════██║ ',
|
|
58
|
+
'██║ ╚═╝ ██║███████╗███████╗███████╗███████║ ',
|
|
59
|
+
'╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝╚══════╝ '
|
|
60
|
+
]
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
export const showAnimatedBanner = async () => {
|
|
64
|
+
const padding = 2;
|
|
65
|
+
const paddingStr = ' '.repeat(padding);
|
|
66
|
+
|
|
67
|
+
// Animate banner drawing
|
|
68
|
+
for (let frame of animatedBanner) {
|
|
69
|
+
console.clear();
|
|
70
|
+
console.log(chalk.cyan('\n'));
|
|
71
|
+
frame.forEach(line => {
|
|
72
|
+
console.log(chalk.cyan(paddingStr + line));
|
|
73
|
+
});
|
|
74
|
+
await sleep(120);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Hold final frame
|
|
78
|
+
await sleep(300);
|
|
79
|
+
|
|
80
|
+
console.log(chalk.cyan(`
|
|
81
|
+
${chalk.gray('━'.repeat(45))}
|
|
82
|
+
|
|
83
|
+
${chalk.white('Melbin Kuriakose')}
|
|
84
|
+
${chalk.white('DevOps Engineer')} ${chalk.dim('|')} ${chalk.white('Linux System Administrator')}
|
|
85
|
+
|
|
86
|
+
${chalk.dim('CLI v1.0.0')}
|
|
87
|
+
`));
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export const showQuietBanner = () => {
|
|
91
|
+
const padding = 2;
|
|
92
|
+
const paddingStr = ' '.repeat(padding);
|
|
93
|
+
|
|
94
|
+
const finalFrame = [
|
|
95
|
+
'███╗ ███╗███████╗██╗ ██╗ ███████╗',
|
|
96
|
+
'████╗ ████║██╔════╝██║ ██║ ██╔════╝',
|
|
97
|
+
'██╔████╔██║█████╗ ██║ ██║ ███████╗',
|
|
98
|
+
'██║╚██╔╝██║██╔══╝ ██║ ██║ ╚════██║',
|
|
99
|
+
'██║ ╚═╝ ██║███████╗███████╗███████╗███████║',
|
|
100
|
+
'╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝╚══════╝'
|
|
101
|
+
];
|
|
102
|
+
|
|
103
|
+
console.clear();
|
|
104
|
+
console.log(chalk.cyan('\n'));
|
|
105
|
+
finalFrame.forEach(line => {
|
|
106
|
+
console.log(chalk.cyan(paddingStr + line));
|
|
107
|
+
});
|
|
108
|
+
console.log(chalk.cyan(`
|
|
109
|
+
${chalk.gray('━'.repeat(45))}
|
|
110
|
+
|
|
111
|
+
${chalk.white('Melbin Kuriakose')} ${chalk.yellow('👋')}
|
|
112
|
+
${chalk.white('DevOps Engineer')} ${chalk.dim('|')} ${chalk.white('Linux System Administrator')}
|
|
113
|
+
|
|
114
|
+
${chalk.dim('CLI v1.0.0')}
|
|
115
|
+
`));
|
|
116
|
+
};
|
package/utils/display.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
|
|
4
|
+
export const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
5
|
+
|
|
6
|
+
export const typeWriter = async (text, delay = 15) => {
|
|
7
|
+
for (const char of text) {
|
|
8
|
+
process.stdout.write(char);
|
|
9
|
+
await sleep(delay);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const printHeader = (text) => {
|
|
14
|
+
console.log('\n' + chalk.cyan.bold('═'.repeat(60)));
|
|
15
|
+
console.log(chalk.cyan.bold(` ${text}`));
|
|
16
|
+
console.log(chalk.cyan.bold('═'.repeat(60)) + '\n');
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const printSection = async (title, content) => {
|
|
20
|
+
console.log('\n' + chalk.cyan.bold(title));
|
|
21
|
+
console.log(chalk.cyan('─'.repeat(60)) + '\n');
|
|
22
|
+
|
|
23
|
+
const lines = content.trim().split('\n');
|
|
24
|
+
for (const line of lines) {
|
|
25
|
+
await typeWriter(chalk.white(line), 10);
|
|
26
|
+
console.log();
|
|
27
|
+
await sleep(30);
|
|
28
|
+
}
|
|
29
|
+
console.log();
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const printError = (message) => {
|
|
33
|
+
console.log(chalk.red.bold(`\n✖ ${message}\n`));
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const printSuccess = (message) => {
|
|
37
|
+
console.log(chalk.green.bold(`\n✔ ${message}\n`));
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const printBox = (lines) => {
|
|
41
|
+
const maxLen = Math.max(...lines.map(l => l.length));
|
|
42
|
+
const width = maxLen + 4;
|
|
43
|
+
|
|
44
|
+
console.log(chalk.cyan('┌' + '─'.repeat(width) + '┐'));
|
|
45
|
+
lines.forEach(line => {
|
|
46
|
+
const padding = ' '.repeat(width - line.length - 2);
|
|
47
|
+
console.log(chalk.cyan('│ ') + chalk.white(line) + padding + chalk.cyan(' │'));
|
|
48
|
+
});
|
|
49
|
+
console.log(chalk.cyan('└' + '─'.repeat(width) + '┘'));
|
|
50
|
+
};
|