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}