fix(cli): allow global format flag after subcommands
CI / push-validation (pull_request) Successful in 17s
CI / typecheck (pull_request) Successful in 54s
CI / build (pull_request) Successful in 31s
CI / helm (pull_request) Successful in 39s
CI / e2e_tests (pull_request) Failing after 1m56s
CI / lint (pull_request) Successful in 3m22s
CI / quality (pull_request) Successful in 3m42s
CI / security (pull_request) Successful in 4m6s
CI / integration_tests (pull_request) Failing after 4m7s
CI / unit_tests (pull_request) Successful in 8m49s
CI / docker (pull_request) Successful in 1m30s
CI / coverage (pull_request) Successful in 10m28s
CI / status-check (pull_request) Failing after 1s
CI / benchmark-publish (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Successful in 57m13s
CI / push-validation (pull_request) Successful in 17s
CI / typecheck (pull_request) Successful in 54s
CI / build (pull_request) Successful in 31s
CI / helm (pull_request) Successful in 39s
CI / e2e_tests (pull_request) Failing after 1m56s
CI / lint (pull_request) Successful in 3m22s
CI / quality (pull_request) Successful in 3m42s
CI / security (pull_request) Successful in 4m6s
CI / integration_tests (pull_request) Failing after 4m7s
CI / unit_tests (pull_request) Successful in 8m49s
CI / docker (pull_request) Successful in 1m30s
CI / coverage (pull_request) Successful in 10m28s
CI / status-check (pull_request) Failing after 1s
CI / benchmark-publish (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Successful in 57m13s
Closes #6879\n\nISSUES CLOSED: #6879
This commit is contained in:
@@ -147,6 +147,11 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||
`sandbox_root=.cleveragents/sandbox/`, so LLM file output (`FILE:` blocks)
|
||||
is written to disk during the execute phase. (#4222)
|
||||
|
||||
- **CLI global format flag parsing** (#6879): Promotes global CLI options before
|
||||
Typer parsing so `agents <command> --format json` and `-f` shorthand work from
|
||||
the documented position. Adds regression coverage for version, info, and
|
||||
diagnostics commands.
|
||||
|
||||
- **Robot Framework TDD Listener Guards** (#5436): Added three guard conditions to the
|
||||
`tdd_expected_fail_listener` `end_test()` function to prevent blindly inverting ALL test
|
||||
failures to passes, which was masking infrastructure errors and causing flaky CI behavior.
|
||||
|
||||
@@ -44,6 +44,25 @@ Feature: Global --format flag propagation to subcommands
|
||||
Then the global format command should succeed
|
||||
And the global format output should be valid JSON
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Regression: global --format accepted after the subcommand name
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
Scenario: Global --format flag works after the info subcommand name
|
||||
When I invoke the CLI with global args "info --format json"
|
||||
Then the global format command should succeed
|
||||
And the global format output should be valid JSON
|
||||
|
||||
Scenario: Global --format flag works after the version subcommand name
|
||||
When I invoke the CLI with global args "version --format json"
|
||||
Then the global format command should succeed
|
||||
And the global format output should be valid JSON
|
||||
|
||||
Scenario: Global --format flag works after the diagnostics subcommand name
|
||||
When I invoke the CLI with global args "diagnostics --format json"
|
||||
Then the global format command should succeed
|
||||
And the global format output should be valid JSON
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Subtask 3: version, info, diagnostics read format from global ctx.obj
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
@@ -7,7 +7,9 @@ import sys
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Annotated, Any
|
||||
|
||||
import click
|
||||
import typer
|
||||
from typer.core import TyperGroup
|
||||
|
||||
from cleveragents import __version__
|
||||
from cleveragents.cli.formatting import OutputFormat
|
||||
@@ -20,6 +22,81 @@ _console: "Console | None" = None
|
||||
_err_console: "Console | None" = None
|
||||
_subcommands_registered = False
|
||||
|
||||
_GLOBAL_VALUE_OPTIONS = frozenset({"--format"})
|
||||
_GLOBAL_VALUE_SHORT_OPTIONS = frozenset({"-f"})
|
||||
_GLOBAL_VALUE_EQ_PREFIXES = ("--format=",)
|
||||
_GLOBAL_FLAG_OPTIONS = frozenset({"--show-secrets", "--version", "-V"})
|
||||
|
||||
|
||||
def _split_short_value_option(option: str) -> tuple[str, str] | None:
|
||||
for short in _GLOBAL_VALUE_SHORT_OPTIONS:
|
||||
if option.startswith(short) and len(option) > len(short):
|
||||
return short, option[len(short) :]
|
||||
return None
|
||||
|
||||
|
||||
def _promote_global_options(args: list[str]) -> list[str]:
|
||||
"""Move global root options before subcommands for Typer parsing."""
|
||||
|
||||
if not args:
|
||||
return args
|
||||
|
||||
promoted: list[str] = []
|
||||
remaining: list[str] = []
|
||||
i = 0
|
||||
length = len(args)
|
||||
|
||||
while i < length:
|
||||
arg = args[i]
|
||||
|
||||
if arg == "--":
|
||||
remaining.extend(args[i:])
|
||||
break
|
||||
|
||||
if arg in _GLOBAL_FLAG_OPTIONS:
|
||||
promoted.append(arg)
|
||||
i += 1
|
||||
continue
|
||||
|
||||
if arg in _GLOBAL_VALUE_OPTIONS:
|
||||
if i + 1 < length:
|
||||
promoted.extend([arg, args[i + 1]])
|
||||
i += 2
|
||||
else:
|
||||
remaining.append(arg)
|
||||
i += 1
|
||||
continue
|
||||
|
||||
eq_prefix = next(
|
||||
(prefix for prefix in _GLOBAL_VALUE_EQ_PREFIXES if arg.startswith(prefix)),
|
||||
None,
|
||||
)
|
||||
if eq_prefix is not None and len(arg) > len(eq_prefix):
|
||||
promoted.extend([eq_prefix.rstrip("="), arg[len(eq_prefix) :]])
|
||||
i += 1
|
||||
continue
|
||||
|
||||
split_short = _split_short_value_option(arg)
|
||||
if split_short is not None:
|
||||
short_opt, value = split_short
|
||||
promoted.extend([short_opt, value])
|
||||
i += 1
|
||||
continue
|
||||
|
||||
remaining.append(arg)
|
||||
i += 1
|
||||
|
||||
return promoted + remaining
|
||||
|
||||
|
||||
class CleverAgentsTyperGroup(TyperGroup):
|
||||
"""Typer group that promotes global options before subcommand parsing."""
|
||||
|
||||
def parse_args(self, ctx: click.Context, args: list[str]) -> list[str]:
|
||||
promoted_args = _promote_global_options(list(args))
|
||||
return super().parse_args(ctx, promoted_args)
|
||||
|
||||
|
||||
__all__ = [
|
||||
"_print_basic_help",
|
||||
"app",
|
||||
@@ -68,6 +145,7 @@ app: Any = typer.Typer(
|
||||
pretty_exceptions_enable=True,
|
||||
pretty_exceptions_show_locals=False,
|
||||
rich_markup_mode="rich",
|
||||
cls=CleverAgentsTyperGroup,
|
||||
)
|
||||
|
||||
|
||||
@@ -700,6 +778,9 @@ def main(args: list[str] | None = None) -> int:
|
||||
if not args:
|
||||
args = ["--help"]
|
||||
|
||||
# Global CLI options are promoted by CleverAgentsTyperGroup.parse_args,
|
||||
# so we avoid reprocessing here to prevent double handling.
|
||||
|
||||
# Fast paths for lightweight flags to avoid heavy imports when using the
|
||||
# real Typer app. If the app is monkeypatched (e.g., tests), fall back to
|
||||
# normal execution so patched call paths are exercised.
|
||||
|
||||
Reference in New Issue
Block a user