Skip to content
This repository was archived by the owner on Oct 28, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 15 additions & 10 deletions agent_starter_pack/cli/commands/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import logging
import pathlib
import shutil
import sys

import click
Expand Down Expand Up @@ -112,7 +113,7 @@ def display_agents_from_path(
console.print(table)


def list_remote_agents(remote_source: str, scan_from_root: bool = False) -> None:
def list_remote_agents(remote_source: str) -> None:
"""Lists agents from a remote source (Git URL)."""
spec = parse_agent_spec(remote_source)
if not spec:
Expand All @@ -121,26 +122,30 @@ def list_remote_agents(remote_source: str, scan_from_root: bool = False) -> None

console.print(f"\nFetching agents from [bold blue]{remote_source}[/]...")

temp_dir: pathlib.Path | None = None
try:
# fetch_remote_template clones the repo and returns a path to the
# specific template directory within the repo.
template_dir_path = fetch_remote_template(spec)

# fetch_remote_template always returns a tuple of (repo_path, template_path)
repo_path, template_path = template_dir_path
scan_path = repo_path if scan_from_root else template_path
# fetch_remote_template returns (template_dir, temp_dir): the directory
# inside the clone that matches the requested template path, plus the
# clone root, which the caller is responsible for cleaning up. When the
# spec has no sub-path (e.g. the adk-samples repo itself) template_dir is
# the clone root.
template_dir, temp_dir = fetch_remote_template(spec)

# Check if this is ADK samples to enable inference
is_adk_samples = (
spec.is_adk_samples if hasattr(spec, "is_adk_samples") else False
)

display_agents_from_path(
scan_path, remote_source, is_adk_samples=is_adk_samples
template_dir, remote_source, is_adk_samples=is_adk_samples
)

except (RuntimeError, FileNotFoundError) as e:
console.print(f"Error: {e}", style="bold red")
finally:
# The clone is only needed while the table is being built.
if temp_dir:
shutil.rmtree(temp_dir, ignore_errors=True)


@click.command("list")
Expand All @@ -167,7 +172,7 @@ def list_agents(adk: bool, source: str | None) -> None:
return

if adk:
list_remote_agents("https://github.com/google/adk-samples", scan_from_root=True)
list_remote_agents("https://github.com/google/adk-samples")
return

if source:
Expand Down
38 changes: 38 additions & 0 deletions tests/cli/commands/test_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import pathlib

from click.testing import CliRunner
from pytest_mock import MockerFixture

Expand All @@ -38,3 +40,39 @@ def test_list_agents_local(mocker: MockerFixture) -> None:
assert "Agent Two" in result.output
assert "Description two" in result.output
mock_get_agents.assert_called_once()


def test_list_remote_agents_scans_template_dir(
mocker: MockerFixture, tmp_path: pathlib.Path
) -> None:
"""A remote source has to be scanned from the template dir inside the clone.

fetch_remote_template returns (template_dir, temp_dir). Taking the second
element points the scan at the clone root, which has no python/agents/ in
it, so listing reports "No agents found" for a perfectly valid source.
"""
clone_root = tmp_path / "asp_remote_template_test"
repo_root = clone_root / "repo"
agent_dir = repo_root / "python" / "agents" / "data-science"
agent_dir.mkdir(parents=True)
(agent_dir / "agent.py").write_text("root_agent = None\n")
(agent_dir / "pyproject.toml").write_text(
'[project]\nname = "data-science"\ndescription = "Sample agent"\n'
)

mocker.patch(
"agent_starter_pack.cli.commands.list.fetch_remote_template",
return_value=(repo_root, clone_root),
)

runner = CliRunner()
result = runner.invoke(
list_agents, ["--source", "https://github.com/google/adk-samples"]
)

assert result.exit_code == 0
assert "data-science" in result.output
assert "No agents found" not in result.output

# the temporary clone is only needed while the table is rendered
assert not clone_root.exists()