by Andrea Griffiths
2
We Are Developers 2025
3
We Are Developers 2025
.prompt.md
files for tailored Copilot interactions, stored in .github/prompts/
..github/copilot-instructions.md
.4
We Are Developers 2025
5
We Are Developers 2025
6
We Are Developers 2025
Tip: Refresh if Codespace stalls during initialization.
7
We Are Developers 2025
cd gitvision
chmod +x workshop-start.sh
./workshop-start.sh
8
We Are Developers 2025
static const String githubModelsToken = "your_token_here";
static const String spotifyClientId = "your_client_id";
9
We Are Developers 2025
flutter run -d web-server
flutter run -d ios
10
We Are Developers 2025
11
We Are Developers 2025
lib/main.dart
- Basic UI structure and GitHub integration skeletonlib/models/commit.dart
- Commit data structurelib/config/api_tokens.dart
- API configuration templatelib/services/github_service.dart
- GitHub API wrapper with error handlinglib/sentiment_analyzer.dart
- Eurovision mood detection logic12
We Are Developers 2025
13
We Are Developers 2025
// Excerpt from sentiment_analyzer.dart // Analyze commit messages and return the detected vibe static String detectVibe(List commitMessages) { if (commitMessages.isEmpty) { return 'Productive'; // Default vibe } // This map defines the keywords for each vibe const Map> vibeKeywords = { 'Productive': ['add', 'implement', 'feature', 'improve', 'optimize', 'refactor'], 'Intense': ['fix', 'bug', 'issue', 'error', 'crash', 'hotfix'], 'Creative': ['design', 'style', 'ui', 'ux', 'animation', 'visual'], // ... and so on for other vibes }; // Count occurrences of keywords for each vibe Map vibeCounts = {}; for (String vibe in vibeKeywords.keys) { vibeCounts[vibe] = 0; } for (String message in commitMessages) { String lowerMessage = message.toLowerCase(); for (String vibe in vibeKeywords.keys) { for (String keyword in vibeKeywords[vibe]!) { if (lowerMessage.contains(keyword.toLowerCase())) { vibeCounts[vibe] = (vibeCounts[vibe] ?? 0) + 1; } } } }
// Excerpt from sentiment_analyzer.dart // Analyze commit messages and return the detected vibe static String detectVibe(List<String> commitMessages) { if (commitMessages.isEmpty) { return 'Productive'; // Default vibe } // This map defines the keywords for each vibe const Map<String, List<String>> vibeKeywords = { 'Productive': ['add', 'implement', 'feature', 'improve', 'optimize', 'refactor'], 'Intense': ['fix', 'bug', 'issue', 'error', 'crash', 'hotfix'], 'Creative': ['design', 'style', 'ui', 'ux', 'animation', 'visual'], // ... and so on for other vibes }; // Count occurrences of keywords for each vibe Map<String, int> vibeCounts = {}; for (String vibe in vibeKeywords.keys) { vibeCounts[vibe] = 0; } for (String message in commitMessages) { String lowerMessage = message.toLowerCase(); for (String vibe in vibeKeywords.keys) { for (String keyword in vibeKeywords[vibe]!) { if (lowerMessage.contains(keyword.toLowerCase())) { vibeCounts[vibe] = (vibeCounts[vibe] ?? 0) + 1; } } } }
14
We Are Developers 2025
15
We Are Developers 2025
16
We Are Developers 2025
17
We Are Developers 2025
String _buildEurovisionPrompt(SentimentResult sentiment) {
return '''
Based on coding mood: "${sentiment.mood}" (${sentiment.confidence} confidence)
Suggest 5 Eurovision songs matching this developer vibe:
- Different years and countries
- Match energy/theme to coding mood
- Include reasoning for each choice
JSON format: [{"title":"", "artist":"", "country":"", "year":2024, "reasoning":""}]
Commit keywords: ${sentiment.keywords.join(', ')}
''';
}
18
We Are Developers 2025
Future<String> _callGitHubModelsAPI(String prompt) async {
final response = await http.post(
Uri.parse('https://models.github.ai/inference/chat/completions'),
headers: {
'Authorization': 'Bearer ${ApiConfig.githubToken}',
'Content-Type': 'application/json',
},
body: jsonEncode({
'messages': [
{'role': 'system', 'content': 'Eurovision expert & music curator'},
{'role': 'user', 'content': prompt}
],
'model': 'openai/gpt-4.1',
'temperature': 0.7, // Balance creativity + consistency
}),
);
// ... error handling
}
19
We Are Developers 2025
// In ai_playlist_service.dart
List<EurovisionSong> _parseAIResponse(String response) {
try {
// Extract JSON from AI response (handles non-JSON text)
final jsonMatch = RegExp(r'\[[\s\S]*\]').firstMatch(response);
if (jsonMatch == null) {
throw FormatException('No JSON array found in response');
}
final jsonString = jsonMatch.group(0);
final List songsList = jsonDecode(jsonString);
return songsList
.map((song) => EurovisionSong.fromJson(song))
.toList();
} catch (e) {
// Handle parsing errors gracefully
print('Error parsing AI response: $e');
return _getFallbackSongs();
}
}
// In eurovision_song.dart
class EurovisionSong {
final String title;
final String artist;
final String country;
final int year;
final String reasoning;
// Constructor and validation
EurovisionSong({
required this.title,
required this.artist,
required this.country,
required this.year,
required this.reasoning,
}) : assert(year >= 1956 && year <= 2025);
// JSON parsing methods
factory EurovisionSong.fromJson(Map json) {...}
}
20
We Are Developers 2025
21
We Are Developers 2025
// Primary search with Eurovision context
String query = "${song.title} ${song.artist} eurovision";
var results = await spotify.searchTracks(query);
// First fallback: without Eurovision context
if (results.isEmpty) {
query = "${song.title} ${song.artist}";
results = await spotify.searchTracks(query);
}
// Second fallback: title only
if (results.isEmpty) {
query = "${song.title}";
results = await spotify.searchTracks(query);
}
// Handle not found
if (results.isEmpty) {
return EurovisionTrack.notFound(song);
}
22
We Are Developers 2025
23
We Are Developers 2025
24
We Are Developers 2025
try {
commits = await githubService.fetchCommits(username);
} catch (e) {
if (e is RateLimitException) {
showRateLimitDialog();
} else if (e is UserNotFoundException) {
showUserNotFoundMessage();
} else {
showGenericErrorMessage();
}
}
try {
playlist = await aiService.generatePlaylist(commits);
} catch (e) {
// Fallback to rule-based recommendations
playlist = sentimentAnalyzer
.getRecommendations(commits);
// Track failure for analytics
analytics.trackEvent('ai_failure', {
'error_type': e.toString(),
'fallback_used': true
});
}
25
We Are Developers 2025
26
We Are Developers 2025
"Every great developer has their soundtrack. Today you discovered yours is Eurovision! 🇪🇺"