fix(cli): fix project context set JSON/YAML output structure #6626
@@ -523,6 +523,15 @@ _ALL_DATA_COLUMNS + ") " "SELECT " + _ALL_DATA_COLUMNS + " FROM v3_plans"`.
|
||||
example outputs now reflect comprehensive provider coverage with accurate warning
|
||||
counts and per-provider recommendations.
|
||||
|
||||
### Changed
|
||||
|
||||
- **Context Set JSON/YAML Output Structure** (#6319): The `agents project context set`
|
||||
command now produces spec-aligned structured output envelopes with `command`,
|
||||
`status`, `exit_code`, `timing`, and typed `messages` arrays (each containing a
|
||||
`level` and `text` field) for both JSON and YAML formats. Adds dedicated rendering
|
||||
helpers (`build_context_set_payload`, `render_context_set_plain`, `render_context_set_rich`)
|
||||
in `src/cleveragents/cli/rendering/project_context_set.py`.
|
||||
|
||||
### Added
|
||||
- **feat(invariants): Invariant Loading and Enforcement in Strategize Phase** (#8532):
|
||||
Implemented invariant loading and enforcement in the Strategize phase. The Strategize
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
* Rui Hu <rui.hu@cleverthis.com>
|
||||
* HAL 9000 <hal9000@cleverthis.com> has contributed fix for #10813 — wiring DecisionService into PlanExecutor for strategy decision persistence during strategize.
|
||||
|
||||
* HAL9000 <HAL9000@cleverthis.com> has contributed CLI rendering improvements and TUI overlay visibility handling for `agents project context set` output.
|
||||
|
||||
# Details
|
||||
|
||||
Below are some of the specific details of various contributions.
|
||||
|
||||
@@ -3993,7 +3993,9 @@ Set the context policy for a project and (optionally) a specific view.
|
||||
"timing": {
|
||||
"duration_ms": 42
|
||||
},
|
||||
"messages": ["Context policy updated"]
|
||||
"messages": [
|
||||
{"level": "ok", "text": "Context policy updated"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -4028,7 +4030,8 @@ Set the context policy for a project and (optionally) a specific view.
|
||||
timing:
|
||||
duration_ms: 42
|
||||
messages:
|
||||
- Context policy updated
|
||||
- level: ok
|
||||
text: Context policy updated
|
||||
```
|
||||
|
||||
###### agents project context show
|
||||
|
||||
@@ -112,3 +112,18 @@ Feature: Project context CLI commands (B2.cli)
|
||||
When I run context show on "local/ctx-app" with format "json"
|
||||
Then the context show command should succeed
|
||||
And the context output should be valid JSON
|
||||
|
||||
Scenario: Context set JSON output matches spec structure
|
||||
When I run context set spec example on "local/ctx-app" with format "json"
|
||||
Then the context set command should succeed
|
||||
And the context set JSON output should match the spec envelope
|
||||
|
||||
Scenario: Context set YAML output matches spec structure
|
||||
When I run context set spec example on "local/ctx-app" with format "yaml"
|
||||
Then the context set command should succeed
|
||||
And the context set YAML output should match the spec envelope
|
||||
|
||||
Scenario: Context set rich output matches spec presentation
|
||||
When I run context set spec example on "local/ctx-app" with format "rich"
|
||||
Then the context set command should succeed
|
||||
And the context set rich output should include spec panels
|
||||
|
||||
@@ -162,3 +162,16 @@ Feature: Project context CLI coverage boost
|
||||
When I save a policy for a nonexistent project "local/no-such-project"
|
||||
Then the save should complete without error
|
||||
And the policy should not be retrievable for "local/no-such-project"
|
||||
|
||||
# --- render_context_set_plain (plain text output path, all lines) ---
|
||||
Scenario: Context set with plain text output format
|
||||
When I run coverage-boost context set on "local/cov-app" with view "default" include-resource "res1" and format "plain"
|
||||
Then the coverage-boost command should succeed
|
||||
And the coverage-boost output should contain "Context Policy"
|
||||
And the coverage-boost output should contain "[OK] Context policy updated"
|
||||
|
||||
# --- _format_size bytes fallback (line 36 of project_context_set.py) ---
|
||||
Scenario: Context set plain output with byte-level file size shows bytes unit
|
||||
When I run coverage-boost context set on "local/cov-app" with view "default" max-file-size 100 and format "plain"
|
||||
Then the coverage-boost command should succeed
|
||||
And the coverage-boost output should contain "100 bytes"
|
||||
|
||||
@@ -95,7 +95,7 @@ def _run(context: Any, func: Any, *args: Any, **kwargs: Any) -> None:
|
||||
soft_wrap=True,
|
||||
)
|
||||
|
||||
def _test_format_output(data, format_type):
|
||||
def _test_format_output(data, format_type, *args, **kwargs):
|
||||
import sys as _sys
|
||||
from io import StringIO as _SIO
|
||||
|
||||
@@ -105,7 +105,7 @@ def _run(context: Any, func: Any, *args: Any, **kwargs: Any) -> None:
|
||||
_old = _sys.stdout
|
||||
_sys.stdout = _b
|
||||
try:
|
||||
_r = _fo(data, format_type)
|
||||
_r = _fo(data, format_type, *args, **kwargs)
|
||||
finally:
|
||||
_sys.stdout = _old
|
||||
return _r or _b.getvalue().rstrip("\n")
|
||||
@@ -379,6 +379,24 @@ def step_cb_set_temporal(context: Any, project: str, scope: str) -> None:
|
||||
_run(context, context_set, project=project, view="default", temporal_scope=scope)
|
||||
|
||||
|
||||
@when(
|
||||
'I run coverage-boost context set on "{project}" with view "{view}" max-file-size {size:d} and format "{fmt}"'
|
||||
)
|
||||
def step_cb_set_size_fmt(
|
||||
context: Any, project: str, view: str, size: int, fmt: str
|
||||
) -> None:
|
||||
from cleveragents.cli.commands.project_context import context_set
|
||||
|
||||
_run(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view=view,
|
||||
max_file_size=size,
|
||||
output_format=fmt,
|
||||
)
|
||||
|
||||
|
||||
@when('I run coverage-boost context set on "{project}" with all ACMS overrides')
|
||||
def step_cb_set_all_acms(context: Any, project: str) -> None:
|
||||
from cleveragents.cli.commands.project_context import context_set
|
||||
|
||||
@@ -12,6 +12,7 @@ from io import StringIO
|
||||
from typing import Any
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
# third-party
|
||||
import yaml
|
||||
from behave import given, then, when # type: ignore[import-untyped]
|
||||
from sqlalchemy import create_engine
|
||||
@@ -141,7 +142,7 @@ def _run_with_container(context: Any, func: Any, *args: Any, **kwargs: Any) -> N
|
||||
soft_wrap=True,
|
||||
)
|
||||
|
||||
def _test_format_output(data, format_type, **kwargs):
|
||||
def _test_format_output(data, format_type, *args, **kwargs):
|
||||
import sys as _sys
|
||||
from io import StringIO as _SIO
|
||||
|
||||
@@ -151,7 +152,7 @@ def _run_with_container(context: Any, func: Any, *args: Any, **kwargs: Any) -> N
|
||||
_old = _sys.stdout
|
||||
_sys.stdout = _b
|
||||
try:
|
||||
_r = _fo(data, format_type, **kwargs)
|
||||
_r = _fo(data, format_type, *args, **kwargs)
|
||||
finally:
|
||||
_sys.stdout = _old
|
||||
return _r or _b.getvalue().rstrip("\n")
|
||||
@@ -188,138 +189,6 @@ def _run_with_container(context: Any, func: Any, *args: Any, **kwargs: Any) -> N
|
||||
context.ctx_output = buf.getvalue()
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# context set
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
|
||||
@when(
|
||||
'I run context set on "{project}" with view "{view}" and include-resource "{res}"'
|
||||
)
|
||||
def step_ctx_set_include_res(context: Any, project: str, view: str, res: str) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view=view,
|
||||
include_resource=[res],
|
||||
)
|
||||
|
||||
|
||||
@when(
|
||||
'I run context set on "{project}" with view "{view}" and exclude-resource "{res}"'
|
||||
)
|
||||
def step_ctx_set_exclude_res(context: Any, project: str, view: str, res: str) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view=view,
|
||||
exclude_resource=[res],
|
||||
)
|
||||
|
||||
|
||||
@when('I run context set on "{project}" with view "{view}" and include-path "{path}"')
|
||||
def step_ctx_set_include_path(context: Any, project: str, view: str, path: str) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view=view,
|
||||
include_path=[path],
|
||||
)
|
||||
|
||||
|
||||
@when('I run context set on "{project}" with view "{view}" and exclude-path "{path}"')
|
||||
def step_ctx_set_exclude_path(context: Any, project: str, view: str, path: str) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view=view,
|
||||
exclude_path=[path],
|
||||
)
|
||||
|
||||
|
||||
@when('I run context set on "{project}" with view "{view}" and max-file-size {size:d}')
|
||||
def step_ctx_set_max_file_size(
|
||||
context: Any, project: str, view: str, size: int
|
||||
) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view=view,
|
||||
max_file_size=size,
|
||||
)
|
||||
|
||||
|
||||
@when('I run context set on "{project}" with view "{view}" and max-total-size {size:d}')
|
||||
def step_ctx_set_max_total_size(
|
||||
context: Any, project: str, view: str, size: int
|
||||
) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view=view,
|
||||
max_total_size=size,
|
||||
)
|
||||
|
||||
|
||||
@when('I run context set on "{project}" with invalid view "{view}"')
|
||||
def step_ctx_set_invalid_view(context: Any, project: str, view: str) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view=view,
|
||||
)
|
||||
|
||||
|
||||
@when('I run context set on "{project}" with view "{view}" and clear flag')
|
||||
def step_ctx_set_clear(context: Any, project: str, view: str) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view=view,
|
||||
clear=True,
|
||||
)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# context show
|
||||
# ------------------------------------------------------------------
|
||||
@@ -393,94 +262,20 @@ def step_ctx_simulate(context: Any, project: str) -> None:
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Given steps (pre-conditions)
|
||||
# context show output format override
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
|
||||
@given('I have set a strategize view on "{project}" with defaults')
|
||||
def step_given_strategize_view(context: Any, project: str) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view="strategize",
|
||||
include_resource=["strat-default"],
|
||||
)
|
||||
|
||||
|
||||
@given('I have set a default view on "{project}" with include-resource "{res}"')
|
||||
def step_given_default_view(context: Any, project: str, res: str) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view="default",
|
||||
include_resource=[res],
|
||||
)
|
||||
|
||||
|
||||
@given('I have set a strategize view on "{project}" with include-resource "{res}"')
|
||||
def step_given_strategize_view_res(context: Any, project: str, res: str) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view="strategize",
|
||||
include_resource=[res],
|
||||
)
|
||||
|
||||
|
||||
@given('I have set an execute view on "{project}" with include-resource "{res}"')
|
||||
def step_given_execute_view_res(context: Any, project: str, res: str) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view="execute",
|
||||
include_resource=[res],
|
||||
)
|
||||
|
||||
|
||||
@given('the context show output format is "{fmt}"')
|
||||
def step_set_ctx_show_format(context: Any, fmt: str) -> None:
|
||||
context.ctx_show_format = fmt
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Then assertions
|
||||
# Generic Then assertions shared across context commands
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
|
||||
@then("the context set command should succeed")
|
||||
def step_ctx_set_ok(context: Any) -> None:
|
||||
assert context.ctx_exit_code == 0, (
|
||||
f"Expected exit 0, got {context.ctx_exit_code}. Output: {context.ctx_output}"
|
||||
)
|
||||
|
||||
|
||||
@then("the context set command should fail")
|
||||
def step_ctx_set_fail(context: Any) -> None:
|
||||
assert context.ctx_exit_code != 0, (
|
||||
f"Expected non-zero exit, got {context.ctx_exit_code}"
|
||||
)
|
||||
|
||||
|
||||
@then("the context show command should succeed")
|
||||
def step_ctx_show_ok(context: Any) -> None:
|
||||
assert context.ctx_exit_code == 0, (
|
||||
@@ -495,78 +290,6 @@ def step_ctx_show_fail(context: Any) -> None:
|
||||
)
|
||||
|
||||
|
||||
def _read_stored_policy(context: Any) -> Any:
|
||||
"""Read the stored policy from the test DB."""
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
_read_policy,
|
||||
)
|
||||
|
||||
return _read_policy(context.ctx_session_factory, "local/ctx-app")
|
||||
|
||||
|
||||
@then('the stored policy default view should include resource "{res}"')
|
||||
def step_policy_include_res(context: Any, res: str) -> None:
|
||||
policy = _read_stored_policy(context)
|
||||
assert res in policy.default_view.include_resources, (
|
||||
f"{res} not in {policy.default_view.include_resources}"
|
||||
)
|
||||
|
||||
|
||||
@then('the stored policy default view should exclude resource "{res}"')
|
||||
def step_policy_exclude_res(context: Any, res: str) -> None:
|
||||
policy = _read_stored_policy(context)
|
||||
assert res in policy.default_view.exclude_resources, (
|
||||
f"{res} not in {policy.default_view.exclude_resources}"
|
||||
)
|
||||
|
||||
|
||||
@then('the stored policy default view should include path "{path}"')
|
||||
def step_policy_include_path(context: Any, path: str) -> None:
|
||||
policy = _read_stored_policy(context)
|
||||
assert path in policy.default_view.include_paths, (
|
||||
f"{path} not in {policy.default_view.include_paths}"
|
||||
)
|
||||
|
||||
|
||||
@then('the stored policy default view should exclude path "{path}"')
|
||||
def step_policy_exclude_path(context: Any, path: str) -> None:
|
||||
policy = _read_stored_policy(context)
|
||||
assert path in policy.default_view.exclude_paths, (
|
||||
f"{path} not in {policy.default_view.exclude_paths}"
|
||||
)
|
||||
|
||||
|
||||
@then("the stored policy default view max file size should be {size:d}")
|
||||
def step_policy_max_file_size(context: Any, size: int) -> None:
|
||||
policy = _read_stored_policy(context)
|
||||
assert policy.default_view.max_file_size == size, (
|
||||
f"Expected {size}, got {policy.default_view.max_file_size}"
|
||||
)
|
||||
|
||||
|
||||
@then("the stored policy default view max total size should be {size:d}")
|
||||
def step_policy_max_total_size(context: Any, size: int) -> None:
|
||||
policy = _read_stored_policy(context)
|
||||
assert policy.default_view.max_total_size == size, (
|
||||
f"Expected {size}, got {policy.default_view.max_total_size}"
|
||||
)
|
||||
|
||||
|
||||
@then('the stored policy strategize view should include resource "{res}"')
|
||||
def step_policy_strat_include_res(context: Any, res: str) -> None:
|
||||
policy = _read_stored_policy(context)
|
||||
assert policy.strategize_view is not None, "strategize_view is None"
|
||||
assert res in policy.strategize_view.include_resources
|
||||
|
||||
|
||||
@then("the stored policy strategize view should be None")
|
||||
def step_policy_strat_none(context: Any) -> None:
|
||||
policy = _read_stored_policy(context)
|
||||
assert policy.strategize_view is None, (
|
||||
f"Expected None, got {policy.strategize_view}"
|
||||
)
|
||||
|
||||
|
||||
@then("the context inspect command should succeed")
|
||||
def step_ctx_inspect_ok(context: Any) -> None:
|
||||
assert context.ctx_exit_code == 0, (
|
||||
@@ -592,11 +315,10 @@ def step_not_implemented(context: Any, msg: str) -> None:
|
||||
|
||||
@then('the resolved view should include resource "{res}"')
|
||||
def step_resolved_view_include_res(context: Any, res: str) -> None:
|
||||
# The show command output contains the resolved view
|
||||
# We read the policy directly for verification
|
||||
policy = _read_stored_policy(context)
|
||||
# Determine which phase was queried from the output
|
||||
# For simplicity, check all possible resolved views
|
||||
"""Verify that a resource appears in at least one resolved view."""
|
||||
from cleveragents.cli.commands.project_context import _read_policy
|
||||
|
||||
policy = _read_policy(context.ctx_session_factory, "local/ctx-app")
|
||||
found = False
|
||||
for phase in ["default", "strategize", "execute", "apply"]:
|
||||
resolved = policy.resolve_view(phase)
|
||||
|
||||
@@ -0,0 +1,440 @@
|
||||
"""Step definitions for the ``agents project context set`` CLI command.
|
||||
|
||||
Extracted from ``project_context_cli_steps.py`` to keep that file under
|
||||
the 500-line limit mandated by CONTRIBUTING.md. All shared fixtures and
|
||||
runners are imported back into this module.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
# third-party
|
||||
import yaml
|
||||
from behave import given, then, when # type: ignore[import-untyped]
|
||||
|
||||
# Shared helpers from the parent CLI steps module
|
||||
from features.steps.project_context_cli_steps import (
|
||||
_run_with_container,
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# When steps — context set operations
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
|
||||
@when(
|
||||
'I run context set on "{project}" with view "{view}" and include-resource "{res}"'
|
||||
)
|
||||
def step_ctx_set_include_res(context: Any, project: str, view: str, res: str) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view=view,
|
||||
include_resource=[res],
|
||||
)
|
||||
|
||||
|
||||
@when(
|
||||
'I run context set on "{project}" with view "{view}" and exclude-resource "{res}"'
|
||||
)
|
||||
def step_ctx_set_exclude_res(context: Any, project: str, view: str, res: str) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view=view,
|
||||
exclude_resource=[res],
|
||||
)
|
||||
|
||||
|
||||
@when('I run context set on "{project}" with view "{view}" and include-path "{path}"')
|
||||
def step_ctx_set_include_path(context: Any, project: str, view: str, path: str) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view=view,
|
||||
include_path=[path],
|
||||
)
|
||||
|
||||
|
||||
@when('I run context set on "{project}" with view "{view}" and exclude-path "{path}"')
|
||||
def step_ctx_set_exclude_path(context: Any, project: str, view: str, path: str) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view=view,
|
||||
exclude_path=[path],
|
||||
)
|
||||
|
||||
|
||||
@when('I run context set spec example on "{project}" with format "{fmt}"')
|
||||
def step_ctx_set_spec_example(context: Any, project: str, fmt: str) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view="strategize",
|
||||
include_resource=["repo"],
|
||||
exclude_path=["**/node_modules/**"],
|
||||
hot_max_tokens=12_000,
|
||||
warm_max_decisions=50,
|
||||
cold_max_decisions=200,
|
||||
query_limit=20,
|
||||
max_file_size=1_048_576,
|
||||
max_total_size=50 * 1_048_576,
|
||||
summarize=True,
|
||||
summary_max_tokens=800,
|
||||
output_format=fmt,
|
||||
)
|
||||
|
||||
|
||||
@when('I run context set on "{project}" with view "{view}" and max-file-size {size:d}')
|
||||
def step_ctx_set_max_file_size(
|
||||
context: Any, project: str, view: str, size: int
|
||||
) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view=view,
|
||||
max_file_size=size,
|
||||
)
|
||||
|
||||
|
||||
@when('I run context set on "{project}" with view "{view}" and max-total-size {size:d}')
|
||||
def step_ctx_set_max_total_size(
|
||||
context: Any, project: str, view: str, size: int
|
||||
) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view=view,
|
||||
max_total_size=size,
|
||||
)
|
||||
|
||||
|
||||
@when('I run context set on "{project}" with invalid view "{view}"')
|
||||
def step_ctx_set_invalid_view(context: Any, project: str, view: str) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view=view,
|
||||
)
|
||||
|
||||
|
||||
@when('I run context set on "{project}" with view "{view}" and clear flag')
|
||||
def step_ctx_set_clear(context: Any, project: str, view: str) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view=view,
|
||||
clear=True,
|
||||
)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Given steps — context set pre-conditions
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
|
||||
@given('I have set a strategize view on "{project}" with defaults')
|
||||
def step_given_strategize_view(context: Any, project: str) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view="strategize",
|
||||
include_resource=["strat-default"],
|
||||
)
|
||||
|
||||
|
||||
@given('I have set a default view on "{project}" with include-resource "{res}"')
|
||||
def step_given_default_view(context: Any, project: str, res: str) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view="default",
|
||||
include_resource=[res],
|
||||
)
|
||||
|
||||
|
||||
@given('I have set a strategize view on "{project}" with include-resource "{res}"')
|
||||
def step_given_strategize_view_res(context: Any, project: str, res: str) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view="strategize",
|
||||
include_resource=[res],
|
||||
)
|
||||
|
||||
|
||||
@given('I have set an execute view on "{project}" with include-resource "{res}"')
|
||||
def step_given_execute_view_res(context: Any, project: str, res: str) -> None:
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
context_set,
|
||||
)
|
||||
|
||||
_run_with_container(
|
||||
context,
|
||||
context_set,
|
||||
project=project,
|
||||
view="execute",
|
||||
include_resource=[res],
|
||||
)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Then assertions — context set output and policy verification
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
|
||||
@then("the context set command should succeed")
|
||||
def step_ctx_set_ok(context: Any) -> None:
|
||||
assert context.ctx_exit_code == 0, (
|
||||
f"Expected exit 0, got {context.ctx_exit_code}. Output: {context.ctx_output}"
|
||||
)
|
||||
|
||||
|
||||
@then("the context set command should fail")
|
||||
def step_ctx_set_fail(context: Any) -> None:
|
||||
assert context.ctx_exit_code != 0, (
|
||||
f"Expected non-zero exit, got {context.ctx_exit_code}"
|
||||
)
|
||||
|
||||
|
||||
@then("the context set JSON output should match the spec envelope")
|
||||
def step_ctx_set_json_spec(context: Any) -> None:
|
||||
output = context.ctx_output.strip()
|
||||
assert output, "No CLI output captured"
|
||||
payload = json.loads(output)
|
||||
|
||||
assert payload.get("command") == "project context set"
|
||||
assert payload.get("status") == "ok"
|
||||
assert payload.get("exit_code") == 0
|
||||
|
||||
timing = payload.get("timing", {})
|
||||
assert isinstance(timing.get("duration_ms"), int)
|
||||
|
||||
messages = payload.get("messages", [])
|
||||
assert messages and messages[0].get("text") == "Context policy updated"
|
||||
|
||||
data = payload.get("data", {})
|
||||
|
||||
context_policy = data.get("context_policy", {})
|
||||
assert context_policy.get("project") == "local/ctx-app"
|
||||
assert context_policy.get("view") == "strategize"
|
||||
assert context_policy.get("include_resources") == ["repo"]
|
||||
assert context_policy.get("exclude_paths") == ["**/node_modules/**"]
|
||||
|
||||
limits = data.get("limits", {})
|
||||
assert limits.get("hot_max_tokens") == 12_000
|
||||
assert limits.get("warm_max_decisions") == 50
|
||||
assert limits.get("cold_max_decisions") == 200
|
||||
assert limits.get("query_limit") == 20
|
||||
assert limits.get("max_file_size") == "1 MB"
|
||||
assert limits.get("max_total_size") == "50 MB"
|
||||
|
||||
summarization = data.get("summarization", {})
|
||||
assert summarization.get("enabled") is True
|
||||
assert summarization.get("max_tokens") == 800
|
||||
|
||||
other_views = data.get("other_views", {})
|
||||
assert other_views.get("default") == "(unset)"
|
||||
assert other_views.get("execute") == "(default)"
|
||||
assert other_views.get("apply") == "(default)"
|
||||
|
||||
|
||||
@then("the context set YAML output should match the spec envelope")
|
||||
def step_ctx_set_yaml_spec(context: Any) -> None:
|
||||
output = context.ctx_output.strip()
|
||||
assert output, "No CLI output captured"
|
||||
payload = yaml.safe_load(output)
|
||||
|
||||
assert payload.get("command") == "project context set"
|
||||
assert payload.get("status") == "ok"
|
||||
assert payload.get("exit_code") == 0
|
||||
|
||||
timing = payload.get("timing", {})
|
||||
assert isinstance(timing.get("duration_ms"), int)
|
||||
|
||||
messages = payload.get("messages", [])
|
||||
assert messages and messages[0].get("text") == "Context policy updated"
|
||||
|
||||
data = payload.get("data", {})
|
||||
context_policy = data.get("context_policy", {})
|
||||
assert context_policy.get("project") == "local/ctx-app"
|
||||
assert context_policy.get("view") == "strategize"
|
||||
assert context_policy.get("include_resources") == ["repo"]
|
||||
assert context_policy.get("exclude_paths") == ["**/node_modules/**"]
|
||||
|
||||
limits = data.get("limits", {})
|
||||
assert limits.get("hot_max_tokens") == 12_000
|
||||
assert limits.get("warm_max_decisions") == 50
|
||||
assert limits.get("cold_max_decisions") == 200
|
||||
assert limits.get("query_limit") == 20
|
||||
assert limits.get("max_file_size") == "1 MB"
|
||||
assert limits.get("max_total_size") == "50 MB"
|
||||
|
||||
summarization = data.get("summarization", {})
|
||||
assert summarization.get("enabled") is True
|
||||
assert summarization.get("max_tokens") == 800
|
||||
|
||||
other_views = data.get("other_views", {})
|
||||
assert other_views.get("default") == "(unset)"
|
||||
assert other_views.get("execute") == "(default)"
|
||||
assert other_views.get("apply") == "(default)"
|
||||
|
||||
|
||||
@then("the context set rich output should include spec panels")
|
||||
def step_ctx_set_rich_spec(context: Any) -> None:
|
||||
output = context.ctx_output
|
||||
assert "Context Policy" in output
|
||||
assert "Limits" in output
|
||||
assert "Summarization" in output
|
||||
assert "Other Views" in output
|
||||
assert "Project: local/ctx-app" in output
|
||||
assert "View: strategize" in output
|
||||
assert "Include: repo" in output
|
||||
assert "Exclude: **/node_modules/**" in output
|
||||
assert "Hot Tokens: 12000 (soft cap)" in output
|
||||
assert "Warm Decisions: 50" in output
|
||||
assert "Cold Decisions: 200" in output
|
||||
assert "Query Limit: 20" in output
|
||||
assert "Max File Size: 1 MB" in output
|
||||
assert "Max Total Size: 50 MB" in output
|
||||
assert "Enabled: yes" in output
|
||||
assert "Max Tokens: 800" in output
|
||||
assert "default: (unset)" in output
|
||||
assert "execute: (default)" in output
|
||||
assert "apply: (default)" in output
|
||||
assert "\u2713 Context policy updated" in output
|
||||
|
||||
|
||||
def _read_stored_policy(context: Any) -> Any:
|
||||
"""Read the stored policy from the test DB."""
|
||||
from cleveragents.cli.commands.project_context import (
|
||||
_read_policy,
|
||||
)
|
||||
|
||||
return _read_policy(context.ctx_session_factory, "local/ctx-app")
|
||||
|
||||
|
||||
@then('the stored policy default view should include resource "{res}"')
|
||||
def step_policy_include_res(context: Any, res: str) -> None:
|
||||
policy = _read_stored_policy(context)
|
||||
assert res in policy.default_view.include_resources, (
|
||||
f"{res} not in {policy.default_view.include_resources}"
|
||||
)
|
||||
|
||||
|
||||
@then('the stored policy default view should exclude resource "{res}"')
|
||||
def step_policy_exclude_res(context: Any, res: str) -> None:
|
||||
policy = _read_stored_policy(context)
|
||||
assert res in policy.default_view.exclude_resources, (
|
||||
f"{res} not in {policy.default_view.exclude_resources}"
|
||||
)
|
||||
|
||||
|
||||
@then('the stored policy default view should include path "{path}"')
|
||||
def step_policy_include_path(context: Any, path: str) -> None:
|
||||
policy = _read_stored_policy(context)
|
||||
assert path in policy.default_view.include_paths, (
|
||||
f"{path} not in {policy.default_view.include_paths}"
|
||||
)
|
||||
|
||||
|
||||
@then('the stored policy default view should exclude path "{path}"')
|
||||
def step_policy_exclude_path(context: Any, path: str) -> None:
|
||||
policy = _read_stored_policy(context)
|
||||
assert path in policy.default_view.exclude_paths, (
|
||||
f"{path} not in {policy.default_view.exclude_paths}"
|
||||
)
|
||||
|
||||
|
||||
@then("the stored policy default view max file size should be {size:d}")
|
||||
def step_policy_max_file_size(context: Any, size: int) -> None:
|
||||
policy = _read_stored_policy(context)
|
||||
assert policy.default_view.max_file_size == size, (
|
||||
f"Expected {size}, got {policy.default_view.max_file_size}"
|
||||
)
|
||||
|
||||
|
||||
@then("the stored policy default view max total size should be {size:d}")
|
||||
def step_policy_max_total_size(context: Any, size: int) -> None:
|
||||
policy = _read_stored_policy(context)
|
||||
assert policy.default_view.max_total_size == size, (
|
||||
f"Expected {size}, got {policy.default_view.max_total_size}"
|
||||
)
|
||||
|
||||
|
||||
@then('the stored policy strategize view should include resource "{res}"')
|
||||
def step_policy_strat_include_res(context: Any, res: str) -> None:
|
||||
policy = _read_stored_policy(context)
|
||||
assert policy.strategize_view is not None, "strategize_view is None"
|
||||
assert res in policy.strategize_view.include_resources
|
||||
|
||||
|
||||
@then("the stored policy strategize view should be None")
|
||||
def step_policy_strat_none(context: Any) -> None:
|
||||
policy = _read_stored_policy(context)
|
||||
assert policy.strategize_view is None, (
|
||||
f"Expected None, got {policy.strategize_view}"
|
||||
)
|
||||
@@ -779,20 +779,45 @@ def context_set(
|
||||
},
|
||||
)
|
||||
|
||||
data = _policy_to_dict(policy)
|
||||
data["acms_config"] = acms
|
||||
if output_format.lower() == OutputFormat.RICH:
|
||||
console.print(
|
||||
Panel(
|
||||
f"[green]✓[/green] Context policy "
|
||||
f"'{view}' view updated for "
|
||||
f"project '{project}'.",
|
||||
title="Context Policy Updated",
|
||||
expand=False,
|
||||
)
|
||||
)
|
||||
from cleveragents.cli.rendering.project_context_set import (
|
||||
build_context_set_payload,
|
||||
render_context_set_plain,
|
||||
render_context_set_rich,
|
||||
)
|
||||
|
||||
payload = build_context_set_payload(
|
||||
project=project,
|
||||
view=view,
|
||||
policy=policy,
|
||||
acms=acms,
|
||||
default_limits={
|
||||
"hot_max_tokens": _DEFAULT_HOT_MAX_TOKENS,
|
||||
"warm_max_decisions": _DEFAULT_WARM_MAX_DECISIONS,
|
||||
"cold_max_decisions": _DEFAULT_COLD_MAX_DECISIONS,
|
||||
"query_limit": None,
|
||||
},
|
||||
)
|
||||
|
||||
success_message = "Context policy updated"
|
||||
fmt_lower = output_format.lower()
|
||||
|
||||
if fmt_lower == OutputFormat.RICH.value:
|
||||
for panel in render_context_set_rich(payload):
|
||||
console.print(panel)
|
||||
console.print(f"[green]\u2713[/green] {success_message}")
|
||||
elif fmt_lower == OutputFormat.PLAIN.value:
|
||||
console.print(render_context_set_plain(payload, success_message))
|
||||
else:
|
||||
console.print(format_output(data, output_format))
|
||||
rendered = format_output(
|
||||
payload,
|
||||
output_format,
|
||||
command="project context set",
|
||||
status="ok",
|
||||
exit_code=0,
|
||||
messages=[{"level": "ok", "text": success_message}],
|
||||
)
|
||||
if rendered:
|
||||
console.print(rendered)
|
||||
|
||||
|
||||
@app.command(name="show")
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
"""Rendering helpers for CLI commands."""
|
||||
|
||||
from .project_context_set import (
|
||||
|
|
||||
build_context_set_payload,
|
||||
render_context_set_plain,
|
||||
render_context_set_rich,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"build_context_set_payload",
|
||||
"render_context_set_plain",
|
||||
"render_context_set_rich",
|
||||
]
|
||||
@@ -0,0 +1,213 @@
|
||||
"""Rendering helpers for ``agents project context set`` output."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from rich.panel import Panel
|
||||
|
||||
from cleveragents.domain.models.core.context_policy import (
|
||||
ContextView,
|
||||
ProjectContextPolicy,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"build_context_set_payload",
|
||||
"render_context_set_plain",
|
||||
"render_context_set_rich",
|
||||
]
|
||||
|
||||
|
||||
def _format_size(value: int | None) -> str:
|
||||
"""Render byte sizes using binary units with whole numbers."""
|
||||
|
||||
if value is None:
|
||||
return "(no limit)"
|
||||
|
||||
units = [
|
||||
("TB", 1024**4),
|
||||
("GB", 1024**3),
|
||||
("MB", 1024**2),
|
||||
("KB", 1024),
|
||||
]
|
||||
for unit, factor in units:
|
||||
if value % factor == 0:
|
||||
return f"{value // factor} {unit}"
|
||||
return f"{value} bytes"
|
||||
|
||||
|
||||
def _format_inline_list(values: list[str], empty_placeholder: str) -> str:
|
||||
"""Join a list into a comma-separated string with a placeholder for empty."""
|
||||
|
||||
return ", ".join(values) if values else empty_placeholder
|
||||
|
||||
|
||||
def _is_view_default(view: ContextView) -> bool:
|
||||
"""Determine whether a view matches the default configuration."""
|
||||
|
||||
default_view = ContextView()
|
||||
return view.model_dump(mode="json") == default_view.model_dump(mode="json")
|
||||
|
||||
|
||||
def _view_status(policy: ProjectContextPolicy, phase: str) -> str:
|
||||
"""Summarise configuration state for a phase view."""
|
||||
|
||||
if phase == "default":
|
||||
return "(unset)" if _is_view_default(policy.default_view) else "configured"
|
||||
|
||||
view_obj = getattr(policy, f"{phase}_view")
|
||||
return "(default)" if view_obj is None else "configured"
|
||||
|
||||
|
||||
def build_context_set_payload(
|
||||
project: str,
|
||||
view: str,
|
||||
policy: ProjectContextPolicy,
|
||||
acms: dict[str, Any],
|
||||
*,
|
||||
default_limits: dict[str, int | None],
|
||||
) -> dict[str, Any]:
|
||||
"""Construct the structured payload for the context-set command."""
|
||||
|
||||
resolved_view = policy.resolve_view(view)
|
||||
context_policy = {
|
||||
"project": project,
|
||||
"view": view,
|
||||
"include_resources": resolved_view.include_resources,
|
||||
"exclude_resources": resolved_view.exclude_resources,
|
||||
"include_paths": resolved_view.include_paths,
|
||||
"exclude_paths": resolved_view.exclude_paths,
|
||||
}
|
||||
|
||||
limits = {
|
||||
"hot_max_tokens": acms.get(
|
||||
"hot_max_tokens", default_limits.get("hot_max_tokens")
|
||||
),
|
||||
"warm_max_decisions": acms.get(
|
||||
"warm_max_decisions", default_limits.get("warm_max_decisions")
|
||||
),
|
||||
"cold_max_decisions": acms.get(
|
||||
"cold_max_decisions", default_limits.get("cold_max_decisions")
|
||||
),
|
||||
"query_limit": acms.get("query_limit", default_limits.get("query_limit")),
|
||||
"max_file_size": _format_size(resolved_view.max_file_size),
|
||||
"max_total_size": _format_size(resolved_view.max_total_size),
|
||||
}
|
||||
|
||||
summarization = {
|
||||
"enabled": bool(acms.get("summarize", True)),
|
||||
"max_tokens": acms.get("summary_max_tokens"),
|
||||
}
|
||||
|
||||
phase_order = ["default", "strategize", "execute", "apply"]
|
||||
other_views = {
|
||||
phase: _view_status(policy, phase) for phase in phase_order if phase != view
|
||||
}
|
||||
|
||||
return {
|
||||
"context_policy": context_policy,
|
||||
"limits": limits,
|
||||
"summarization": summarization,
|
||||
"other_views": other_views,
|
||||
}
|
||||
|
||||
|
||||
def render_context_set_plain(payload: dict[str, Any], message: str) -> str:
|
||||
"""Render the spec-aligned plain text output for context-set."""
|
||||
|
||||
context_policy = payload["context_policy"]
|
||||
limits = payload["limits"]
|
||||
summarization = payload["summarization"]
|
||||
other_views = payload["other_views"]
|
||||
|
||||
sections: list[str] = []
|
||||
|
||||
cp_lines = [
|
||||
"Context Policy",
|
||||
f" Project: {context_policy['project']}",
|
||||
f" View: {context_policy['view']}",
|
||||
" Include: "
|
||||
f"{_format_inline_list(context_policy['include_resources'], '(all)')}",
|
||||
f" Exclude: {_format_inline_list(context_policy['exclude_paths'], '(none)')}",
|
||||
]
|
||||
sections.append("\n".join(cp_lines))
|
||||
|
||||
limits_lines = [
|
||||
"Limits",
|
||||
f" Hot Tokens: {limits['hot_max_tokens']} (soft cap)",
|
||||
f" Warm Decisions: {limits['warm_max_decisions']}",
|
||||
f" Cold Decisions: {limits['cold_max_decisions']}",
|
||||
]
|
||||
query_limit = limits["query_limit"]
|
||||
limits_lines.append(
|
||||
f" Query Limit: {query_limit if query_limit is not None else '(default)'}"
|
||||
)
|
||||
limits_lines.append(f" Max File Size: {limits['max_file_size']}")
|
||||
limits_lines.append(f" Max Total Size: {limits['max_total_size']}")
|
||||
sections.append("\n".join(limits_lines))
|
||||
|
||||
sum_max = summarization["max_tokens"]
|
||||
sum_max_str = sum_max if sum_max is not None else "(default)"
|
||||
summarization_lines = [
|
||||
"Summarization",
|
||||
f" Enabled: {'yes' if summarization['enabled'] else 'no'}",
|
||||
f" Max Tokens: {sum_max_str}",
|
||||
|
HAL9001
commented
BLOCKING: Five lines in this file exceed ruff's 88-character limit (E501), causing Lines over 88 chars:
Fix by extracting the long ternaries into local variables: Run **BLOCKING**: Five lines in this file exceed ruff's 88-character limit (E501), causing `CI / lint` to fail. `src/` files are not exempt from E501 -- only `features/steps/*.py` has that exemption.
Lines over 88 chars:
- Line 154 (101 chars): summarization max_tokens ternary
- Line 184 (90 chars): Rich markup hot_max_tokens
- Line 188 (89 chars): query_limit ternary
- Line 194 (91 chars): Rich markup enabled ternary
- Line 196 (101 chars): second max_tokens ternary
Fix by extracting the long ternaries into local variables:
```python
max_tok_str = (
str(summarization["max_tokens"])
if summarization["max_tokens"] is not None
else "(default)"
)
```
Run `nox -s lint` locally to confirm zero violations.
|
||||
]
|
||||
sections.append("\n".join(summarization_lines))
|
||||
|
||||
other_view_lines = ["Other Views"]
|
||||
for phase, status in other_views.items():
|
||||
other_view_lines.append(f" {phase}: {status}")
|
||||
sections.append("\n".join(other_view_lines))
|
||||
|
||||
return "\n\n".join(sections) + f"\n\n[OK] {message}"
|
||||
|
||||
|
||||
def render_context_set_rich(payload: dict[str, Any]) -> list[Panel]:
|
||||
"""Build Rich panels matching the specification."""
|
||||
|
||||
context_policy = payload["context_policy"]
|
||||
limits = payload["limits"]
|
||||
summarization = payload["summarization"]
|
||||
other_views = payload["other_views"]
|
||||
|
||||
cp_lines = [
|
||||
f"[cyan bold]Project:[/cyan bold] {context_policy['project']}",
|
||||
f"[blue bold]View:[/blue bold] {context_policy['view']}",
|
||||
"[green bold]Include:[/green bold] "
|
||||
f"{_format_inline_list(context_policy['include_resources'], '(all)')}",
|
||||
"[yellow bold]Exclude:[/yellow bold] "
|
||||
f"{_format_inline_list(context_policy['exclude_paths'], '(none)')}",
|
||||
]
|
||||
|
||||
hot_tokens = limits["hot_max_tokens"]
|
||||
qlimit = limits["query_limit"]
|
||||
qlimit_str = qlimit if qlimit is not None else "(default)"
|
||||
limits_lines = [
|
||||
f"[magenta bold]Hot Tokens:[/magenta bold] {hot_tokens} (soft cap)",
|
||||
f"[magenta bold]Warm Decisions:[/magenta bold] {limits['warm_max_decisions']}",
|
||||
f"[magenta bold]Cold Decisions:[/magenta bold] {limits['cold_max_decisions']}",
|
||||
f"[blue bold]Query Limit:[/blue bold] {qlimit_str}",
|
||||
f"[blue bold]Max File Size:[/blue bold] {limits['max_file_size']}",
|
||||
f"[blue bold]Max Total Size:[/blue bold] {limits['max_total_size']}",
|
||||
]
|
||||
|
||||
sum_enabled = "yes" if summarization["enabled"] else "no"
|
||||
sum_max = summarization["max_tokens"]
|
||||
sum_max_str = sum_max if sum_max is not None else "(default)"
|
||||
summarization_lines = [
|
||||
f"[green bold]Enabled:[/green bold] {sum_enabled}",
|
||||
f"[blue bold]Max Tokens:[/blue bold] {sum_max_str}",
|
||||
]
|
||||
|
||||
other_view_lines = [
|
||||
f"[blue bold]{phase}:[/blue bold] {status}"
|
||||
for phase, status in other_views.items()
|
||||
]
|
||||
|
||||
return [
|
||||
Panel("\n".join(cp_lines), title="Context Policy", expand=False),
|
||||
Panel("\n".join(limits_lines), title="Limits", expand=False),
|
||||
Panel("\n".join(summarization_lines), title="Summarization", expand=False),
|
||||
Panel("\n".join(other_view_lines), title="Other Views", expand=False),
|
||||
]
|
||||
Reference in New Issue
Block a user
BLOCKING: The
# noqa: F401comment on this line triggers aRUF100(unused noqa suppression) because the imported names are already exported via__all__— ruff does not flag re-exports that appear in__all__. Remove the suppression comment: