Skip to content

feat: add --watch mode for continuous CLI polling#1

Merged
sushantvema-harper merged 4 commits into
mainfrom
feat/watch-mode
Feb 7, 2026
Merged

feat: add --watch mode for continuous CLI polling#1
sushantvema-harper merged 4 commits into
mainfrom
feat/watch-mode

Conversation

@flancast90

Copy link
Copy Markdown
Contributor

Summary

Adds a --watch global flag (with configurable --interval <SECONDS>, default 30) that re-runs any read-only CLI command on a polling loop. This enables continuous monitoring of Temporal workflows directly from the terminal or through piped toolchains.

  • TTY mode: clears the screen and reprints the full output each cycle with a timestamped header ([2026-02-06 20:04:05] Refreshing every 30s... (Ctrl+C to stop))
  • Pipe mode: appends JSON output each cycle with no clearing, ideal for streaming to jq, alerting scripts, or log aggregation

Supported commands

Command Example
workflow list tpv --watch --interval 10 workflow list --status failed
workflow get tpv --watch workflow get <workflow-id>
workflow count tpv --watch --interval 5 workflow count --status running
activity list tpv --watch activity list <workflow-id>
event list tpv --watch event list <workflow-id>
insight scan tpv --watch --interval 60 insight scan --since 2h

Write commands (cancel, terminate) and serve are intentionally excluded.

Example: streaming critical alerts to jq

tpv --watch --interval 30 --output json insight scan --since 1h \
  | jq '.findings[] | select(.severity == "critical")'

Changes

New file: src/watch.rs

  • WatchConfig struct (interval, format, is_tty)
  • run_watch_loop() — generic async polling loop that accepts any Fn() -> Future<Result<()>> closure
  • Uses tokio::select! with tokio::signal::ctrl_c() for clean shutdown during the sleep interval
  • Errors are printed to stderr but do not break the loop
  • 3 unit tests: runs task multiple times, continues on error, runs at least once with long interval

Modified: src/cli.rs

  • Added --watch (bool) and --interval (u64, default 30) to GlobalArgs
  • Added #[derive(Clone)] to WorkflowAction, ActivityAction, EventAction, InsightAction so actions can be reused across loop iterations

Modified: src/lib.rs

  • Registered pub mod watch;

Modified: src/main.rs

  • Builds WatchConfig from CLI flags when --watch is set
  • Each read command's dispatch wraps its handler in run_watch_loop when watch mode is active
  • Added use std::io::IsTerminal and use std::time::Duration imports

Test plan

  • cargo check — compiles cleanly
  • cargo test --lib — all 155 tests pass (including 3 new watch tests)
  • Live test: tpv --watch --interval 3 --limit 2 workflow list against Temporal Cloud — polled successfully every 3s
  • Live test: tpv --watch --interval 3 --output json --limit 1 workflow count — appended JSON lines each cycle
  • --help output shows new --watch and --interval flags
  • Verify --watch with insight scan against live data
  • Verify Ctrl+C clean shutdown in an interactive terminal

Made with Cursor

Add global --watch and --interval flags to re-run any read-only CLI
command on a polling interval. TTY output clears and reprints each
cycle; piped output appends JSON lines for streaming consumption.

Supported commands: workflow list/get/count, activity list, event list,
insight scan. Write commands (cancel, terminate) and serve are excluded.

Includes 3 unit tests for the watch loop and has been live-tested
against Temporal Cloud.

Co-authored-by: Cursor <cursoragent@cursor.com>
@greptile-apps

greptile-apps Bot commented Feb 7, 2026

Copy link
Copy Markdown

Greptile Overview

Greptile Summary

Implements a polling watch mode for continuous monitoring of Temporal workflows. The --watch flag with configurable --interval enables read-only commands to run on a loop, clearing the screen in TTY mode or appending JSON output when piped.

Key changes:

  • New src/watch.rs module with run_watch_loop() function that handles polling, TTY detection, and graceful Ctrl+C shutdown
  • Extended CLI with --watch and --interval flags (default 30s)
  • Added Clone derives to action enums for reuse across loop iterations
  • Integrated watch mode into workflow, activity, event, and insight commands while excluding write operations

