forked from cleveragents/cleveragents-core
feat(cli): register v3 project and resource commands in main.py + DI wiring (B2.9)
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
@cli @v3 @registration
|
||||
Feature: V3 CLI Registration and DI Wiring
|
||||
As a developer using the CleverAgents CLI
|
||||
I want v3 project and resource commands registered in main.py
|
||||
So that I can access them from the top-level CLI
|
||||
|
||||
Background:
|
||||
Given the v3 CLI registration module is loaded
|
||||
|
||||
# ── Subcommand registration ──
|
||||
|
||||
Scenario: project-v3 subcommand is registered in the CLI app
|
||||
When I check the v3 registered subcommand names
|
||||
Then the v3 subcommand "project-v3" should be present
|
||||
|
||||
Scenario: resource-v3 subcommand is registered in the CLI app
|
||||
When I check the v3 registered subcommand names
|
||||
Then the v3 subcommand "resource-v3" should be present
|
||||
|
||||
Scenario: project-v3 is listed in valid_cmds for argument validation
|
||||
When I inspect the v3 valid commands list
|
||||
Then the v3 valid command "project-v3" should be listed
|
||||
|
||||
Scenario: resource-v3 is listed in valid_cmds for argument validation
|
||||
When I inspect the v3 valid commands list
|
||||
Then the v3 valid command "resource-v3" should be listed
|
||||
|
||||
# ── DI Container wiring ──
|
||||
|
||||
Scenario: DI container provides ProjectServiceV3
|
||||
When I request the v3 project service from the container
|
||||
Then the v3 container should return a ProjectServiceV3 instance
|
||||
|
||||
Scenario: DI container provides ResourceServiceV3
|
||||
When I request the v3 resource service from the container
|
||||
Then the v3 container should return a ResourceServiceV3 instance
|
||||
|
||||
# ── End-to-end smoke: commands don't crash on --help ──
|
||||
|
||||
Scenario: project-v3 --help runs without error
|
||||
When I invoke the v3 CLI with "project-v3 --help"
|
||||
Then the v3 CLI exit code should be 0
|
||||
|
||||
Scenario: resource-v3 --help runs without error
|
||||
When I invoke the v3 CLI with "resource-v3 --help"
|
||||
Then the v3 CLI exit code should be 0
|
||||
@@ -0,0 +1,133 @@
|
||||
"""Step definitions for V3 CLI registration and DI wiring feature.
|
||||
|
||||
All step texts use the 'v3' prefix to avoid global behave
|
||||
step-name collisions with existing definitions.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from behave import given, then, when # type: ignore[import-untyped]
|
||||
|
||||
|
||||
# ── Background ──
|
||||
|
||||
|
||||
@given("the v3 CLI registration module is loaded")
|
||||
def step_v3_cli_module_loaded(context):
|
||||
"""Import the CLI main module so registration runs."""
|
||||
import importlib
|
||||
|
||||
context.cli_main = importlib.import_module("cleveragents.cli.main")
|
||||
# Ensure subcommands are registered
|
||||
context.cli_main.ensure_cli_commands_registered()
|
||||
|
||||
|
||||
# ── Subcommand registration ──
|
||||
|
||||
|
||||
@when("I check the v3 registered subcommand names")
|
||||
def step_check_v3_subcommand_names(context):
|
||||
"""Collect registered Typer group names."""
|
||||
app = context.cli_main.app
|
||||
context.v3_group_names = [g.name for g in app.registered_groups]
|
||||
|
||||
|
||||
@then('the v3 subcommand "{name}" should be present')
|
||||
def step_v3_subcommand_present(context, name):
|
||||
"""Assert a subcommand name exists in registered groups."""
|
||||
assert name in context.v3_group_names, (
|
||||
f"Subcommand '{name}' not found in registered groups: {context.v3_group_names}"
|
||||
)
|
||||
|
||||
|
||||
# ── valid_cmds list ──
|
||||
|
||||
|
||||
@when("I inspect the v3 valid commands list")
|
||||
def step_inspect_v3_valid_cmds(context):
|
||||
"""Read the valid_cmds list from the main() function source.
|
||||
|
||||
Since valid_cmds is a local variable inside main(), we extract it
|
||||
by inspecting the source code. Alternatively, we call main() with
|
||||
a known-bad command and check the error, but reading source is simpler.
|
||||
"""
|
||||
import inspect
|
||||
import re
|
||||
|
||||
src = inspect.getsource(context.cli_main.main)
|
||||
# Find the valid_cmds = [...] list literal
|
||||
match = re.search(r"valid_cmds\s*=\s*\[(.*?)\]", src, re.DOTALL)
|
||||
assert match, "valid_cmds list not found in main() source"
|
||||
raw = match.group(1)
|
||||
# Extract all string literals from it
|
||||
context.v3_valid_cmds = re.findall(r'"([^"]+)"', raw)
|
||||
|
||||
|
||||
@then('the v3 valid command "{name}" should be listed')
|
||||
def step_v3_valid_cmd_listed(context, name):
|
||||
"""Assert a command name is in the valid_cmds list."""
|
||||
assert name in context.v3_valid_cmds, (
|
||||
f"Command '{name}' not in valid_cmds: {context.v3_valid_cmds}"
|
||||
)
|
||||
|
||||
|
||||
# ── DI Container wiring ──
|
||||
|
||||
|
||||
@when("I request the v3 project service from the container")
|
||||
def step_request_v3_project_service(context):
|
||||
"""Get ProjectServiceV3 from the DI container."""
|
||||
from cleveragents.application.container import get_container
|
||||
|
||||
container = get_container()
|
||||
context.v3_project_service = container.project_service_v3()
|
||||
|
||||
|
||||
@then("the v3 container should return a ProjectServiceV3 instance")
|
||||
def step_v3_container_project_service(context):
|
||||
"""Assert the container returned a ProjectServiceV3."""
|
||||
from cleveragents.application.services.project_service_v3 import (
|
||||
ProjectServiceV3,
|
||||
)
|
||||
|
||||
assert isinstance(context.v3_project_service, ProjectServiceV3), (
|
||||
f"Expected ProjectServiceV3, got {type(context.v3_project_service)}"
|
||||
)
|
||||
|
||||
|
||||
@when("I request the v3 resource service from the container")
|
||||
def step_request_v3_resource_service(context):
|
||||
"""Get ResourceServiceV3 from the DI container."""
|
||||
from cleveragents.application.container import get_container
|
||||
|
||||
container = get_container()
|
||||
context.v3_resource_service = container.resource_service_v3()
|
||||
|
||||
|
||||
@then("the v3 container should return a ResourceServiceV3 instance")
|
||||
def step_v3_container_resource_service(context):
|
||||
"""Assert the container returned a ResourceServiceV3."""
|
||||
from cleveragents.application.services.resource_service_v3 import (
|
||||
ResourceServiceV3,
|
||||
)
|
||||
|
||||
assert isinstance(context.v3_resource_service, ResourceServiceV3), (
|
||||
f"Expected ResourceServiceV3, got {type(context.v3_resource_service)}"
|
||||
)
|
||||
|
||||
|
||||
# ── Smoke tests: --help ──
|
||||
|
||||
|
||||
@when('I invoke the v3 CLI with "{args}"')
|
||||
def step_invoke_v3_cli(context, args):
|
||||
"""Run the CLI main function with the given args."""
|
||||
context.v3_exit_code = context.cli_main.main(args.split())
|
||||
|
||||
|
||||
@then("the v3 CLI exit code should be {code:d}")
|
||||
def step_v3_cli_exit_code(context, code):
|
||||
"""Assert the CLI exit code."""
|
||||
assert context.v3_exit_code == code, (
|
||||
f"Expected exit code {code}, got {context.v3_exit_code}"
|
||||
)
|
||||
@@ -14,6 +14,8 @@ from cleveragents.application.services.actor_service import ActorService
|
||||
from cleveragents.application.services.context_service import ContextService
|
||||
from cleveragents.application.services.plan_service import PlanService
|
||||
from cleveragents.application.services.project_service import ProjectService
|
||||
from cleveragents.application.services.project_service_v3 import ProjectServiceV3
|
||||
from cleveragents.application.services.resource_service_v3 import ResourceServiceV3
|
||||
from cleveragents.application.services.vector_store_service import VectorStoreService
|
||||
from cleveragents.config.settings import Settings, get_settings
|
||||
from cleveragents.domain.providers.ai_provider import AIProviderInterface
|
||||
@@ -129,6 +131,10 @@ class Container(containers.DeclarativeContainer):
|
||||
unit_of_work=unit_of_work,
|
||||
)
|
||||
|
||||
# V3 services (in-memory, no persistence dependencies yet)
|
||||
project_service_v3 = providers.Factory(ProjectServiceV3)
|
||||
resource_service_v3 = providers.Factory(ResourceServiceV3)
|
||||
|
||||
vector_store_service = providers.Factory(
|
||||
VectorStoreService,
|
||||
settings=settings,
|
||||
|
||||
@@ -78,6 +78,8 @@ def _register_subcommands() -> None:
|
||||
try:
|
||||
from cleveragents.cli.commands import action, actor, context, plan, project
|
||||
from cleveragents.cli.commands.auto_debug import app as auto_debug_app
|
||||
from cleveragents.cli.commands.project_v3 import app as project_v3_app
|
||||
from cleveragents.cli.commands.resource_v3 import app as resource_v3_app
|
||||
except Exception as exc: # pragma: no cover
|
||||
import traceback
|
||||
|
||||
@@ -110,6 +112,16 @@ def _register_subcommands() -> None:
|
||||
help="Manage actions (reusable plan templates) for the v3 plan lifecycle",
|
||||
)
|
||||
app.add_typer(auto_debug_app, name="auto-debug", help="Auto-debug operations")
|
||||
app.add_typer(
|
||||
project_v3_app,
|
||||
name="project-v3",
|
||||
help="Project management (v3 with namespaces and resource linking)",
|
||||
)
|
||||
app.add_typer(
|
||||
resource_v3_app,
|
||||
name="resource-v3",
|
||||
help="Resource management (v3 with types, registry, and tree view)",
|
||||
)
|
||||
|
||||
_subcommands_registered = True
|
||||
|
||||
@@ -141,6 +153,8 @@ def _print_basic_help() -> None:
|
||||
typer.echo(" build Build the current plan")
|
||||
typer.echo(" apply Apply plan changes")
|
||||
typer.echo(" auto-debug Auto-debug operations")
|
||||
typer.echo(" project-v3 Project management (v3)")
|
||||
typer.echo(" resource-v3 Resource management (v3)")
|
||||
typer.echo(" version Show version")
|
||||
typer.echo("")
|
||||
typer.echo("Actors: set a default with 'agents actor set-default <name>'.")
|
||||
@@ -458,6 +472,8 @@ def main(args: list[str] | None = None) -> int:
|
||||
"actor",
|
||||
"action", # v3 plan lifecycle actions
|
||||
"auto-debug", # Auto-debug commands
|
||||
"project-v3", # V3 project commands (namespaces, linking)
|
||||
"resource-v3", # V3 resource commands (types, registry, tree)
|
||||
"tell", # Shortcut for plan tell
|
||||
"build", # Shortcut for plan build
|
||||
"apply", # Shortcut for plan apply
|
||||
|
||||
Reference in New Issue
Block a user