feat(04-01): integrate notifications and focus mode into daemon

- Notifier + FocusTimer fields in Daemon, initialized in NewDaemon
- processHookEvent notifies on Working -> Needs Input only (D-01)
- Focus mode suppresses notifications when active (D-05)
- FocusArgs in protocol, "focus" handler in daemon
- CLI "vmux focus <minutes>" command
- Tests for all notification transitions and focus handler
This commit is contained in:
Pierre Martin
2026-03-23 21:26:02 +01:00
parent b96c6d05be
commit efbe31928e
5 changed files with 183 additions and 3 deletions

29
main.go
View File

@@ -6,6 +6,7 @@ import (
"log"
"os"
"path/filepath"
"strconv"
"strings"
"time"
@@ -100,6 +101,33 @@ func main() {
}
fmt.Println("Label set.")
case "focus":
if len(filteredArgs) < 2 {
fmt.Fprintf(os.Stderr, "Usage: vmux focus <minutes>\n")
os.Exit(1)
}
minutes, err := strconv.Atoi(filteredArgs[1])
if err != nil || minutes < 0 {
fmt.Fprintf(os.Stderr, "Error: invalid minutes %q\n", filteredArgs[1])
os.Exit(1)
}
if err := EnsureDaemon(sockPath); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
client := NewClient(sockPath)
focusArgs, _ := json.Marshal(FocusArgs{Minutes: minutes})
resp, err := client.Send(Request{Action: "focus", Args: focusArgs})
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
if !resp.OK {
fmt.Fprintf(os.Stderr, "Error: %s\n", resp.Error)
os.Exit(1)
}
fmt.Printf("Focus mode: notifications suppressed for %d minutes\n", minutes)
case "stop":
client := NewClient(sockPath)
resp, err := client.Send(Request{Action: "stop"})
@@ -170,6 +198,7 @@ Commands:
list List active Claude Code sessions
switch <query> Switch to the workspace of the matching session
label <id> <text> Assign a label to a session
focus <minutes> Suppress notifications for N minutes
stop Stop the vmux daemon
daemon Run the daemon in foreground (used internally)