@@ -27,17 +27,19 @@ const (
2727
2828// Session represents a Claude Code session
2929type Session struct {
30- Project string `json:"project"`
31- Status Status `json:"status"`
32- LastActivity time.Time `json:"last_activity"`
33- Task string `json:"task"`
34- Summary string `json:"summary,omitempty"`
35- LastMessage string `json:"last_message,omitempty"`
36- LogFile string `json:"-"`
37- ProjectPath string `json:"-"` // Full path to the project directory
38- IsDesktop bool `json:"is_desktop,omitempty"` // True if session appears to be from desktop app
39- IsGhost bool `json:"is_ghost,omitempty"` // True if process running but log is stale
40- GhostPID int `json:"ghost_pid,omitempty"` // PID of the ghost process (for killing)
30+ Project string `json:"project"`
31+ Status Status `json:"status"`
32+ LastActivity time.Time `json:"last_activity"`
33+ Task string `json:"task"`
34+ Summary string `json:"summary,omitempty"`
35+ LastMessage string `json:"last_message,omitempty"`
36+ LogFile string `json:"-"`
37+ ProjectPath string `json:"-"` // Full path to the project directory
38+ IsDesktop bool `json:"is_desktop,omitempty"` // True if session appears to be from desktop app
39+ IsGhost bool `json:"is_ghost,omitempty"` // True if process running but log is stale
40+ GhostPID int `json:"ghost_pid,omitempty"` // PID of the ghost process (for killing)
41+ GitBranch string `json:"git_branch,omitempty"` // Current git branch
42+ HasUnsandboxed bool `json:"has_unsandboxed,omitempty"` // True if any command bypassed sandbox
4143}
4244
4345// RunningProcess represents a Claude process with its PID and working directory
@@ -53,6 +55,7 @@ type LogEntry struct {
5355 Timestamp time.Time `json:"timestamp"`
5456 Message * Message `json:"message,omitempty"`
5557 Summary string `json:"summary,omitempty"` // For type: "summary" entries
58+ GitBranch string `json:"gitBranch,omitempty"`
5659}
5760
5861// Message represents the message field in a log entry
@@ -63,9 +66,16 @@ type Message struct {
6366
6467// ContentItem represents an item in the content array
6568type ContentItem struct {
66- Type string `json:"type"`
67- Text string `json:"text,omitempty"`
68- Name string `json:"name,omitempty"` // For tool_use
69+ Type string `json:"type"`
70+ Text string `json:"text,omitempty"`
71+ Name string `json:"name,omitempty"` // For tool_use
72+ Input json.RawMessage `json:"input,omitempty"` // For tool_use inputs
73+ }
74+
75+ // BashToolInput represents the input for a Bash tool_use entry
76+ type BashToolInput struct {
77+ Command string `json:"command"`
78+ DangerouslyDisableSandbox bool `json:"dangerouslyDisableSandbox"`
6979}
7080
7181// ClaudeProjectsDir returns the path to the Claude projects directory
@@ -293,6 +303,12 @@ func parseSession(projectName, logFile string, runningDirs map[string]int) (Sess
293303 // Extract last assistant message text
294304 session .LastMessage = extractLastAssistantMessage (entries )
295305
306+ // Extract git branch (use most recent non-empty)
307+ session .GitBranch = extractGitBranch (entries )
308+
309+ // Detect if any commands ran without sandbox
310+ session .HasUnsandboxed = detectUnsandboxedCommands (entries )
311+
296312 // Determine status from log entries
297313 session .Status , session .Task , session .IsGhost = determineStatus (entries , isRunning )
298314
@@ -381,6 +397,36 @@ func extractLastAssistantMessage(entries []LogEntry) string {
381397 return ""
382398}
383399
400+ // extractGitBranch extracts the most recent git branch from entries
401+ func extractGitBranch (entries []LogEntry ) string {
402+ for i := len (entries ) - 1 ; i >= 0 ; i -- {
403+ if entries [i ].GitBranch != "" {
404+ return entries [i ].GitBranch
405+ }
406+ }
407+ return ""
408+ }
409+
410+ // detectUnsandboxedCommands checks if any Bash commands ran with sandbox disabled
411+ func detectUnsandboxedCommands (entries []LogEntry ) bool {
412+ for _ , entry := range entries {
413+ if entry .Type != "assistant" || entry .Message == nil {
414+ continue
415+ }
416+ for _ , content := range entry .Message .Content {
417+ if content .Type == "tool_use" && content .Name == "Bash" && len (content .Input ) > 0 {
418+ var input BashToolInput
419+ if json .Unmarshal (content .Input , & input ) == nil {
420+ if input .DangerouslyDisableSandbox {
421+ return true
422+ }
423+ }
424+ }
425+ }
426+ }
427+ return false
428+ }
429+
384430// decodeProjectName converts the directory name to a readable project name
385431func decodeProjectName (name string ) string {
386432 // Format: -Users-username-Projects-org-project
@@ -430,9 +476,9 @@ func readLastEntries(filePath string, count int) ([]LogEntry, error) {
430476
431477 var entries []LogEntry
432478 scanner := bufio .NewScanner (file )
433- // Increase buffer size for long lines
479+ // Increase buffer size for very long lines (some entries can be several MB)
434480 buf := make ([]byte , 0 , 64 * 1024 )
435- scanner .Buffer (buf , 1024 * 1024 )
481+ scanner .Buffer (buf , 10 * 1024 * 1024 ) // 10MB max
436482
437483 for scanner .Scan () {
438484 line := scanner .Text ()
0 commit comments