Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ go fmt ./...

### Package Structure
- **cmd/cli/**: CLI entry point - registers all commands, initializes services via dependency injection
- **cmd/daemon/**: Daemon entry point - sets up pub/sub, socket handler, and optional CCOtel gRPC server
- **cmd/daemon/**: Daemon entry point - sets up pub/sub, socket handler, and optional AICodeOtel gRPC server
- **commands/**: CLI command implementations - each command in its own file, `base.go` holds injected services
- **daemon/**: Daemon internals - socket handler, Watermill pub/sub channel, CCOtel gRPC server/processor
- **daemon/**: Daemon internals - socket handler, Watermill pub/sub channel, AICodeOtel gRPC server/processor
- **model/**: Business logic - API clients, config, crypto, shell hooks, service installers, dotfile handlers

### Service Interfaces (model package)
Expand All @@ -69,7 +69,7 @@ Injection happens in `cmd/*/main.go` via `commands.InjectVar()` and `commands.In
1. **SocketHandler**: Unix domain socket server accepting JSON messages from CLI
2. **GoChannel**: Watermill pub/sub for decoupled message processing
3. **SocketTopicProcessor**: Consumes messages and routes to appropriate handlers
4. **CCOtelServer** (optional): gRPC server implementing OTEL collector for Claude Code metrics/logs passthrough
4. **AICodeOtelServer** (optional): gRPC server implementing OTEL collector for AI coding CLI metrics/logs passthrough (Claude Code, Codex, etc.)

### Data Flow
1. Shell hooks capture commands → CLI stores locally (file-based buffer)
Expand All @@ -81,7 +81,7 @@ Injection happens in `cmd/*/main.go` via `commands.InjectVar()` and `commands.In
- Main config: `$HOME/.shelltime/config.toml`
- Local overrides: `$HOME/.shelltime/config.local.toml` (merged, gitignored)
- Daemon socket: `/tmp/shelltime.sock` (configurable via `socketPath`)
- CCOtel gRPC port: configurable via `ccotel.grpcPort` (default: 4317)
- AICodeOtel gRPC port: configurable via `aiCodeOtel.grpcPort` (default: 54027)

## Commit Rules

Expand All @@ -92,4 +92,4 @@ Follow Conventional Commits with scope: `fix(daemon): ...`, `feat(cli): ...`, `r
- Daemon is optional but recommended (<8ms latency vs ~100ms+ direct)
- Encryption requires daemon mode and a token with encryption capability
- Shell hooks are platform-specific (bash, zsh, fish) - test on target shells
- CCOtel feature enables Claude Code metrics/logs passthrough via gRPC (port 4317)
- AICodeOtel feature enables AI coding CLI metrics/logs passthrough via gRPC (port 54027) - supports Claude Code, Codex, and other OTEL-compatible CLIs
18 changes: 9 additions & 9 deletions cmd/daemon/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,16 +125,16 @@ func main() {
}
}

// Start CCOtel service if enabled (v2 - OTEL gRPC passthrough)
var ccOtelServer *daemon.CCOtelServer
if cfg.CCOtel != nil && cfg.CCOtel.Enabled != nil && *cfg.CCOtel.Enabled {
ccOtelProcessor := daemon.NewCCOtelProcessor(cfg)
ccOtelServer = daemon.NewCCOtelServer(cfg.CCOtel.GRPCPort, ccOtelProcessor)
if err := ccOtelServer.Start(); err != nil {
slog.Error("Failed to start CCOtel gRPC server", slog.Any("err", err))
// Start AICodeOtel service if enabled (OTEL gRPC passthrough for Claude Code, Codex, etc.)
var aiCodeOtelServer *daemon.AICodeOtelServer
if cfg.AICodeOtel != nil && cfg.AICodeOtel.Enabled != nil && *cfg.AICodeOtel.Enabled {
aiCodeOtelProcessor := daemon.NewAICodeOtelProcessor(cfg)
aiCodeOtelServer = daemon.NewAICodeOtelServer(cfg.AICodeOtel.GRPCPort, aiCodeOtelProcessor)
if err := aiCodeOtelServer.Start(); err != nil {
slog.Error("Failed to start AICodeOtel gRPC server", slog.Any("err", err))
} else {
slog.Info("CCOtel gRPC server started", slog.Int("port", cfg.CCOtel.GRPCPort))
defer ccOtelServer.Stop()
slog.Info("AICodeOtel gRPC server started", slog.Int("port", cfg.AICodeOtel.GRPCPort))
defer aiCodeOtelServer.Stop()
}
}

Expand Down
12 changes: 6 additions & 6 deletions commands/cc.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ func commandCCInstall(c *cli.Context) error {
color.Yellow.Println("Installing Claude Code OTEL configuration...")

// Create shell services
zshService := model.NewZshCCOtelEnvService()
fishService := model.NewFishCCOtelEnvService()
bashService := model.NewBashCCOtelEnvService()
zshService := model.NewZshAICodeOtelEnvService()
fishService := model.NewFishAICodeOtelEnvService()
bashService := model.NewBashAICodeOtelEnvService()

// Install for all shells (non-blocking failures)
if err := zshService.Install(); err != nil {
Expand All @@ -60,9 +60,9 @@ func commandCCUninstall(c *cli.Context) error {
color.Yellow.Println("Removing Claude Code OTEL configuration...")

// Create shell services
zshService := model.NewZshCCOtelEnvService()
fishService := model.NewFishCCOtelEnvService()
bashService := model.NewBashCCOtelEnvService()
zshService := model.NewZshAICodeOtelEnvService()
fishService := model.NewFishAICodeOtelEnvService()
bashService := model.NewBashAICodeOtelEnvService()

// Uninstall from all shells
if err := zshService.Uninstall(); err != nil {
Expand Down
8 changes: 4 additions & 4 deletions commands/daemon.status.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,14 @@ func commandDaemonStatus(c *cli.Context) error {
printSectionHeader("Configuration")
fmt.Printf(" Socket Path: %s\n", socketPath)

if cfg.CCOtel != nil && cfg.CCOtel.Enabled != nil && *cfg.CCOtel.Enabled {
if cfg.AICodeOtel != nil && cfg.AICodeOtel.Enabled != nil && *cfg.AICodeOtel.Enabled {
debugStatus := "off"
if cfg.CCOtel.Debug != nil && *cfg.CCOtel.Debug {
if cfg.AICodeOtel.Debug != nil && *cfg.AICodeOtel.Debug {
debugStatus = "on"
}
fmt.Printf(" CCOtel: enabled (port %d, debug %s)\n", cfg.CCOtel.GRPCPort, debugStatus)
fmt.Printf(" AICodeOtel: enabled (port %d, debug %s)\n", cfg.AICodeOtel.GRPCPort, debugStatus)
} else {
fmt.Println(" CCOtel: disabled")
fmt.Println(" AICodeOtel: disabled")
}

if cfg.CodeTracking != nil && cfg.CodeTracking.Enabled != nil && *cfg.CodeTracking.Enabled {
Expand Down
Loading
Loading