Skip to content

Commit 0ee1058

Browse files
bobakemamianclaude
andauthored
feat(press): --follow streams live output as the press runs (#147)
buttons press NAME --follow Tees child stdout/stderr to the CLI's stderr as each line arrives. stdout lines print as-is; stderr lines are prefixed with '! ' so a human can tell them apart at a glance. The final structured Result still goes to stdout at the end, so agents can do: buttons press X --follow --json 2> live-feed.log | jq . — watching live progress on stderr while parsing the final JSON on stdout. Pipe-safe, agent-safe, no TUI. Answers "how do i press a button and follow the logs at the same time" — one command, stdout and stderr cleanly separated. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 8fe4743 commit 0ee1058

3 files changed

Lines changed: 33 additions & 4 deletions

File tree

.agents/skills/buttons/SKILL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ buttons press [flags]
273273
|------|------|-------------|
274274
| `--arg` | stringArray | argument as key=value |
275275
| `--dry-run` | bool | show what would execute without running |
276+
| `-f, --follow` | bool | stream stdout/stderr to stderr as the press runs (final Result still goes to stdout) |
276277
| `--idempotency-key` | string | reuse the cached result for this key if present (cross-run dedup) |
277278
| `--idempotency-ttl` | duration | how long idempotency entries stay valid (e.g. 1h, 24h) |
278279
| `--timeout` | int | override timeout in seconds |

cmd/press.go

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ var pressTimeout int
2222
var pressDryRun bool
2323
var pressIdempotencyKey string
2424
var pressIdempotencyTTL time.Duration
25+
var pressFollow bool
2526

2627
var pressCmd = &cobra.Command{
2728
Use: "press [name]",
@@ -165,10 +166,35 @@ Examples:
165166
defer queueLock.Release()
166167
}
167168

168-
// No streaming sink for CLI presses — the final Result is the
169-
// only thing the caller needs. The TUI log viewer (C2) passes
170-
// a real sink to see lines as they happen.
171-
result := engine.Execute(ctx, btn, parsedArgs, batteries, nil, codePath)
169+
// If --follow is set, tee child stdout/stderr to our stderr
170+
// as lines arrive. Pure-text stream: stdout lines prefixed
171+
// with nothing, stderr lines prefixed with `! `. The final
172+
// structured Result still goes to stdout at the end so
173+
// agents can parse `buttons press --follow --json | jq .`
174+
// cleanly — live feed on stderr, result on stdout.
175+
var sink chan engine.LogLine
176+
var sinkDone chan struct{}
177+
if pressFollow {
178+
sink = make(chan engine.LogLine, 128)
179+
sinkDone = make(chan struct{})
180+
go func() {
181+
for line := range sink {
182+
prefix := ""
183+
if line.Sev == engine.SeverityStderr {
184+
prefix = "! "
185+
}
186+
fmt.Fprintf(os.Stderr, "%s%s\n", prefix, line.Text)
187+
}
188+
close(sinkDone)
189+
}()
190+
}
191+
192+
result := engine.Execute(ctx, btn, parsedArgs, batteries, sink, codePath)
193+
194+
if pressFollow {
195+
close(sink)
196+
<-sinkDone // drain the channel before emitting the final Result
197+
}
172198

173199
// Attach prompt if AGENT.md has custom content (not the default template)
174200
if promptMD := readPrompt(btn.Name); promptMD != "" {
@@ -309,5 +335,6 @@ func init() {
309335
pressCmd.Flags().BoolVar(&pressDryRun, "dry-run", false, "show what would execute without running")
310336
pressCmd.Flags().StringVar(&pressIdempotencyKey, "idempotency-key", "", "reuse the cached result for this key if present (cross-run dedup)")
311337
pressCmd.Flags().DurationVar(&pressIdempotencyTTL, "idempotency-ttl", 24*time.Hour, "how long idempotency entries stay valid (e.g. 1h, 24h)")
338+
pressCmd.Flags().BoolVarP(&pressFollow, "follow", "f", false, "stream stdout/stderr to stderr as the press runs (final Result still goes to stdout)")
312339
rootCmd.AddCommand(pressCmd)
313340
}

docs/cli/buttons_press.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ buttons press [name] [flags]
3939
```
4040
--arg stringArray argument as key=value
4141
--dry-run show what would execute without running
42+
-f, --follow stream stdout/stderr to stderr as the press runs (final Result still goes to stdout)
4243
-h, --help help for press
4344
--idempotency-key string reuse the cached result for this key if present (cross-run dedup)
4445
--idempotency-ttl duration how long idempotency entries stay valid (e.g. 1h, 24h) (default 24h0m0s)

0 commit comments

Comments
 (0)