Adding AI Error Analysis to a macOS Menu Bar App with Gemini API
Source: Dev.to
The Problem
When a monitored script fails, most tools just show you a red icon.
In 2026 we expect the AI to come to you and provide actionable insight automatically.
Architecture
- The script is executed via
std::process::Command. - The entire flow is automatic; no user interaction is required.
Implementation
pub async fn analyze_error(prompt: &str, api_key: &str) -> Result {
let client = reqwest::Client::new();
let response = client
.post("https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent")
.header("X-goog-api-key", api_key)
.json(&serde_json::json!({
"contents": [{
"parts": [{ "text": prompt }]
}],
"generationConfig": {
"maxOutputTokens": 256,
"temperature": 0.3
}
}))
.send()
.await?;
let data: serde_json::Value = response.json().await?;
let text = data["candidates"][0]["content"]["parts"][0]["text"]
.as_str()
.unwrap_or("Analysis unavailable")
.to_string();
Ok(text)
}
Key Decisions
- Model:
gemini-2.5-flashfor speed and cost efficiency.
Triggering the Analysis
match output {
Ok(out) if out.status.success() => MonitorResult::Success(
String::from_utf8_lossy(&out.stdout).to_string()
),
Ok(out) => {
let error_text = format!(
"stdout: {}\nstderr: {}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
// Only analyze if AI is enabled and key is available
let insight = if monitor.analyze_errors {
if let Some(key) = api_key {
analyze_error(&error_text, key).await.ok()
} else {
None
}
} else {
None
};
MonitorResult::Error { error_text, insight }
}
Err(e) => MonitorResult::Error {
error_text: e.to_string(),
insight: None,
},
}
The AI Insight UI
AI INSIGHT
{result.insight}
Storing the API Key Securely
pub fn save_gemini_key(key: &str) -> keyring::Result {
// implementation omitted for brevity
unimplemented!()
}
pub fn load_gemini_key() -> keyring::Result {
// implementation omitted for brevity
unimplemented!()
}
References
- HiyokoBar:
- 日本語版:
- Product Hunt launch: