c47e6445d0
CI / quality (pull_request) Successful in 19s
CI / lint (pull_request) Successful in 22s
CI / benchmark-publish (pull_request) Has been skipped
CI / security (pull_request) Successful in 33s
CI / build (pull_request) Successful in 26s
CI / typecheck (pull_request) Successful in 1m0s
CI / integration_tests (pull_request) Successful in 4m56s
CI / unit_tests (pull_request) Successful in 15m11s
CI / docker (pull_request) Successful in 1m33s
CI / benchmark-regression (pull_request) Successful in 20m55s
CI / coverage (pull_request) Successful in 33m42s
CI / lint (push) Successful in 13s
CI / build (push) Successful in 15s
CI / quality (push) Successful in 17s
CI / security (push) Successful in 28s
CI / typecheck (push) Successful in 32s
CI / benchmark-regression (push) Has been skipped
CI / integration_tests (push) Successful in 3m3s
CI / benchmark-publish (push) Successful in 10m8s
CI / unit_tests (push) Successful in 12m42s
CI / docker (push) Successful in 39s
CI / coverage (push) Has been cancelled
Add ActorCompiler module that translates GRAPH-type ActorConfigSchema definitions into LangGraph NodeConfig/Edge structures with LSP binding metadata. Includes subgraph resolution with cross-actor cycle detection, entry/exit validation, and CompilationMetadata for diagnostics. New files: - src/cleveragents/actor/compiler.py: Core compiler with compile_actor() - features/actor_compiler.feature: 13 Behave scenarios - features/steps/actor_compiler_steps.py: Step definitions - robot/actor_compiler.robot: 4 Robot smoke tests - benchmarks/actor_compiler_bench.py: ASV performance benchmarks - docs/reference/actor_compiler.md: Compilation pipeline reference Modified: - src/cleveragents/actor/__init__.py: Export compiler types - vulture_whitelist.py: Whitelist new public API ISSUES CLOSED: #158
574 lines
20 KiB
Python
574 lines
20 KiB
Python
"""Step definitions for Agent Skills discovery tests."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import shutil
|
|
import tempfile
|
|
from pathlib import Path
|
|
from typing import Any
|
|
from unittest.mock import MagicMock
|
|
|
|
from behave import given, then, when
|
|
from behave.runner import Context
|
|
|
|
from cleveragents.application.services.config_service import ConfigService
|
|
from cleveragents.skills.discovery import (
|
|
DiscoveredAgentSkill,
|
|
build_tool_spec,
|
|
discover_agent_skills,
|
|
parse_agent_skills_paths,
|
|
register_discovered_skills,
|
|
scan_directory,
|
|
)
|
|
from cleveragents.tool.registry import ToolRegistry
|
|
from cleveragents.tool.runtime import ToolSpec
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def _create_skill_md(folder: Path, name: str, description: str) -> None:
|
|
"""Create a SKILL.md with YAML front-matter in *folder*."""
|
|
folder.mkdir(parents=True, exist_ok=True)
|
|
content = (
|
|
"---\n"
|
|
f"name: {name}\n"
|
|
f"description: {description}\n"
|
|
"---\n"
|
|
f"\n# {name}\n\n"
|
|
f"{description}\n"
|
|
)
|
|
(folder / "SKILL.md").write_text(content, encoding="utf-8")
|
|
|
|
|
|
def _noop_handler(**_kwargs: Any) -> dict[str, Any]:
|
|
return {"status": "test"}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Background
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given("the agent skills discovery module is available")
|
|
def step_discovery_module_available(context: Context) -> None:
|
|
"""Verify imports are accessible."""
|
|
assert parse_agent_skills_paths is not None
|
|
assert scan_directory is not None
|
|
assert discover_agent_skills is not None
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Config key
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when('I check the config registry for "{key}"')
|
|
def step_check_config_registry(context: Context, key: str) -> None:
|
|
context.config_entry = ConfigService.get_entry(key)
|
|
|
|
|
|
@then("the config entry should exist")
|
|
def step_config_entry_exists(context: Context) -> None:
|
|
assert context.config_entry is not None, "Config entry not found"
|
|
|
|
|
|
@then('the config entry type should be "{type_name}"')
|
|
def step_config_entry_type(context: Context, type_name: str) -> None:
|
|
entry = context.config_entry
|
|
assert entry is not None
|
|
assert entry.python_type.__name__ == type_name
|
|
|
|
|
|
@then('the config entry env var should be "{env_var}"')
|
|
def step_config_entry_env_var(context: Context, env_var: str) -> None:
|
|
entry = context.config_entry
|
|
assert entry is not None
|
|
assert entry.env_var == env_var
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Path parsing
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when('I parse agent skills paths "{raw_paths}"')
|
|
def step_parse_paths(context: Context, raw_paths: str) -> None:
|
|
context.parsed_paths = parse_agent_skills_paths(raw_paths)
|
|
|
|
|
|
@when("I parse an empty agent skills path string")
|
|
def step_parse_empty_paths(context: Context) -> None:
|
|
context.parsed_paths = parse_agent_skills_paths("")
|
|
|
|
|
|
@then("the result should contain {count:d} path")
|
|
def step_path_count_singular(context: Context, count: int) -> None:
|
|
assert len(context.parsed_paths) == count
|
|
|
|
|
|
@then("the result should contain {count:d} paths")
|
|
def step_path_count(context: Context, count: int) -> None:
|
|
assert len(context.parsed_paths) == count
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Discovery from directories
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given("a temporary directory with agent skill folders")
|
|
def step_temp_dir_with_skills(context: Context) -> None:
|
|
tmpdir = Path(tempfile.mkdtemp())
|
|
context.tmpdir = tmpdir
|
|
context.add_cleanup(lambda: shutil.rmtree(str(tmpdir), ignore_errors=True))
|
|
|
|
assert context.table is not None, "Expected table data"
|
|
for row in context.table:
|
|
folder_name = row["folder_name"]
|
|
skill_name = row["skill_name"]
|
|
description = row["description"]
|
|
if skill_name == "_none_":
|
|
# Write SKILL.md without a name field
|
|
folder = tmpdir / folder_name
|
|
folder.mkdir(parents=True, exist_ok=True)
|
|
content = f"---\ndescription: {description}\n---\n\n# {folder_name}\n"
|
|
(folder / "SKILL.md").write_text(content, encoding="utf-8")
|
|
else:
|
|
_create_skill_md(tmpdir / folder_name, skill_name, description)
|
|
|
|
|
|
@given("a temporary directory with some folders missing SKILL.md")
|
|
def step_temp_dir_no_skill_md(context: Context) -> None:
|
|
tmpdir = Path(tempfile.mkdtemp())
|
|
context.tmpdir = tmpdir
|
|
context.add_cleanup(lambda: shutil.rmtree(str(tmpdir), ignore_errors=True))
|
|
# Create folder without SKILL.md
|
|
(tmpdir / "no-skill-folder").mkdir()
|
|
# Create a plain file (not a folder)
|
|
(tmpdir / "plain-file.txt").write_text("not a skill", encoding="utf-8")
|
|
|
|
|
|
@given("a temporary directory with a SKILL.md that has no front-matter")
|
|
def step_temp_dir_no_front_matter(context: Context) -> None:
|
|
tmpdir = Path(tempfile.mkdtemp())
|
|
context.tmpdir = tmpdir
|
|
context.add_cleanup(lambda: shutil.rmtree(str(tmpdir), ignore_errors=True))
|
|
folder = tmpdir / "bad-skill"
|
|
folder.mkdir()
|
|
(folder / "SKILL.md").write_text("# No front-matter here\n", encoding="utf-8")
|
|
|
|
|
|
@when("I scan the directory for agent skills")
|
|
def step_scan_directory(context: Context) -> None:
|
|
context.discovered = scan_directory(context.tmpdir)
|
|
|
|
|
|
@then("I should discover {count:d} agent skill")
|
|
def step_discover_count_singular(context: Context, count: int) -> None:
|
|
assert len(context.discovered) == count, (
|
|
f"Expected {count}, got {len(context.discovered)}"
|
|
)
|
|
|
|
|
|
@then("I should discover {count:d} agent skills")
|
|
def step_discover_count(context: Context, count: int) -> None:
|
|
assert len(context.discovered) == count, (
|
|
f"Expected {count}, got {len(context.discovered)}"
|
|
)
|
|
|
|
|
|
@then('the discovered skill "{name}" should have description "{description}"')
|
|
def step_discovered_skill_description(
|
|
context: Context, name: str, description: str
|
|
) -> None:
|
|
skill = next((s for s in context.discovered if s.name == name), None)
|
|
assert skill is not None, f"Skill '{name}' not found"
|
|
assert skill.description == description
|
|
|
|
|
|
@then("the discovered skill should use folder name as name")
|
|
def step_discovered_skill_folder_name(context: Context) -> None:
|
|
assert len(context.discovered) == 1
|
|
skill = context.discovered[0]
|
|
# When no name in front-matter, it falls back to folder name
|
|
assert skill.name == "fallback-tool"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Non-existent directory
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when("I run discovery on a non-existent directory")
|
|
def step_discover_nonexistent(context: Context) -> None:
|
|
context.discovery_result = discover_agent_skills(
|
|
[Path("/nonexistent/path/that/does/not/exist")]
|
|
)
|
|
|
|
|
|
@then("the discovery result should have {count:d} discovered skills")
|
|
def step_discovery_result_count(context: Context, count: int) -> None:
|
|
assert len(context.discovery_result.discovered) == count
|
|
|
|
|
|
@then('the discovery result should have errors mentioning "{text}"')
|
|
def step_discovery_result_errors(context: Context, text: str) -> None:
|
|
errors_joined = " ".join(context.discovery_result.errors)
|
|
assert text in errors_joined, f"Expected '{text}' in errors, got: {errors_joined}"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# ToolSpec building
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given('a discovered agent skill named "{name}" at "{path}"')
|
|
def step_given_discovered_skill(context: Context, name: str, path: str) -> None:
|
|
context.discovered_skill = DiscoveredAgentSkill(
|
|
name=name,
|
|
description=f"Test skill {name}",
|
|
path=path,
|
|
)
|
|
|
|
|
|
@when("I build a ToolSpec from the discovered skill")
|
|
def step_build_tool_spec(context: Context) -> None:
|
|
context.tool_spec = build_tool_spec(context.discovered_skill)
|
|
|
|
|
|
@then('the ToolSpec name should be "{expected}"')
|
|
def step_tool_spec_name(context: Context, expected: str) -> None:
|
|
assert context.tool_spec.name == expected
|
|
|
|
|
|
@then('the ToolSpec source should be "{expected}"')
|
|
def step_tool_spec_source(context: Context, expected: str) -> None:
|
|
assert context.tool_spec.source == expected
|
|
|
|
|
|
@then('the ToolSpec source_metadata should contain path "{expected}"')
|
|
def step_tool_spec_source_metadata_path(context: Context, expected: str) -> None:
|
|
assert context.tool_spec.source_metadata.get("path") == expected
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Registration
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given("a ToolRegistry with no existing tools")
|
|
def step_empty_tool_registry(context: Context) -> None:
|
|
context.tool_registry = ToolRegistry()
|
|
|
|
|
|
@given("a list of {count:d} discovered agent skills")
|
|
def step_discovered_skills_list(context: Context, count: int) -> None:
|
|
context.discovered_skills = [
|
|
DiscoveredAgentSkill(
|
|
name=f"tool-{i}",
|
|
description=f"Test tool {i}",
|
|
path=f"/tmp/skills/tool-{i}",
|
|
)
|
|
for i in range(count)
|
|
]
|
|
|
|
|
|
@given('a ToolRegistry with an existing tool "{name}"')
|
|
def step_registry_with_tool(context: Context, name: str) -> None:
|
|
context.tool_registry = ToolRegistry()
|
|
existing_spec = ToolSpec(
|
|
name=name,
|
|
description="Existing tool",
|
|
handler=_noop_handler,
|
|
)
|
|
context.tool_registry.register(existing_spec)
|
|
|
|
|
|
@given('a discovered agent skill named "{name}"')
|
|
def step_single_discovered_skill(context: Context, name: str) -> None:
|
|
context.discovered_skills = [
|
|
DiscoveredAgentSkill(
|
|
name=name,
|
|
description=f"Discovered {name}",
|
|
path=f"/tmp/skills/{name}",
|
|
)
|
|
]
|
|
|
|
|
|
@when('I register discovered skills with "{strategy}" conflict strategy')
|
|
def step_register_discovered(context: Context, strategy: str) -> None:
|
|
try:
|
|
registered, conflicts = register_discovered_skills(
|
|
context.discovered_skills,
|
|
context.tool_registry,
|
|
on_conflict=strategy,
|
|
)
|
|
context.registered_tools = registered
|
|
context.registration_conflicts = conflicts
|
|
context.registration_error = None
|
|
except ValueError as exc:
|
|
context.registration_error = exc
|
|
context.registered_tools = []
|
|
context.registration_conflicts = []
|
|
|
|
|
|
@then("{count:d} tools should be registered in the ToolRegistry")
|
|
def step_registered_tool_count(context: Context, count: int) -> None:
|
|
assert len(context.registered_tools) == count, (
|
|
f"Expected {count}, got {len(context.registered_tools)}"
|
|
)
|
|
|
|
|
|
@then("{count:d} tool should be registered in the ToolRegistry")
|
|
def step_registered_tool_count_singular(context: Context, count: int) -> None:
|
|
assert len(context.registered_tools) == count, (
|
|
f"Expected {count}, got {len(context.registered_tools)}"
|
|
)
|
|
|
|
|
|
@then("{count:d} conflicts should be reported")
|
|
def step_conflict_count(context: Context, count: int) -> None:
|
|
assert len(context.registration_conflicts) == count
|
|
|
|
|
|
@then('{count:d} conflict should be reported with tool name "{name}"')
|
|
def step_conflict_with_name(context: Context, count: int, name: str) -> None:
|
|
assert len(context.registration_conflicts) == count
|
|
if count > 0:
|
|
assert context.registration_conflicts[0].tool_name == name
|
|
|
|
|
|
@then('a discovery ValueError should be raised mentioning "{text}"')
|
|
def step_value_error_raised_discovery(context: Context, text: str) -> None:
|
|
assert context.registration_error is not None, "Expected ValueError not raised"
|
|
assert text in str(context.registration_error), (
|
|
f"Expected '{text}' in error message, got: {context.registration_error}"
|
|
)
|
|
|
|
|
|
@then('the tool "{name}" should have source "{expected_source}"')
|
|
def step_tool_has_source(context: Context, name: str, expected_source: str) -> None:
|
|
spec = context.tool_registry.get(name)
|
|
assert spec is not None, f"Tool '{name}' not found in registry"
|
|
assert spec.source == expected_source
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Refresh
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given("a SkillRegistryService with a ToolRegistry")
|
|
def step_service_with_registry(context: Context) -> None:
|
|
from cleveragents.application.services.skill_registry_service import (
|
|
SkillRegistryService,
|
|
)
|
|
|
|
context.tool_registry = ToolRegistry()
|
|
# Create a mock skill_repo since we only need discovery features
|
|
mock_repo = MagicMock()
|
|
context.skill_registry_service = SkillRegistryService(
|
|
skill_repo=mock_repo,
|
|
tool_registry=context.tool_registry,
|
|
)
|
|
|
|
|
|
@given("agent skills paths pointing to a directory with {count:d} skills")
|
|
def step_paths_with_skills(context: Context, count: int) -> None:
|
|
tmpdir = Path(tempfile.mkdtemp())
|
|
context.tmpdir = tmpdir
|
|
context.add_cleanup(lambda: shutil.rmtree(str(tmpdir), ignore_errors=True))
|
|
for i in range(count):
|
|
_create_skill_md(tmpdir / f"skill-{i}", f"skill-{i}", f"Skill {i}")
|
|
context.agent_skills_raw_paths = str(tmpdir)
|
|
|
|
|
|
@when("I call discover_and_register")
|
|
def step_call_discover_and_register(context: Context) -> None:
|
|
context.discovery_result = context.skill_registry_service.discover_and_register(
|
|
context.agent_skills_raw_paths
|
|
)
|
|
|
|
|
|
@then("{count:d} agent skills should be registered")
|
|
def step_agent_skills_registered(context: Context, count: int) -> None:
|
|
registered = context.skill_registry_service.registered_agent_tools
|
|
assert len(registered) == count, f"Expected {count}, got {len(registered)}"
|
|
|
|
|
|
@when("the directory now has {count:d} skills and I call refresh_agent_skills")
|
|
def step_add_more_and_refresh(context: Context, count: int) -> None:
|
|
# Add more skills to the directory
|
|
existing = list(context.tmpdir.iterdir())
|
|
existing_count = len([d for d in existing if d.is_dir()])
|
|
for i in range(existing_count, count):
|
|
_create_skill_md(context.tmpdir / f"skill-{i}", f"skill-{i}", f"Skill {i}")
|
|
context.discovery_result = context.skill_registry_service.refresh_agent_skills(
|
|
context.agent_skills_raw_paths
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Edge cases
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when("I register an empty discovered skills list")
|
|
def step_register_empty_list(context: Context) -> None:
|
|
registered, conflicts = register_discovered_skills(
|
|
[],
|
|
context.tool_registry,
|
|
)
|
|
context.registered_tools = registered
|
|
context.registration_conflicts = conflicts
|
|
|
|
|
|
@then(
|
|
"the registration should return {spec_count:d} specs and {conflict_count:d} conflicts"
|
|
)
|
|
def step_registration_returns(
|
|
context: Context, spec_count: int, conflict_count: int
|
|
) -> None:
|
|
assert len(context.registered_tools) == spec_count
|
|
assert len(context.registration_conflicts) == conflict_count
|
|
|
|
|
|
@when("I scan a non-directory path for agent skills")
|
|
def step_scan_non_dir(context: Context) -> None:
|
|
tmpfile = Path(tempfile.mktemp(suffix=".txt"))
|
|
tmpfile.write_text("not a directory", encoding="utf-8")
|
|
context.add_cleanup(lambda: tmpfile.unlink(missing_ok=True))
|
|
context.discovered = scan_directory(tmpfile)
|
|
|
|
|
|
@when("I run discovery on a path that is a file")
|
|
def step_discover_file_path(context: Context) -> None:
|
|
tmpfile = Path(tempfile.mktemp(suffix=".txt"))
|
|
tmpfile.write_text("not a directory", encoding="utf-8")
|
|
context.add_cleanup(lambda: tmpfile.unlink(missing_ok=True))
|
|
context.discovery_result = discover_agent_skills([tmpfile])
|
|
|
|
|
|
@given("a temporary directory with a SKILL.md that has invalid YAML")
|
|
def step_temp_dir_invalid_yaml(context: Context) -> None:
|
|
tmpdir = Path(tempfile.mkdtemp())
|
|
context.tmpdir = tmpdir
|
|
context.add_cleanup(lambda: shutil.rmtree(str(tmpdir), ignore_errors=True))
|
|
folder = tmpdir / "bad-yaml-skill"
|
|
folder.mkdir()
|
|
(folder / "SKILL.md").write_text(
|
|
"---\ninvalid: [yaml: {broken\n---\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
|
|
@given("a temporary directory with a SKILL.md that has empty front-matter")
|
|
def step_temp_dir_empty_fm(context: Context) -> None:
|
|
tmpdir = Path(tempfile.mkdtemp())
|
|
context.tmpdir = tmpdir
|
|
context.add_cleanup(lambda: shutil.rmtree(str(tmpdir), ignore_errors=True))
|
|
folder = tmpdir / "empty-fm-skill"
|
|
folder.mkdir()
|
|
(folder / "SKILL.md").write_text(
|
|
"---\n\n---\n\n# Empty FM\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
|
|
@given("a temporary directory with a SKILL.md that has non-dict YAML")
|
|
def step_temp_dir_non_dict_yaml(context: Context) -> None:
|
|
tmpdir = Path(tempfile.mkdtemp())
|
|
context.tmpdir = tmpdir
|
|
context.add_cleanup(lambda: shutil.rmtree(str(tmpdir), ignore_errors=True))
|
|
folder = tmpdir / "list-yaml-skill"
|
|
folder.mkdir()
|
|
(folder / "SKILL.md").write_text(
|
|
"---\n- item1\n- item2\n---\n\n# List YAML\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
|
|
@given("a temporary directory with a SKILL.md that has numeric description")
|
|
def step_temp_dir_numeric_desc(context: Context) -> None:
|
|
tmpdir = Path(tempfile.mkdtemp())
|
|
context.tmpdir = tmpdir
|
|
context.add_cleanup(lambda: shutil.rmtree(str(tmpdir), ignore_errors=True))
|
|
folder = tmpdir / "numeric-desc-skill"
|
|
folder.mkdir()
|
|
(folder / "SKILL.md").write_text(
|
|
"---\nname: numeric-skill\ndescription: 42\n---\n\n# Numeric\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
|
|
@given("a temporary directory with a SKILL.md that has empty description")
|
|
def step_temp_dir_empty_desc(context: Context) -> None:
|
|
tmpdir = Path(tempfile.mkdtemp())
|
|
context.tmpdir = tmpdir
|
|
context.add_cleanup(lambda: shutil.rmtree(str(tmpdir), ignore_errors=True))
|
|
folder = tmpdir / "empty-desc-skill"
|
|
folder.mkdir()
|
|
(folder / "SKILL.md").write_text(
|
|
'---\nname: empty-desc\ndescription: ""\n---\n\n# Empty Desc\n',
|
|
encoding="utf-8",
|
|
)
|
|
|
|
|
|
@then('the first discovered skill description should contain "{text}"')
|
|
def step_first_skill_desc_contains(context: Context, text: str) -> None:
|
|
assert len(context.discovered) > 0, "No discovered skills"
|
|
desc = context.discovered[0].description
|
|
assert text in desc, f"Expected '{text}' in description, got: {desc}"
|
|
|
|
|
|
@when("I call the noop handler")
|
|
def step_call_noop_handler(context: Context) -> None:
|
|
from cleveragents.skills.discovery import _noop_handler
|
|
|
|
context.noop_result = _noop_handler()
|
|
|
|
|
|
@then('the noop handler should return status "{expected}"')
|
|
def step_noop_result(context: Context, expected: str) -> None:
|
|
assert context.noop_result["status"] == expected
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Source metadata
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given('a registered skill "{name}" with an agent_skill source')
|
|
def step_registered_skill_with_agent_source(context: Context, name: str) -> None:
|
|
from cleveragents.application.services.skill_service import SkillService
|
|
from cleveragents.skills.schema import (
|
|
SkillAgentFolderSchema,
|
|
SkillConfigSchema,
|
|
)
|
|
|
|
context.skill_service = SkillService()
|
|
config = SkillConfigSchema(
|
|
name=name,
|
|
description="Test skill with agent_skill source",
|
|
agent_skill_folders=[
|
|
SkillAgentFolderSchema(path="/tmp/agent-skill-folder", name="test-agent")
|
|
],
|
|
)
|
|
context.skill_service.add_skill(config, config_path="/tmp/test.yaml")
|
|
context.skill_name = name
|
|
|
|
|
|
@when('I resolve tools for "{name}"')
|
|
def step_resolve_tools(context: Context, name: str) -> None:
|
|
skill, entries = context.skill_service.resolve_tools(name)
|
|
context.resolved_skill = skill
|
|
context.resolved_entries = entries
|
|
|
|
|
|
@then("the resolved tools should include agent_skill source entries")
|
|
def step_resolved_tools_agent_source(context: Context) -> None:
|
|
agent_entries = [e for e in context.resolved_entries if "agent_skill:" in e.name]
|
|
assert len(agent_entries) > 0, "Expected at least one agent_skill source entry"
|