Commit 1e0690bf862f

Vincent Demeester <vincent@sbr.pm>
2026-02-02 17:19:08
claude-hooks: add pi context detection and improve terminal titles
- Add isPiContext() to detect execution context (pi vs Claude Code) - Use π prefix for pi, Claude prefix for Claude Code - Always show working directory in terminal title - Multi-strategy detection: parent process, process tree, env vars - Improves terminal title from 'Claude Ready' to 'π home' in pi sessions
1 parent aa2af7e
Changed files (2)
tools
claude-hooks
cmd
initialize-session
update-terminal-title
tools/claude-hooks/cmd/initialize-session/main.go
@@ -3,6 +3,7 @@ package main
 import (
 	"fmt"
 	"os"
+	"os/exec"
 	"path/filepath"
 	"strconv"
 	"strings"
@@ -106,6 +107,31 @@ func loadCoreSkill() error {
 	return nil
 }
 
+// isPiContext checks if we're running under pi coding agent
+func isPiContext() bool {
+	// Strategy 1: Check immediate parent process
+	ppid := os.Getppid()
+	cmdline := fmt.Sprintf("/proc/%d/comm", ppid)
+	data, err := os.ReadFile(cmdline)
+	if err == nil && strings.TrimSpace(string(data)) == "pi" {
+		return true
+	}
+
+	// Strategy 2: Check if "pi" is anywhere in the process tree
+	cmd := exec.Command("sh", "-c", fmt.Sprintf("ps -o comm= -p $(ps -o ppid= -p %d) 2>/dev/null || pstree -s %d 2>/dev/null", ppid, os.Getpid()))
+	output, err := cmd.Output()
+	if err == nil && strings.Contains(string(output), "pi") {
+		return true
+	}
+
+	// Strategy 3: Check PI_* environment variables (if pi sets any in future)
+	if os.Getenv("PI_AGENT") != "" || os.Getenv("PI_CODING_AGENT") != "" {
+		return true
+	}
+
+	return false
+}
+
 func main() {
 	// Check if this is a subagent session
 	if isSubagentSession() {
@@ -119,8 +145,13 @@ func main() {
 		os.Exit(0)
 	}
 
-	// Set initial tab title
+	// Detect context and set appropriate title
+	isPi := isPiContext()
 	tabTitle := "Claude Ready"
+	if isPi {
+		tabTitle = "π Ready"
+	}
+
 	setTerminalTitle(tabTitle)
 	fmt.Fprintf(os.Stderr, "📍 Session initialized: \"%s\"\n", tabTitle)
 
tools/claude-hooks/cmd/update-terminal-title/main.go
@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"os"
+	"os/exec"
 	"path/filepath"
 	"strings"
 )
@@ -97,9 +98,42 @@ func getTodoProgress(todos []TodoItem) (string, bool) {
 	return "", false
 }
 
+// isPiContext checks if we're running under pi coding agent
+func isPiContext() bool {
+	// Strategy 1: Check immediate parent process
+	ppid := os.Getppid()
+	cmdline := fmt.Sprintf("/proc/%d/comm", ppid)
+	data, err := os.ReadFile(cmdline)
+	if err == nil && strings.TrimSpace(string(data)) == "pi" {
+		return true
+	}
+
+	// Strategy 2: Check if "pi" is anywhere in the process tree
+	// Look at process tree via pstree or ps
+	cmd := exec.Command("sh", "-c", fmt.Sprintf("ps -o comm= -p $(ps -o ppid= -p %d) 2>/dev/null || pstree -s %d 2>/dev/null", ppid, os.Getpid()))
+	output, err := cmd.Output()
+	if err == nil && strings.Contains(string(output), "pi") {
+		return true
+	}
+
+	// Strategy 3: Check PI_* environment variables (if pi sets any in future)
+	if os.Getenv("PI_AGENT") != "" || os.Getenv("PI_CODING_AGENT") != "" {
+		return true
+	}
+
+	return false
+}
+
 // buildTitle constructs the terminal title based on available context
 func buildTitle(toolResult *ToolResult) string {
 	var parts []string
+	isPi := isPiContext()
+
+	// Determine prefix (π for pi, Claude for Claude Code)
+	prefix := "Claude"
+	if isPi {
+		prefix = "π"
+	}
 
 	// Priority 1: Check for active task from TodoWrite
 	if toolResult != nil && toolResult.ToolName == "TodoWrite" {
@@ -125,12 +159,17 @@ func buildTitle(toolResult *ToolResult) string {
 		parts = append(parts, projectName)
 	}
 
-	// Build final title
-	if len(parts) > 0 {
-		return fmt.Sprintf("Claude: %s", strings.Join(parts, " • "))
+	// Priority 4: Always add working directory
+	if cwd, err := os.Getwd(); err == nil {
+		parts = append(parts, filepath.Base(cwd))
 	}
 
-	return "Claude Ready"
+	// Build final title
+	if len(parts) > 0 {
+		return fmt.Sprintf("%s %s", prefix, strings.Join(parts, " • "))
+	}
+
+	return fmt.Sprintf("%s Ready", prefix)
 }
 
 func main() {