refactor(cli): move context commands to actor context subgroup #1194
@@ -116,7 +116,7 @@ Feature: CLI Plan and Context Commands - Complete Coverage
|
||||
Scenario: Remove file from context
|
||||
Given I have an initialized project
|
||||
And I have added files "main.py", "utils.py" to context
|
||||
When I run "cleveragents context rm main.py"
|
||||
When I run "cleveragents actor context remove main.py"
|
||||
Then the command should succeed
|
||||
And "main.py" should not be in context
|
||||
And "utils.py" should still be in context
|
||||
@@ -157,7 +157,7 @@ Feature: CLI Plan and Context Commands - Complete Coverage
|
||||
|
||||
Scenario: Cannot remove non-existent context file
|
||||
Given I have an initialized project
|
||||
When I run "cleveragents context rm non-existent.py"
|
||||
When I run "cleveragents actor context remove non-existent.py"
|
||||
Then the command should fail
|
||||
And the error message should mention "not in context"
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ def step_run_context_remove(context):
|
||||
else:
|
||||
file_to_remove = "dummy_file.py"
|
||||
|
||||
result = runner.invoke(context_app, ["rm", file_to_remove])
|
||||
result = runner.invoke(context_app, ["remove", file_to_remove])
|
||||
context.result = result
|
||||
|
||||
|
||||
@@ -424,7 +424,7 @@ def step_verify_context_help(context):
|
||||
"""Verify context help displayed."""
|
||||
assert hasattr(context, "result")
|
||||
assert context.result.exit_code == 0
|
||||
assert "Context management" in context.result.output
|
||||
assert "context management" in context.result.output.lower()
|
||||
|
||||
|
||||
@then("the plan help should display")
|
||||
|
||||
@@ -307,7 +307,7 @@ def step_exec_context_remove(context):
|
||||
project_service.get_current_project.return_value = object()
|
||||
container.project_service.return_value = project_service
|
||||
|
||||
result = runner.invoke(context_app.app, ["rm", "/file1.txt", "/file2.txt"])
|
||||
result = runner.invoke(context_app.app, ["remove", "/file1.txt", "/file2.txt"])
|
||||
context.result = result
|
||||
|
||||
|
||||
@@ -323,7 +323,7 @@ def step_exec_context_remove_no_project(context):
|
||||
project_service.get_current_project.return_value = None
|
||||
container.project_service.return_value = project_service
|
||||
|
||||
result = runner.invoke(context_app.app, ["rm", "/file1.txt"])
|
||||
result = runner.invoke(context_app.app, ["remove", "/file1.txt"])
|
||||
context.result = result
|
||||
|
||||
|
||||
@@ -339,7 +339,7 @@ def step_exec_context_remove_relative(context):
|
||||
project_service.get_current_project.return_value = object()
|
||||
container.project_service.return_value = project_service
|
||||
|
||||
result = runner.invoke(context_app.app, ["rm", "file1.txt", "file2.txt"])
|
||||
result = runner.invoke(context_app.app, ["remove", "file1.txt", "file2.txt"])
|
||||
context.result = result
|
||||
|
||||
|
||||
@@ -356,7 +356,7 @@ def step_exec_context_remove_failure(context):
|
||||
project_service.get_current_project.return_value = object()
|
||||
container.project_service.return_value = project_service
|
||||
|
||||
result = runner.invoke(context_app.app, ["rm", "/file1.txt"])
|
||||
result = runner.invoke(context_app.app, ["remove", "/file1.txt"])
|
||||
context.result = result
|
||||
|
||||
|
||||
@@ -822,8 +822,8 @@ def step_check_empty_message(context):
|
||||
f"Clean output (no color codes): '{clean_output}'"
|
||||
)
|
||||
|
||||
assert "agents context add" in clean_output, (
|
||||
f"Expected help text 'agents context add' in output.\n"
|
||||
assert "agents actor context add" in clean_output, (
|
||||
f"Expected help text 'agents actor context add' in output.\n"
|
||||
f"Raw output: '{context.result.output}'\n"
|
||||
f"Clean output (no color codes): '{clean_output}'"
|
||||
)
|
||||
@@ -1166,47 +1166,47 @@ def step_then_show_returns_content(context):
|
||||
|
||||
@when("the programmatic remove command runs without an active project")
|
||||
def step_when_programmatic_remove_no_project(context):
|
||||
"""Execute rm_command without an active project configured."""
|
||||
"""Execute remove_command without an active project configured."""
|
||||
context.programmatic_exception = None
|
||||
with patched_container(context, project=None):
|
||||
try:
|
||||
context_app.rm_command(["missing.txt"])
|
||||
context_app.remove_command(["missing.txt"])
|
||||
except Exception as exc:
|
||||
context.programmatic_exception = exc
|
||||
|
||||
|
||||
@when("the programmatic remove command runs for files")
|
||||
def step_when_programmatic_remove(context):
|
||||
"""Execute rm_command where files are missing."""
|
||||
"""Execute remove_command where files are missing."""
|
||||
context.programmatic_exception = None
|
||||
with patched_container(context, project=object()):
|
||||
try:
|
||||
context_app.rm_command(["missing.txt", "other.txt"])
|
||||
context_app.remove_command(["missing.txt", "other.txt"])
|
||||
except Exception as exc:
|
||||
context.programmatic_exception = exc
|
||||
|
||||
|
||||
@when("the programmatic remove command runs with removable files")
|
||||
def step_when_programmatic_remove_success(context):
|
||||
"""Execute rm_command when files are present in context."""
|
||||
"""Execute remove_command when files are present in context."""
|
||||
context.programmatic_exception = None
|
||||
context.mock_service.remove_from_context.reset_mock()
|
||||
with patched_container(context, project=object()):
|
||||
try:
|
||||
context_app.rm_command(["/file1.txt", "/file2.txt"])
|
||||
context_app.remove_command(["/file1.txt", "/file2.txt"])
|
||||
except Exception as exc:
|
||||
context.programmatic_exception = exc
|
||||
|
||||
|
||||
@then("a CleverAgentsError is raised for programmatic remove")
|
||||
def step_then_remove_requires_presence(context):
|
||||
"""Ensure rm_command raises when files are not in context."""
|
||||
"""Ensure remove_command raises when files are not in context."""
|
||||
assert isinstance(context.programmatic_exception, CleverAgentsError)
|
||||
|
||||
|
||||
@then("the programmatic remove command should complete successfully")
|
||||
def step_then_remove_success(context):
|
||||
"""Ensure rm_command succeeds when files are removed."""
|
||||
"""Ensure remove_command succeeds when files are removed."""
|
||||
assert context.programmatic_exception is None
|
||||
assert context.mock_service.remove_from_context.call_count == 2
|
||||
|
||||
|
||||
@@ -198,17 +198,17 @@ Show Context Content
|
||||
[Teardown] Cleanup Test Directory
|
||||
|
||||
Remove File From Context
|
||||
[Documentation] Test context rm command
|
||||
[Documentation] Test actor context remove command
|
||||
[Setup] Initialize Test Project With Context
|
||||
|
||||
# Remove test.py
|
||||
${result}= Run Process ${PYTHON} -m cleveragents context rm test.py
|
||||
# Remove test.py via canonical path: agents actor context remove
|
||||
${result}= Run Process ${PYTHON} -m cleveragents actor context remove test.py
|
||||
... cwd=${TEST_DIR} timeout=120s on_timeout=kill
|
||||
|
||||
Should Be Equal As Integers ${result.rc} 0
|
||||
|
||||
# Verify removal
|
||||
${result}= Run Process ${PYTHON} -m cleveragents context list
|
||||
${result}= Run Process ${PYTHON} -m cleveragents actor context list
|
||||
... cwd=${TEST_DIR} timeout=120s on_timeout=kill
|
||||
|
||||
Should Not Contain ${result.stdout} test.py
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
This module implements context-related commands for managing files and directories
|
||||
that the AI will work with.
|
||||
|
||||
Canonical path: ``agents actor context <subcommand>``
|
||||
Deprecated alias: ``agents context <subcommand>`` (emits deprecation warning)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -23,7 +26,9 @@ if TYPE_CHECKING:
|
||||
from cleveragents.domain.models.core import Context
|
||||
|
||||
# Create sub-app for context commands
|
||||
app = typer.Typer(help="Context management commands")
|
||||
app = typer.Typer(
|
||||
help="Actor context management commands (canonical: agents actor context)"
|
||||
)
|
||||
console = _get_console()
|
||||
|
||||
|
||||
@@ -142,7 +147,7 @@ def show_command(path: str | None = None) -> str | None:
|
||||
return f"Total files: {len(context_files)}"
|
||||
|
||||
|
||||
def rm_command(paths: list[str]) -> None:
|
||||
def remove_command(paths: list[str]) -> None:
|
||||
"""Programmatic interface for removing files from context.
|
||||
|
||||
Args:
|
||||
@@ -177,6 +182,18 @@ def rm_command(paths: list[str]) -> None:
|
||||
raise CleverAgentsError(f"File(s) not in context: {', '.join(not_in_context)}")
|
||||
|
||||
|
||||
def rm_command(paths: list[str]) -> None:
|
||||
"""Deprecated alias for :func:`remove_command`."""
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
"rm_command is deprecated; use remove_command instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
remove_command(paths)
|
||||
|
||||
|
||||
def clear_command() -> None:
|
||||
"""Programmatic interface for clearing all context files."""
|
||||
from cleveragents.application.container import get_container
|
||||
@@ -285,7 +302,7 @@ def context_load(
|
||||
context_add(paths, recursive)
|
||||
|
||||
|
||||
@app.command("rm")
|
||||
@app.command("remove")
|
||||
def context_remove(
|
||||
paths: Annotated[
|
||||
list[str],
|
||||
@@ -358,6 +375,25 @@ def context_remove(
|
||||
raise typer.Abort() from e
|
||||
|
||||
|
||||
@app.command("rm", hidden=True)
|
||||
def context_rm_deprecated(
|
||||
paths: Annotated[
|
||||
list[str],
|
||||
typer.Argument(help="Paths to remove from context"),
|
||||
],
|
||||
) -> None:
|
||||
"""Remove files from context (deprecated: use 'remove' instead)."""
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
"'agents context rm' is deprecated; use 'agents actor context remove' instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=1,
|
||||
)
|
||||
console.print("[yellow]Warning:[/yellow] 'rm' is deprecated; use 'remove' instead.")
|
||||
context_remove(paths)
|
||||
|
||||
|
||||
@app.command("list")
|
||||
def context_list(
|
||||
context_dir: Annotated[
|
||||
@@ -412,7 +448,7 @@ def context_list(
|
||||
|
||||
if not context_files:
|
||||
console.print("[yellow]No files in context.[/yellow]")
|
||||
console.print("Use 'agents context add <path>' to add files.")
|
||||
console.print("Use 'agents actor context add <path>' to add files.")
|
||||
return
|
||||
|
||||
# Display context files
|
||||
@@ -519,7 +555,8 @@ def context_show(
|
||||
console.print(f"Total size: {total_size:,} bytes")
|
||||
|
||||
console.print(
|
||||
"\nUse 'agents context show <file>' to view specific file content."
|
||||
"\nUse 'agents actor context show <file>' to view"
|
||||
" specific file content."
|
||||
)
|
||||
|
||||
except CleverAgentsError as e:
|
||||
|
||||
@@ -110,7 +110,21 @@ def _register_subcommands() -> None:
|
||||
return
|
||||
|
||||
app.add_typer(project.app, name="project", help="Project management")
|
||||
app.add_typer(context.app, name="context", help="Context management")
|
||||
|
||||
# Register context as a sub-app of actor (canonical: agents actor context)
|
||||
actor.app.add_typer(
|
||||
context.app,
|
||||
name="context",
|
||||
help="Actor context management commands",
|
||||
)
|
||||
|
||||
# Keep top-level 'agents context' as deprecated alias
|
||||
app.add_typer(
|
||||
context.app,
|
||||
name="context",
|
||||
help="Context management (deprecated: use 'agents actor context' instead)",
|
||||
deprecated=True,
|
||||
)
|
||||
app.add_typer(
|
||||
plan.app,
|
||||
name="plan",
|
||||
@@ -236,7 +250,7 @@ def _print_basic_help() -> None:
|
||||
typer.echo("Usage: cleveragents [OPTIONS] COMMAND [ARGS]...")
|
||||
typer.echo("\nCommon commands:")
|
||||
typer.echo(" project Project management")
|
||||
typer.echo(" context Context management")
|
||||
typer.echo(" actor context Actor context management")
|
||||
typer.echo(" plan Plan operations (actor required)")
|
||||
typer.echo(" actor Actor management and defaults")
|
||||
typer.echo(" init Initialize a project")
|
||||
|
||||
Reference in New Issue
Block a user