Implementation highlights:

  • Uses tokio::select! to enable immediate Ctrl+C shutdown during sleep intervals
  • Error resilience: prints errors to stderr but continues polling
  • Well-tested with 3 unit tests covering multi-run, error handling, and immediate execution scenarios
  • Correctly wraps closures with cloned values to satisfy the Fn trait bound

Confidence Score: 5/5

  • This PR is safe to merge with no issues found
  • The implementation is clean, well-tested, and properly scoped. All code follows Rust best practices, uses appropriate async patterns, handles errors gracefully, and includes comprehensive unit tests. The watch mode is correctly limited to read-only commands, preventing accidental repeated execution of destructive operations.
  • No files require special attention

Important Files Changed

Filename Overview
src/watch.rs New module with clean polling loop implementation and comprehensive tests
src/cli.rs Added --watch and --interval flags, added Clone derives for action enums
src/lib.rs Simple module registration for the new watch module
src/main.rs Integrated watch mode with proper wrapping of read commands, excludes write operations

Sequence Diagram

sequenceDiagram
    participant User
    participant CLI as CLI Parser
    participant Main as main.rs
    participant Watch as watch.rs
    participant Commands as Command Handlers
    participant Client as Temporal Client

    User->>CLI: tpv --watch --interval 30 workflow list
    CLI->>Main: Parse args (watch=true, interval=30)
    Main->>Main: Build WatchConfig (interval, is_tty)
    Main->>Client: create_client()
    Client-->>Main: Arc<dyn TemporalClient>
    
    alt Watch mode enabled
        Main->>Watch: run_watch_loop(config, task_closure)
        loop Every interval seconds
            alt TTY mode
                Watch->>Watch: Clear screen
                Watch->>Watch: Print timestamp header
            end
            Watch->>Commands: Execute task() closure
            Commands->>Client: client.list(filter, limit)
            Client-->>Commands: Result<Vec<Workflow>>
            Commands->>Commands: print output (table/json)
            Commands-->>Watch: Result<()>
            alt Error occurred
                Watch->>Watch: Print error to stderr
            end
            Watch->>Watch: tokio::select! sleep or ctrl_c
            alt Ctrl+C pressed
                Watch->>Watch: Break loop
                Watch-->>Main: Ok(())
            end
        end
    else Normal mode
        Main->>Commands: handle(action, client, format)
        Commands->>Client: client.list(filter, limit)
        Client-->>Commands: Result<Vec<Workflow>>
        Commands->>Commands: print output
        Commands-->>Main: Result<()>
    end
    
    Main-->>User: Exit
Loading

flancast90 and others added 3 commits February 6, 2026 18:25
When --watch is used with piped stdout, only emit output when it
changes from the previous cycle. Identical output is suppressed,
making watch mode behave like tailing new events rather than
reprinting the full dataset every interval.

- Refactor output layer: add write_* functions accepting &mut dyn Write
- Refactor command handlers: add handle_to() variants with writer param
- Watch loop captures output into buffer in pipe mode, diffs vs previous
- TTY mode unchanged (clear + full reprint each cycle)
- Move InsightsResult summary println to eprintln to avoid capture noise

Co-authored-by: Cursor <cursoragent@cursor.com>
First cycle now captures a silent baseline instead of dumping all
existing data. Only subsequent cycles that detect a change emit
output. This makes --watch behave like tailing new events rather
than replaying history on startup.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add --sort asc|desc flag (default: desc) to `tpv workflow list`.
Sorting is done client-side after fetching results, which works
across all Temporal server versions.

- asc: oldest first (ascending by start time)
- desc: newest first (descending by start time, default)

Compatible with --watch and all other flags.

Co-authored-by: Cursor <cursoragent@cursor.com>
@sushantvema-harper sushantvema-harper merged commit 437004b into main Feb 7, 2026
1 check passed
@sushantvema-harper sushantvema-harper deleted the feat/watch-mode branch February 7, 2026 02:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants