fix(cli): add [REGEX] positional argument to actor context list (#6500)
CI / lint (pull_request) Successful in 36s
CI / quality (pull_request) Successful in 39s
CI / typecheck (pull_request) Successful in 54s
CI / helm (pull_request) Successful in 32s
CI / build (pull_request) Successful in 39s
CI / security (pull_request) Successful in 56s
CI / push-validation (pull_request) Successful in 32s
CI / e2e_tests (pull_request) Successful in 3m38s
CI / integration_tests (pull_request) Failing after 4m21s
CI / unit_tests (pull_request) Failing after 7m21s
CI / docker (pull_request) Has been skipped
CI / coverage (pull_request) Successful in 13m26s
CI / status-check (pull_request) Failing after 1s
CI / benchmark-publish (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Successful in 58m7s

ISSUES CLOSED: #6500
This commit is contained in:
2026-04-10 00:44:54 +00:00
parent d8a31527f3
commit bd403a78fd
5 changed files with 154 additions and 7 deletions
+9
View File
@@ -6,6 +6,15 @@ Feature: Actor context remove, export, and import commands
Background:
Given a temporary context directory for actor context tests
@actor_context_list_regex
Scenario: List actor contexts filtered by regex
Given an actor context named "docs" exists
And an actor context named "notes" exists
When I run actor context list "docs"
Then the actor context list command should succeed
And the list output should contain "docs"
And the list output should not contain "notes"
# ── context remove ─────────────────────────────────────────
Scenario: Remove a named actor context
@@ -105,6 +105,36 @@ def step_create_json_file_with_name(context, name):
context.import_file.write_text(json.dumps(data, indent=2), encoding="utf-8")
# ---------------------------------------------------------------------------
# When — list
# ---------------------------------------------------------------------------
@when('I run actor context list "{pattern}"')
def step_list_with_pattern(context, pattern):
context.result = context.runner.invoke(
actor_context_app,
[
"list",
pattern,
"--context-dir",
str(context.context_dir),
],
)
@when("I run actor context list without a pattern")
def step_list_without_pattern(context):
context.result = context.runner.invoke(
actor_context_app,
[
"list",
"--context-dir",
str(context.context_dir),
],
)
# ---------------------------------------------------------------------------
# When — remove
# ---------------------------------------------------------------------------
@@ -330,6 +360,15 @@ def step_roundtrip_import(context, name):
# ---------------------------------------------------------------------------
@then("the actor context list command should succeed")
def step_list_success(context):
assert context.result.exit_code == 0, (
f"Expected exit 0, got {context.result.exit_code}.\n"
f"stdout: {context.result.output}\n"
f"stderr: {getattr(context.result, 'stderr', '')}"
)
@then("the actor context remove command should succeed")
def step_remove_success(context):
assert context.result.exit_code == 0, (
@@ -414,6 +453,18 @@ def step_context_exists_check(context, name):
# ---------------------------------------------------------------------------
@then('the list output should contain "{text}"')
def step_list_output_contains(context, text):
output = context.result.output
assert text in output, f"Expected '{text}' in list output: {output}"
@then('the list output should not contain "{text}"')
def step_list_output_not_contains(context, text):
output = context.result.output
assert text not in output, f"Did not expect '{text}' in list output: {output}"
@then('the output should contain valid JSON with key "{key}"')
def step_output_json_key(context, key):
output = context.result.output
+19
View File
@@ -179,6 +179,25 @@ List Context Files
[Teardown] Cleanup Test Directory
Actor Context List Supports Regex Filter
[Documentation] Verify actor context list supports optional regex filtering
[Setup] Initialize Test Project With Context
${result}= Run Process ${PYTHON} -m cleveragents actor context list test
... cwd=${TEST_DIR} timeout=120s on_timeout=kill
Should Be Equal As Integers ${result.rc} 0
Should Contain ${result.stdout} test.py
Should Not Contain ${result.stdout} utils.py
${result}= Run Process ${PYTHON} -m cleveragents actor context list nomatch
... cwd=${TEST_DIR} timeout=120s on_timeout=kill
Should Be Equal As Integers ${result.rc} 0
Should Contain ${result.stdout} No contexts found.
[Teardown] Cleanup Test Directory
Show Context Content
[Documentation] Test context show command
[Setup] Initialize Test Project With Context
@@ -11,6 +11,7 @@ from __future__ import annotations
import hashlib
import json
import re
from pathlib import Path
from typing import Annotated, Any
@@ -29,6 +30,46 @@ console = Console()
_FORMAT_HELP = "Output format: json, yaml, plain, table, rich, or color (default: rich)"
@app.command("list")
def context_list(
regex: str | None = typer.Argument(
default=None,
help="Optional regex filter for context names",
),
context_dir: Annotated[
Path | None,
typer.Option(
"--context-dir",
help="Directory where contexts are stored",
resolve_path=True,
),
] = None,
) -> None:
"""List named actor contexts, optionally filtered by a regular expression."""
pattern: re.Pattern[str] | None = None
if regex is not None:
try:
pattern = re.compile(regex)
except re.error as exc:
console.print(f"[red]Error:[/red] Invalid regex pattern: {exc}")
raise typer.Abort() from exc
base = _default_context_base(context_dir)
context_names = _list_context_names(base)
if pattern is not None:
context_names = [name for name in context_names if pattern.search(name)]
if not context_names:
typer.echo("No contexts found.")
return
for name in context_names:
typer.echo(name)
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
+34 -7
View File
@@ -9,6 +9,7 @@ Deprecated alias: ``agents context <subcommand>`` (emits deprecation warning)
from __future__ import annotations
import re
from pathlib import Path
from typing import TYPE_CHECKING, Annotated, Any
@@ -396,6 +397,10 @@ def context_rm_deprecated(
@app.command("list")
def context_list(
regex: str | None = typer.Argument(
default=None,
help="Optional regex filter for context names",
),
context_dir: Annotated[
Path | None,
typer.Option(
@@ -409,6 +414,14 @@ def context_list(
Or list named contexts in a directory.
"""
pattern: re.Pattern[str] | None = None
if regex is not None:
try:
pattern = re.compile(regex)
except re.error as exc:
console.print(f"[red]Error:[/red] Invalid regex pattern: {exc}")
raise typer.Abort() from exc
if context_dir is not None:
# List named contexts in the given directory
ctx_base = context_dir
@@ -417,6 +430,8 @@ def context_list(
return
context_dirs = sorted([d.name for d in ctx_base.iterdir() if d.is_dir()])
if pattern is not None:
context_dirs = [name for name in context_dirs if pattern.search(name)]
if not context_dirs:
typer.echo("No contexts found.")
@@ -451,24 +466,36 @@ def context_list(
console.print("Use 'agents actor context add <path>' to add files.")
return
# Display context files
table = Table(title=f"Context Files ({len(context_files)} total)")
table.add_column("File Path", style="cyan")
table.add_column("Type", style="green")
table.add_column("Size", style="magenta")
table.add_column("Added", style="yellow")
# Filter and normalize entries
filtered_entries: list[tuple[str, str, str, str]] = []
for file_info in context_files:
path_text, type_label, size, added_at, _ = _normalize_context_entry(
file_info
)
display_name = Path(path_text).name if path_text else ""
if pattern is not None and not pattern.search(display_name):
continue
if isinstance(size, str):
size_str = size
else:
size_str = f"{size:,} bytes" if size > 0 else "0 bytes"
filtered_entries.append((display_name, type_label, size_str, added_at))
if not filtered_entries:
console.print("[yellow]No files matched the pattern.[/yellow]")
return
# Display context files
table = Table(title=f"Context Files ({len(filtered_entries)} total)")
table.add_column("File Path", style="cyan")
table.add_column("Type", style="green")
table.add_column("Size", style="magenta")
table.add_column("Added", style="yellow")
for display_name, type_label, size_str, added_at in filtered_entries:
table.add_row(
display_name,
type_label,