Merge task attempt 27b79b03-9c06-433e-81bf-90465e2a3419 into main

This commit is contained in:
Louis Knight-Webb
2025-06-19 21:26:25 -04:00
5 changed files with 73 additions and 1 deletions

View File

@@ -23,6 +23,7 @@ pub struct RunningExecution {
pub struct AppState {
pub running_executions: Arc<Mutex<HashMap<Uuid, RunningExecution>>>,
pub db_pool: sqlx::SqlitePool,
pub config: Arc<tokio::sync::RwLock<crate::models::config::Config>>,
}
/// Commit any unstaged changes in the worktree after execution completion
@@ -84,6 +85,40 @@ async fn commit_execution_changes(
Ok(())
}
/// Play a system sound notification
async fn play_sound_notification() {
// Use platform-specific sound notification
if cfg!(target_os = "macos") {
let _ = tokio::process::Command::new("afplay")
.arg("/System/Library/Sounds/Glass.aiff")
.spawn();
} else if cfg!(target_os = "linux") {
// Try different Linux notification sounds
if let Ok(_) = tokio::process::Command::new("paplay")
.arg("/usr/share/sounds/alsa/Front_Left.wav")
.spawn()
{
// Success with paplay
} else if let Ok(_) = tokio::process::Command::new("aplay")
.arg("/usr/share/sounds/alsa/Front_Left.wav")
.spawn()
{
// Success with aplay
} else {
// Try system bell as fallback
let _ = tokio::process::Command::new("echo")
.arg("-e")
.arg("\\a")
.spawn();
}
} else if cfg!(target_os = "windows") {
let _ = tokio::process::Command::new("powershell")
.arg("-c")
.arg("[console]::beep(800, 300)")
.spawn();
}
}
pub async fn execution_monitor(app_state: AppState) {
let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(5));
@@ -215,6 +250,15 @@ pub async fn execution_monitor(app_state: AppState) {
tracing::info!("Execution {} {}{}", execution_id, status_text, exit_text);
// Play sound notification if enabled
let sound_enabled = {
let config = app_state.config.read().await;
config.sound_alerts
};
if sound_enabled {
play_sound_notification().await;
}
// Get task attempt to access worktree path for committing changes
if let Ok(Some(task_attempt)) =
TaskAttempt::find_by_id(&app_state.db_pool, task_attempt_id).await

View File

@@ -110,6 +110,7 @@ async fn main() -> anyhow::Result<()> {
let app_state = AppState {
running_executions: Arc::new(Mutex::new(HashMap::new())),
db_pool: pool.clone(),
config: config_arc.clone(),
};
// Start background task to check for init status and spawn processes

View File

@@ -9,6 +9,7 @@ pub struct Config {
pub theme: ThemeMode,
pub executor: ExecutorConfig,
pub disclaimer_acknowledged: bool,
pub sound_alerts: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
@@ -26,6 +27,7 @@ impl Default for Config {
theme: ThemeMode::System,
executor: ExecutorConfig::Claude,
disclaimer_acknowledged: false,
sound_alerts: true,
}
}
}

View File

@@ -4,6 +4,7 @@ import { Button } from "@/components/ui/button";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Label } from "@/components/ui/label";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Checkbox } from "@/components/ui/checkbox";
import { Loader2 } from "lucide-react";
import type { Config, ThemeMode, ApiResponse } from "shared/types";
import { useTheme } from "@/components/theme-provider";
@@ -190,6 +191,30 @@ export function Settings() {
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Notifications</CardTitle>
<CardDescription>
Configure how you receive notifications about task completion.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center space-x-2">
<Checkbox
id="sound-alerts"
checked={config.sound_alerts}
onCheckedChange={(checked: boolean) => updateConfig({ sound_alerts: checked })}
/>
<div className="space-y-0.5">
<Label htmlFor="sound-alerts" className="cursor-pointer">Sound Alerts</Label>
<p className="text-sm text-muted-foreground">
Play a sound when task attempts finish running.
</p>
</div>
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Safety & Disclaimers</CardTitle>

View File

@@ -3,7 +3,7 @@
export type ApiResponse<T> = { success: boolean, data: T | null, message: string | null, };
export type Config = { theme: ThemeMode, executor: ExecutorConfig, disclaimer_acknowledged: boolean, };
export type Config = { theme: ThemeMode, executor: ExecutorConfig, disclaimer_acknowledged: boolean, sound_alerts: boolean, };
export type ThemeMode = "light" | "dark" | "system";