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
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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user