Skip to content
Open
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
24 changes: 23 additions & 1 deletion cmd/job/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,18 @@ import (
bkIO "github.com/buildkite/cli/v3/internal/io"
"github.com/buildkite/cli/v3/pkg/cmd/factory"
"github.com/buildkite/cli/v3/pkg/cmd/validation"
"github.com/mcncl/terminal-to-llm/digest"
)

type LogCmd struct {
JobID string `arg:"" help:"Job UUID to get logs for"`
Pipeline string `help:"Deprecated; ignored because job UUIDs no longer require pipeline or build context" short:"p"`
BuildNumber string `help:"Deprecated; ignored because job UUIDs no longer require pipeline or build context" short:"b"`
NoTimestamps bool `help:"Strip timestamp prefixes from log output" name:"no-timestamps"`
LLMOptimized bool `help:"Format output to be optimal for LLM consumption (strips ANSI, deduplicates loops)" name:"agent" aliases:"llm"`
Format string `help:"Output rendering for --agent: plain or markdown" name:"format" enum:"plain,markdown" default:"plain"`
MaxTokens int `help:"Hard ceiling on the estimated token count of --agent output (0 = unlimited)" name:"max-tokens"`
NoWindow bool `help:"Disable failure-focused windowing in --agent output (keep all lines)" name:"no-window"`
}

func (c *LogCmd) Help() string {
Expand All @@ -27,6 +32,12 @@ Examples:

# Strip timestamp prefixes from output
$ bk job log 0190046e-e199-453b-a302-a21a4d649d31 --no-timestamps

# Format for LLM consumption
$ bk job log 0190046e-e199-453b-a302-a21a4d649d31 --agent

# Format for LLM as markdown, capped at 2000 tokens, keeping all lines
$ bk job log 0190046e-e199-453b-a302-a21a4d649d31 --agent --format markdown --max-tokens 2000 --no-window
`
}

Expand Down Expand Up @@ -73,14 +84,25 @@ func (c *LogCmd) Run(kongCtx *kong.Context, globals cli.GlobalFlags) error {
logContent = stripTimestamps(logContent)
}

if c.LLMOptimized {
opt := digest.Default()
opt.Format = digest.ParseFormat(c.Format)
opt.MaxTokens = c.MaxTokens
opt.Window = !c.NoWindow
logContent = digest.Process([]byte(logContent), opt)
}

writer, cleanup := bkIO.Pager(f.NoPager)
defer func() { _ = cleanup() }()

fmt.Fprint(writer, logContent)
return nil
}

var timestampRegex = regexp.MustCompile(`bk;t=\d+\x07`)
// timestampRegex matches Buildkite's inline timestamp markers, including the
// optional APC introducer (`\x1b_`) so the whole sequence is removed rather than
// leaving a dangling escape byte behind.
var timestampRegex = regexp.MustCompile(`(?:\x1b_)?bk;t=\d+\x07`)

func stripTimestamps(content string) string {
return timestampRegex.ReplaceAllString(content, "")
Expand Down
12 changes: 12 additions & 0 deletions cmd/job/log_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package job

import "testing"

func TestStripTimestamps(t *testing.T) {
t.Parallel()

in := "\x1b_bk;t=1700000000000\x07hello"
if got := stripTimestamps(in); got != "hello" {
t.Errorf("stripTimestamps(%q) = %q, want %q", in, got, "hello")
}
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ require (
github.com/go-git/go-git/v5 v5.19.1
github.com/goccy/go-yaml v1.19.2
github.com/google/uuid v1.6.0
github.com/mcncl/terminal-to-llm v0.0.0-20260625015351-3819cf9f5f9b
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
github.com/posthog/posthog-go v1.16.1
github.com/vektah/gqlparser/v2 v2.5.35
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2J
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/ay2DU=
github.com/mattn/go-runewidth v0.0.24/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/mcncl/terminal-to-llm v0.0.0-20260625015351-3819cf9f5f9b h1:EY/R1QdVIzyKIYx+EPk/I3jq7W2HTHeMOFGA6Rr1Eps=
github.com/mcncl/terminal-to-llm v0.0.0-20260625015351-3819cf9f5f9b/go.mod h1:+zIpLjV+/ByEl/BRo9rxZdpZAxxQAG0yZg4kxW+hOmM=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
Expand Down