fedora-csb-system-manager
1package main
2
3import (
4 "fmt"
5 "os"
6 "os/exec"
7 "path/filepath"
8 "strconv"
9 "strings"
10 "time"
11
12 "github.com/vdemeester/home/tools/claude-hooks/internal/paths"
13)
14
15const (
16 debounceDuration = 2 * time.Second
17)
18
19func getLockfile() string {
20 return filepath.Join(os.TempDir(), "claude-session-start.lock")
21}
22
23// shouldDebounce checks if we're within the debounce window
24func shouldDebounce() bool {
25 lockfile := getLockfile()
26
27 data, err := os.ReadFile(lockfile)
28 if err == nil {
29 lockTime, err := strconv.ParseInt(strings.TrimSpace(string(data)), 10, 64)
30 if err == nil {
31 now := time.Now().UnixMilli()
32 if now-lockTime < debounceDuration.Milliseconds() {
33 return true
34 }
35 }
36 }
37
38 // Update lockfile with current timestamp
39 now := time.Now().UnixMilli()
40 if err := os.WriteFile(lockfile, []byte(fmt.Sprintf("%d", now)), 0644); err != nil {
41 // Ignore write errors
42 }
43
44 return false
45}
46
47// isSubagentSession checks if this is a subagent session
48func isSubagentSession() bool {
49 claudeProjectDir := os.Getenv("CLAUDE_PROJECT_DIR")
50 if strings.Contains(claudeProjectDir, "/.claude/agents/") {
51 return true
52 }
53 if os.Getenv("CLAUDE_AGENT_TYPE") != "" {
54 return true
55 }
56 return false
57}
58
59// setTerminalTitle sets the terminal tab title using ANSI escape codes
60func setTerminalTitle(title string) {
61 fmt.Fprintf(os.Stderr, "\x1b]0;%s\x07", title)
62 fmt.Fprintf(os.Stderr, "\x1b]2;%s\x07", title)
63 fmt.Fprintf(os.Stderr, "\x1b]30;%s\x07", title)
64}
65
66// logSessionStart logs the session start to history
67func logSessionStart() error {
68 timestamp := paths.GetTimestamp()
69 yearMonth := timestamp[:7] // YYYY-MM
70
71 logDir := filepath.Join(paths.HistoryDir(), "sessions", yearMonth)
72 if err := os.MkdirAll(logDir, 0755); err != nil {
73 return err
74 }
75
76 logEntry := fmt.Sprintf("%s - Session started\n", time.Now().Format(time.RFC3339))
77 logFile := filepath.Join(logDir, fmt.Sprintf("%s_session-log.txt", timestamp[:10]))
78
79 f, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
80 if err != nil {
81 return err
82 }
83 defer f.Close()
84
85 _, err = f.WriteString(logEntry)
86 return err
87}
88
89// loadCoreSkill outputs the CORE skill content so Claude receives it at session start
90func loadCoreSkill() error {
91 homeDir, err := os.UserHomeDir()
92 if err != nil {
93 return err
94 }
95
96 coreSkillPath := filepath.Join(homeDir, ".config/claude/skills/CORE/SKILL.md")
97 content, err := os.ReadFile(coreSkillPath)
98 if err != nil {
99 return err
100 }
101
102 // Output to stdout so Claude receives it
103 fmt.Println("\n<!-- CORE Skill Auto-Loaded at Session Start -->")
104 fmt.Println(string(content))
105 fmt.Println("<!-- End CORE Skill -->")
106
107 return nil
108}
109
110func main() {
111 // Check if this is a subagent session
112 if isSubagentSession() {
113 fmt.Fprintln(os.Stderr, "🤖 Subagent session detected - skipping session initialization")
114 os.Exit(0)
115 }
116
117 // Check debounce to prevent duplicate notifications
118 if shouldDebounce() {
119 fmt.Fprintln(os.Stderr, "🔇 Debouncing duplicate SessionStart event")
120 os.Exit(0)
121 }
122
123 // Set initial tab title
124 tabTitle := "Claude Ready"
125 setTerminalTitle(tabTitle)
126 fmt.Fprintf(os.Stderr, "📍 Session initialized: \"%s\"\n", tabTitle)
127
128 // Load CORE skill at session start
129 if err := loadCoreSkill(); err != nil {
130 // Warn but don't break session start
131 fmt.Fprintf(os.Stderr, "[initialize-session] Warning: Could not load CORE skill: %v\n", err)
132 }
133
134 // Send desktop notification
135 cmd := exec.Command("notify-send", "-u", "low", "Claude Code", "Session started")
136 if err := cmd.Run(); err != nil {
137 // Silent failure - don't break workflow
138 fmt.Fprintf(os.Stderr, "[initialize-session] Warning: Could not send notification: %v\n", err)
139 }
140
141 // Log session start to history (silent failure)
142 if err := logSessionStart(); err != nil {
143 // Don't break session start for logging issues
144 fmt.Fprintf(os.Stderr, "[initialize-session] Warning: Could not log session start: %v\n", err)
145 }
146
147 os.Exit(0)
148}