diff --git a/cmd/hooks/statusline/main.go b/cmd/hooks/statusline/main.go index dc555b2..4d09a77 100644 --- a/cmd/hooks/statusline/main.go +++ b/cmd/hooks/statusline/main.go @@ -3,9 +3,9 @@ package main import ( + "context" "encoding/json" "fmt" - "net/http" "net/url" "os" "time" @@ -13,6 +13,9 @@ import ( "github.com/lukaszraczylo/claude-mnemonic/pkg/hooks" ) +// statsTimeout bounds the worker stats request. +const statsTimeout = 100 * time.Millisecond + // StatusInput is the JSON input from Claude Code's statusline feature. type StatusInput struct { HookEventName string `json:"hook_event_name"` @@ -105,26 +108,19 @@ func handleStatusline(input *StatusInput, port int) string { // getWorkerStats fetches stats from the worker service. func getWorkerStats(port int, project string) *WorkerStats { // Build URL with optional project parameter - endpoint := fmt.Sprintf("http://127.0.0.1:%d/api/stats", port) + endpoint := "/api/stats" if project != "" { endpoint += "?project=" + url.QueryEscape(project) } - // Create HTTP client with short timeout (statusline must be fast) - client := &http.Client{Timeout: 100 * time.Millisecond} - - resp, err := client.Get(endpoint) + // Short timeout: statusline must be fast + body, err := hooks.GETBody(context.Background(), statsTimeout, port, endpoint) if err != nil { return nil } - defer func() { _ = resp.Body.Close() }() - - if resp.StatusCode != http.StatusOK { - return nil - } var stats WorkerStats - if err := json.NewDecoder(resp.Body).Decode(&stats); err != nil { + if err := json.Unmarshal(body, &stats); err != nil { return nil } diff --git a/pkg/hooks/httpclient.go b/pkg/hooks/httpclient.go index 5dea2a9..2d0da5e 100644 --- a/pkg/hooks/httpclient.go +++ b/pkg/hooks/httpclient.go @@ -79,3 +79,16 @@ func parseHTTPResponse(raw []byte) (*httpResponse, error) { } return &httpResponse{Status: code, StatusText: rest, Body: body}, nil } + +// GETBody fetches path from the worker within timeout and returns the raw body. +// Statuses of 400 and above are returned as errors. +func GETBody(ctx context.Context, timeout time.Duration, port int, path string) ([]byte, error) { + resp, err := httpDo(ctx, timeout, "GET", port, path, nil) + if err != nil { + return nil, err + } + if resp.Status >= 400 { + return nil, fmt.Errorf("request failed: %s", resp.StatusText) + } + return resp.Body, nil +}