roast-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/SETUP.md ADDED
@@ -0,0 +1,108 @@
1
+ # Setup Guide
2
+
3
+ ## Quick Start
4
+
5
+ 1. **Install dependencies**
6
+ ```bash
7
+ cd ~/muin/projects/roast
8
+ npm install
9
+ ```
10
+
11
+ 2. **Set up API key**
12
+ ```bash
13
+ export ANTHROPIC_API_KEY="your-key-here"
14
+ ```
15
+
16
+ Get your key at: https://console.anthropic.com/settings/keys
17
+
18
+ 3. **Test it out**
19
+ ```bash
20
+ # Try the bubble sort example
21
+ node bin/roast.js examples/bubble-sort.js
22
+
23
+ # Try serious mode
24
+ node bin/roast.js --serious examples/sql-injection.py
25
+
26
+ # Try the bad React code
27
+ node bin/roast.js examples/bad-react.jsx
28
+ ```
29
+
30
+ 4. **Install globally (optional)**
31
+ ```bash
32
+ npm link
33
+ # Now you can use `roast` anywhere
34
+ roast examples/bubble-sort.js
35
+ ```
36
+
37
+ ## Development
38
+
39
+ ### Project Structure
40
+ ```
41
+ roast/
42
+ ├── bin/roast.js # CLI entry point
43
+ ├── lib/roast.js # Core logic
44
+ ├── examples/ # Test files
45
+ │ ├── bubble-sort.js
46
+ │ ├── bad-react.jsx
47
+ │ └── sql-injection.py
48
+ ├── docs/
49
+ │ └── style-guide.md # Output guidelines
50
+ ├── package.json
51
+ └── README.md
52
+ ```
53
+
54
+ ### Testing
55
+
56
+ Run the CLI on different files:
57
+ ```bash
58
+ node bin/roast.js examples/bubble-sort.js
59
+ node bin/roast.js examples/bad-react.jsx --serious
60
+ node bin/roast.js examples/sql-injection.py
61
+ ```
62
+
63
+ ### Making Changes
64
+
65
+ 1. Edit `lib/roast.js` for core logic
66
+ 2. Edit `bin/roast.js` for CLI options
67
+ 3. Update prompts in `ROAST_PROMPT` or `SERIOUS_PROMPT`
68
+ 4. Test with examples before committing
69
+
70
+ ## Publishing to npm
71
+
72
+ 1. **Create npm account** (if needed)
73
+ ```bash
74
+ npm login
75
+ ```
76
+
77
+ 2. **Update version**
78
+ ```bash
79
+ npm version patch # 0.1.0 → 0.1.1
80
+ npm version minor # 0.1.0 → 0.2.0
81
+ npm version major # 0.1.0 → 1.0.0
82
+ ```
83
+
84
+ 3. **Publish**
85
+ ```bash
86
+ npm publish --access public
87
+ ```
88
+
89
+ 4. **Test installation**
90
+ ```bash
91
+ npm install -g @muin/roast
92
+ roast --version
93
+ ```
94
+
95
+ ## Tips
96
+
97
+ - Keep prompts under 500 tokens for speed
98
+ - Test with various languages
99
+ - Screenshots for README examples
100
+ - Update style guide as you refine the tone
101
+
102
+ ## Next Steps
103
+
104
+ - [ ] Git repo initialization
105
+ - [ ] Push to GitHub
106
+ - [ ] Create demo GIF/video
107
+ - [ ] Tweet about it
108
+ - [ ] Publish to npm
package/bin/roast.js ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { program } from 'commander';
4
+ import { roastFile } from '../lib/roast.js';
5
+ import chalk from 'chalk';
6
+ import { readFileSync } from 'fs';
7
+ import { fileURLToPath } from 'url';
8
+ import { dirname, join } from 'path';
9
+
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = dirname(__filename);
12
+ const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));
13
+
14
+ program
15
+ .name('roast')
16
+ .description('AI code reviewer that roasts your code (with love)')
17
+ .version(packageJson.version)
18
+ .argument('<file>', 'code file to roast')
19
+ .option('-s, --serious', 'serious mode (professional review)')
20
+ .option('--severity <level>', 'roast severity: mild, medium, harsh', 'medium')
21
+ .option('-m, --model <model>', 'AI model to use', 'claude-sonnet-4-5-20250929')
22
+ .option('--no-color', 'disable colors')
23
+ .action(async (file, options) => {
24
+ try {
25
+ // Validate severity level
26
+ const validSeverities = ['mild', 'medium', 'harsh'];
27
+ if (options.severity && !validSeverities.includes(options.severity)) {
28
+ console.error(chalk.red(`Invalid severity level: ${options.severity}`));
29
+ console.error(chalk.gray(`Valid options: ${validSeverities.join(', ')}`));
30
+ process.exit(1);
31
+ }
32
+ await roastFile(file, options);
33
+ } catch (error) {
34
+ console.error(chalk.red('💥 Error:'), error.message);
35
+ process.exit(1);
36
+ }
37
+ });
38
+
39
+ program.parse();
@@ -0,0 +1,70 @@
1
+ # Style Guide
2
+
3
+ ## The "No AI Vibes" Rule
4
+
5
+ This tool is built with AI, but it shouldn't **feel** like AI slop. Here's what that means:
6
+
7
+ ### ❌ Avoid
8
+
9
+ - **Corporate cheerleading**: "Great job! 🎉" "You're doing amazing!"
10
+ - **Generic platitudes**: "Consider following best practices"
11
+ - **Emoji overload**: Every sentence doesn't need 5 emojis
12
+ - **Fake enthusiasm**: "I'm so excited to help you with..."
13
+ - **Robotic language**: "As an AI language model..."
14
+ - **Hedge words**: "Perhaps maybe you might consider possibly..."
15
+
16
+ ### ✅ Do
17
+
18
+ - **Be direct**: Say what's wrong, why it matters, how to fix it
19
+ - **Be specific**: Quote actual code, reference line numbers
20
+ - **Be funny naturally**: Like a human developer would
21
+ - **Be useful**: Every roast should have actionable feedback
22
+ - **Be respectful**: Roast the code, not the person
23
+
24
+ ## Tone Examples
25
+
26
+ **Bad (AI vibes):**
27
+ > "Great effort! 🌟 However, I noticed that perhaps you might want to consider using async/await instead of callbacks. It would be more modern! Keep up the good work! 💪"
28
+
29
+ **Good (human dev):**
30
+ > "🔥 Callback hell called, it wants its code back. Switch to async/await unless you enjoy debugging spaghetti."
31
+
32
+ **Bad (too mean):**
33
+ > "🔥 Did you write this with your eyes closed? This is the worst code I've ever seen."
34
+
35
+ **Good (roast with love):**
36
+ > "🔥 This function has more side effects than a pharmaceutical commercial. Let's make it pure."
37
+
38
+ ## Output Structure
39
+
40
+ 1. **Hook** - Start with the most interesting/funny issue
41
+ 2. **Issues** - 2-4 specific problems with solutions
42
+ 3. **Compliment** - End with something genuinely good or a useful tip
43
+
44
+ ## Emoji Usage
45
+
46
+ Use emojis as **markers**, not decoration:
47
+ - 🔥 = Issue/roast
48
+ - 💡 = Suggestion
49
+ - ✨ = Compliment
50
+ - 🚨 = Serious bug
51
+
52
+ Don't: "This is so cool! 😎🚀✨💯🔥"
53
+
54
+ ## Length
55
+
56
+ - **Roast mode**: 200-400 words
57
+ - **Serious mode**: 250-400 words
58
+ - Too short = not useful
59
+ - Too long = nobody reads it
60
+
61
+ ## Shareability
62
+
63
+ Every roast should be:
64
+ - **Screenshot-friendly** - Fits in one image
65
+ - **Self-contained** - Makes sense without context
66
+ - **Quotable** - Has at least one memorable line
67
+
68
+ ---
69
+
70
+ Remember: We're building a tool developers **want** to use, not one that feels like homework.
@@ -0,0 +1,28 @@
1
+ import React from 'react';
2
+
3
+ // A React component with several issues
4
+ function UserProfile(props) {
5
+ const [data, setData] = React.useState(null);
6
+
7
+ // Fetching in render - bad idea
8
+ fetch('/api/user/' + props.userId)
9
+ .then(res => res.json())
10
+ .then(json => setData(json));
11
+
12
+ // No loading state
13
+ return (
14
+ <div onClick={() => {
15
+ // Inline function in render - creates new function every time
16
+ console.log('clicked');
17
+ }}>
18
+ <h1>{data.name}</h1>
19
+ <p>{data.email}</p>
20
+ {/* Rendering array without keys */}
21
+ {data.posts.map(post => (
22
+ <div>{post.title}</div>
23
+ ))}
24
+ </div>
25
+ );
26
+ }
27
+
28
+ export default UserProfile;
@@ -0,0 +1,16 @@
1
+ // Classic bubble sort - perfect roast target
2
+ function bubbleSort(arr) {
3
+ for (let i = 0; i < arr.length; i++) {
4
+ for (let j = 0; j < arr.length - 1; j++) {
5
+ if (arr[j] > arr[j + 1]) {
6
+ let temp = arr[j];
7
+ arr[j] = arr[j + 1];
8
+ arr[j + 1] = temp;
9
+ }
10
+ }
11
+ }
12
+ return arr;
13
+ }
14
+
15
+ const numbers = [64, 34, 25, 12, 22, 11, 90];
16
+ console.log(bubbleSort(numbers));
@@ -0,0 +1,18 @@
1
+ # SQL injection vulnerability example
2
+ import sqlite3
3
+
4
+ def get_user(username):
5
+ conn = sqlite3.connect('users.db')
6
+ cursor = conn.cursor()
7
+
8
+ # DANGER: String concatenation with user input
9
+ query = f"SELECT * FROM users WHERE username = '{username}'"
10
+ cursor.execute(query)
11
+
12
+ result = cursor.fetchone()
13
+ conn.close()
14
+ return result
15
+
16
+ # This could be exploited with: username = "admin' OR '1'='1"
17
+ user = get_user(input("Enter username: "))
18
+ print(user)
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Quick test script to verify the CLI works
4
+ import { exec } from 'child_process';
5
+ import { promisify } from 'util';
6
+
7
+ const execAsync = promisify(exec);
8
+
9
+ console.log('🧪 Testing roast CLI...\n');
10
+
11
+ async function test() {
12
+ // Check if ANTHROPIC_API_KEY is set
13
+ if (!process.env.ANTHROPIC_API_KEY) {
14
+ console.error('❌ ANTHROPIC_API_KEY not set');
15
+ console.log('Set it with: export ANTHROPIC_API_KEY="your-key-here"');
16
+ process.exit(1);
17
+ }
18
+
19
+ try {
20
+ // Test version
21
+ const { stdout: version } = await execAsync('node bin/roast.js --version');
22
+ console.log('✅ Version:', version.trim());
23
+
24
+ // Test help
25
+ const { stdout: help } = await execAsync('node bin/roast.js --help');
26
+ console.log('✅ Help command works');
27
+
28
+ console.log('\n📝 To test actual roasting, run:');
29
+ console.log(' node bin/roast.js examples/bubble-sort.js');
30
+ console.log(' node bin/roast.js --serious examples/sql-injection.py');
31
+ console.log(' node bin/roast.js examples/bad-react.jsx');
32
+
33
+ } catch (error) {
34
+ console.error('❌ Test failed:', error.message);
35
+ process.exit(1);
36
+ }
37
+ }
38
+
39
+ test();
package/lib/roast.js ADDED
@@ -0,0 +1,199 @@
1
+ import Anthropic from '@anthropic-ai/sdk';
2
+ import chalk from 'chalk';
3
+ import { readFileSync } from 'fs';
4
+ import { basename, extname } from 'path';
5
+
6
+ const LANGUAGE_MAP = {
7
+ '.js': 'JavaScript',
8
+ '.ts': 'TypeScript',
9
+ '.jsx': 'React/JSX',
10
+ '.tsx': 'React/TSX',
11
+ '.py': 'Python',
12
+ '.go': 'Go',
13
+ '.rs': 'Rust',
14
+ '.java': 'Java',
15
+ '.c': 'C',
16
+ '.cpp': 'C++',
17
+ '.rb': 'Ruby',
18
+ '.php': 'PHP',
19
+ '.swift': 'Swift',
20
+ '.kt': 'Kotlin',
21
+ '.sh': 'Shell',
22
+ '.sql': 'SQL',
23
+ '.html': 'HTML',
24
+ '.css': 'CSS',
25
+ };
26
+
27
+ const MILD_ROAST_PROMPT = `You're a friendly senior developer reviewing code. Your job:
28
+
29
+ 1. Point out real issues gently (bugs, anti-patterns, performance problems)
30
+ 2. Be encouraging and constructive - focus on learning opportunities
31
+ 3. Use light humor, keep it warm and supportive
32
+ 4. Be specific - quote the actual code you're mentioning
33
+ 5. End with genuine compliments and helpful tips
34
+
35
+ Style: Like a supportive mentor at code review. Constructive feedback with a smile.
36
+
37
+ Format your response with:
38
+ - 💡 for suggestions and improvements
39
+ - ✨ for compliments and good practices
40
+ - 🚨 for serious bugs (if any)
41
+ - 💪 for encouragement
42
+
43
+ Keep it under 400 words. Keep it positive.`;
44
+
45
+ const MEDIUM_ROAST_PROMPT = `You're a senior developer with a sharp wit reviewing code. Your job:
46
+
47
+ 1. Point out real issues (bugs, anti-patterns, performance problems, security holes)
48
+ 2. Be funny but not mean - roast the code, not the person
49
+ 3. Use developer humor (not corporate AI cheerleader vibes)
50
+ 4. Be specific - quote the actual code you're roasting
51
+ 5. End with 1-2 genuine compliments or useful tips
52
+
53
+ Style: Like a sarcastic but helpful senior dev at code review. Think "Gordon Ramsay for code" but constructive.
54
+
55
+ Format your response with:
56
+ - 🔥 for roasts/issues
57
+ - 💡 for suggestions
58
+ - ✨ for compliments
59
+ - 🚨 for serious bugs
60
+
61
+ Keep it under 400 words. Make it shareable.`;
62
+
63
+ const HARSH_ROAST_PROMPT = `You're a brutally honest senior developer with zero patience for bad code. Your job:
64
+
65
+ 1. Ruthlessly point out every issue (bugs, anti-patterns, performance disasters, security nightmares)
66
+ 2. Be savage - this code needs to know what it did wrong
67
+ 3. Use cutting developer humor (think "your code is so bad, it makes PHP look elegant")
68
+ 4. Be specific - quote the crimes against programming
69
+ 5. Maybe end with ONE backhanded compliment if you can find something
70
+
71
+ Style: Like Gordon Ramsay at his angriest, but for code. No mercy. Pure fire.
72
+
73
+ Format your response with:
74
+ - 🔥🔥🔥 for roasts (triple fire for triple pain)
75
+ - 💀 for code that should be deleted
76
+ - 🚨 for serious bugs
77
+ - 💡 for "how did you not know this?"
78
+
79
+ Keep it savage. Keep it under 400 words. No holding back.`;
80
+
81
+ const SERIOUS_PROMPT = `You're a senior developer conducting a professional code review. Analyze this code for:
82
+
83
+ 1. Bugs and potential runtime errors
84
+ 2. Security vulnerabilities
85
+ 3. Performance issues
86
+ 4. Code quality and maintainability
87
+ 5. Best practices for the language/framework
88
+
89
+ Be thorough but concise. Format your response with:
90
+ - 🚨 Critical issues (security, bugs)
91
+ - ⚠️ Warnings (performance, code smell)
92
+ - 💡 Suggestions (improvements, best practices)
93
+ - ✅ Good practices observed
94
+
95
+ Keep it actionable and under 400 words.`;
96
+
97
+ export async function roastFile(filePath, options = {}) {
98
+ // Read file
99
+ const code = readFileSync(filePath, 'utf-8');
100
+ const fileName = basename(filePath);
101
+ const ext = extname(filePath);
102
+ const language = LANGUAGE_MAP[ext] || 'code';
103
+
104
+ // Check for API key
105
+ const apiKey = process.env.ANTHROPIC_API_KEY;
106
+ if (!apiKey) {
107
+ throw new Error(
108
+ 'ANTHROPIC_API_KEY not found in environment.\n' +
109
+ 'Get your key at: https://console.anthropic.com/settings/keys\n' +
110
+ 'Then run: export ANTHROPIC_API_KEY="your-key-here"'
111
+ );
112
+ }
113
+
114
+ // Display header
115
+ console.log('');
116
+ if (options.serious) {
117
+ console.log(chalk.blue.bold('📋 Professional Code Review'));
118
+ console.log(chalk.gray(`File: ${fileName} (${language})`));
119
+ } else {
120
+ const severity = options.severity || 'medium';
121
+ const headers = {
122
+ mild: chalk.yellow.bold('😊 CODE REVIEW (Be Nice Mode)'),
123
+ medium: chalk.red.bold('🔥 CODE ROAST 🔥'),
124
+ harsh: chalk.red.bold('💀 CODE EXECUTION 💀')
125
+ };
126
+ console.log(headers[severity]);
127
+ console.log(chalk.gray(`Victim: ${fileName} (${language})`));
128
+ if (severity === 'harsh') {
129
+ console.log(chalk.red('⚠️ WARNING: Brutally honest mode enabled'));
130
+ } else if (severity === 'mild') {
131
+ console.log(chalk.green('✨ Friendly feedback mode'));
132
+ }
133
+ }
134
+ console.log(chalk.gray('─'.repeat(50)));
135
+ console.log('');
136
+
137
+ // Call Anthropic API
138
+ const client = new Anthropic({ apiKey });
139
+
140
+ const spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
141
+ let i = 0;
142
+ const interval = setInterval(() => {
143
+ process.stdout.write(`\r${chalk.cyan(spinner[i++ % spinner.length])} Analyzing...`);
144
+ }, 80);
145
+
146
+ try {
147
+ let prompt;
148
+ if (options.serious) {
149
+ prompt = SERIOUS_PROMPT;
150
+ } else {
151
+ const severity = options.severity || 'medium';
152
+ const prompts = {
153
+ mild: MILD_ROAST_PROMPT,
154
+ medium: MEDIUM_ROAST_PROMPT,
155
+ harsh: HARSH_ROAST_PROMPT
156
+ };
157
+ prompt = prompts[severity];
158
+ }
159
+
160
+ const message = await client.messages.create({
161
+ model: options.model,
162
+ max_tokens: 2048,
163
+ messages: [{
164
+ role: 'user',
165
+ content: `${prompt}\n\n\`\`\`${language}\n${code}\n\`\`\``
166
+ }]
167
+ });
168
+
169
+ clearInterval(interval);
170
+ process.stdout.write('\r' + ' '.repeat(20) + '\r');
171
+
172
+ const review = message.content[0].text;
173
+
174
+ // Colorize output
175
+ const colorized = review
176
+ .replace(/🔥/g, chalk.red('🔥'))
177
+ .replace(/💡/g, chalk.yellow('💡'))
178
+ .replace(/✨/g, chalk.green('✨'))
179
+ .replace(/🚨/g, chalk.red.bold('🚨'))
180
+ .replace(/⚠️/g, chalk.yellow('⚠️'))
181
+ .replace(/✅/g, chalk.green('✅'))
182
+ .replace(/💀/g, chalk.red.bold('💀'))
183
+ .replace(/💪/g, chalk.cyan('💪'));
184
+
185
+ console.log(colorized);
186
+ console.log('');
187
+ console.log(chalk.gray('─'.repeat(50)));
188
+
189
+ if (!options.serious) {
190
+ console.log(chalk.gray.italic('Roasted with ❤️ by Claude'));
191
+ }
192
+ console.log('');
193
+
194
+ } catch (error) {
195
+ clearInterval(interval);
196
+ process.stdout.write('\r' + ' '.repeat(20) + '\r');
197
+ throw error;
198
+ }
199
+ }
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "roast-cli",
3
+ "version": "1.0.0",
4
+ "description": "AI code reviewer that roasts your code (with love)",
5
+ "main": "lib/roast.js",
6
+ "bin": {
7
+ "roast": "bin/roast.js",
8
+ "roast-cli": "bin/roast.js"
9
+ },
10
+ "scripts": {
11
+ "test": "node examples/test.js"
12
+ },
13
+ "keywords": [
14
+ "code-review",
15
+ "ai",
16
+ "cli",
17
+ "developer-tools",
18
+ "humor"
19
+ ],
20
+ "homepage": "https://github.com/muin-company/roast#readme",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/muin-company/roast.git"
24
+ },
25
+ "bugs": {
26
+ "url": "https://github.com/muin-company/roast/issues"
27
+ },
28
+ "author": "muin",
29
+ "license": "MIT",
30
+ "engines": {
31
+ "node": ">=18.0.0"
32
+ },
33
+ "dependencies": {
34
+ "@anthropic-ai/sdk": "^0.32.1",
35
+ "chalk": "^5.3.0",
36
+ "commander": "^12.0.0"
37
+ },
38
+ "type": "module"
39
+ }
package/test-code.js ADDED
@@ -0,0 +1,12 @@
1
+ function bubbleSort(arr) {
2
+ for (var i = 0; i < arr.length; i++) {
3
+ for (var j = 0; j < arr.length - i - 1; j++) {
4
+ if (arr[j] > arr[j + 1]) {
5
+ var temp = arr[j];
6
+ arr[j] = arr[j + 1];
7
+ arr[j + 1] = temp;
8
+ }
9
+ }
10
+ }
11
+ return arr;
12
+ }