Harden agent-friendly discovery and one-shot execution

This commit is contained in:
rob
2026-07-20 17:28:44 -03:00
parent 23723eaddb
commit 06a1606370
6 changed files with 377 additions and 33 deletions
+154
View File
@@ -1,5 +1,6 @@
"""Tests for CLI commands."""
import json
import tempfile
from pathlib import Path
from types import SimpleNamespace
@@ -67,6 +68,159 @@ class TestListCommand:
assert 'test-tool' in captured.out
assert 'another-tool' in captured.out
def test_json_is_compact_catalog_unless_full_requested(
self, temp_tools_dir, capsys
):
from cmdforge.tool import save_tool
save_tool(Tool(
name="catalog-tool",
description="Catalog entry",
input_schema={"type": "string"},
output_schema={"type": "string"},
steps=[PromptStep(
prompt="Private implementation {input}",
provider="mock",
output_var="result",
plain_text=True,
)],
output="{result}",
))
with patch('sys.argv', ['cmdforge', 'list', '--json']):
assert main() == 0
compact = json.loads(capsys.readouterr().out)
assert compact[0]["name"] == "catalog-tool"
assert compact[0]["input_schema"] == {"type": "string"}
assert "steps" not in compact[0]
with patch('sys.argv', ['cmdforge', 'list', '--json', '--full']):
assert main() == 0
full = json.loads(capsys.readouterr().out)
assert full[0]["steps"][0]["prompt"] == "Private implementation {input}"
def test_filter_reports_filtered_count_and_empty_result(
self, temp_tools_dir, capsys
):
from cmdforge.tool import save_tool
save_tool(Tool(name="alpha", description="First"))
save_tool(Tool(name="beta", description="Second"))
with patch('sys.argv', ['cmdforge', 'list', '--filter', 'alpha']):
assert main() == 0
output = capsys.readouterr().out
assert "Available tools (1)" in output
assert "alpha" in output
assert "beta" not in output
with patch('sys.argv', ['cmdforge', 'list', '--filter', 'missing']):
assert main() == 0
assert "No tools match filter 'missing'" in capsys.readouterr().out
def test_json_limit_and_invalid_limit(self, temp_tools_dir, capsys):
from cmdforge.tool import save_tool
save_tool(Tool(name="alpha"))
save_tool(Tool(name="beta"))
with patch('sys.argv', ['cmdforge', 'list', '--json', '--limit', '1']):
assert main() == 0
assert len(json.loads(capsys.readouterr().out)) == 1
with patch('sys.argv', ['cmdforge', 'list', '--limit', '0']):
assert main() == 1
assert "at least 1" in capsys.readouterr().err
with patch('sys.argv', ['cmdforge', 'list', '--full']):
assert main() == 1
assert "--full requires --json" in capsys.readouterr().err
class TestRunOnceCommand:
def test_combines_instruction_with_piped_input(self, capsys):
result = SimpleNamespace(success=True, text="summary", error=None)
with (
patch('sys.argv', [
'cmdforge', 'run-once', 'Summarize:\n{input}',
'--provider', 'mock',
]),
patch('sys.stdin', StringIO('document body')),
patch('cmdforge.providers.call_provider', return_value=result) as call_provider,
):
assert main() == 0
assert call_provider.call_args.args[:2] == (
'mock', 'Summarize:\ndocument body'
)
assert capsys.readouterr().out == "summary\n"
def test_appends_piped_input_when_template_has_no_placeholder(self):
result = SimpleNamespace(success=True, text="ok", error=None)
with (
patch('sys.argv', [
'cmdforge', 'run-once', 'Summarize this', '--provider', 'mock',
]),
patch('sys.stdin', StringIO('document body')),
patch('cmdforge.providers.call_provider', return_value=result) as call_provider,
):
assert main() == 0
assert call_provider.call_args.args[1] == "Summarize this\n\ndocument body"
def test_uses_configured_default_provider(self):
result = SimpleNamespace(success=True, text="ok", error=None)
config = SimpleNamespace(default_provider="configured-provider")
stdin = MagicMock()
stdin.isatty.return_value = True
with (
patch('sys.argv', ['cmdforge', 'run-once', 'hello']),
patch('sys.stdin', stdin),
patch('cmdforge.config.load_config', return_value=config),
patch('cmdforge.providers.call_provider', return_value=result) as call_provider,
):
assert main() == 0
assert call_provider.call_args.args[0] == "configured-provider"
def test_selects_first_available_balanced_provider(self):
from cmdforge.cli.tool_commands import _resolve_one_shot_provider
from cmdforge.providers import Provider
config = SimpleNamespace(default_provider=None)
providers = [
Provider("opencode-pickle", "missing-opencode"),
Provider("codex", "codex exec -"),
]
with (
patch('cmdforge.config.load_config', return_value=config),
patch('cmdforge.providers.load_providers', return_value=providers),
patch(
'cmdforge.cli.tool_commands._provider_is_available',
side_effect=lambda provider: provider.name == "codex",
),
):
assert _resolve_one_shot_provider() == "codex"
@pytest.mark.parametrize(
"arguments,message",
[
(["--timeout", "0"], "--timeout"),
(["--max-tokens", "-1"], "--max-tokens"),
],
)
def test_rejects_invalid_limits_before_provider_call(
self, arguments, message, capsys
):
with (
patch('sys.argv', [
'cmdforge', 'run-once', 'hello', '--provider', 'mock', *arguments,
]),
patch('sys.stdin', StringIO()),
patch('cmdforge.providers.call_provider') as call_provider,
):
assert main() == 1
call_provider.assert_not_called()
assert message in capsys.readouterr().err
class TestCreateCommand:
"""Tests for 'cmdforge create' command."""