mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-09-23 03:11:05 +00:00
feat: add platform tools to JSON crew wizard (#7384)
* feat: support platform tools in JSON crews * style: clarify platform integration labels * fix: avoid repeating tool picker title * fix(platform): surface JSON tool discovery errors
This commit is contained in:
@@ -16,6 +16,7 @@ from rich.text import Text
|
||||
from crewai_cli.constants import ENV_VARS
|
||||
from crewai_cli.git import initialize_if_git_available
|
||||
from crewai_cli.model_catalog import get_provider_models
|
||||
from crewai_cli.platform_tools_catalog import PLATFORM_TOOLS
|
||||
from crewai_cli.tui_picker import pick_many, pick_one
|
||||
from crewai_cli.utils import (
|
||||
enable_prompt_line_editing,
|
||||
@@ -103,6 +104,7 @@ _TEMPLATES_DIR = Path(__file__).parent / "templates" / "json_crew"
|
||||
# ── Common tools for picker ────────────────────────────────────
|
||||
|
||||
_TOOL_CATEGORIES: list[tuple[str, list[tuple[str, str]]]] = [
|
||||
("CrewAI Platform", PLATFORM_TOOLS),
|
||||
(
|
||||
"Search & Research",
|
||||
[
|
||||
@@ -304,6 +306,9 @@ def _show_interpolation_hint(kind: str) -> None:
|
||||
|
||||
|
||||
def _tool_label(name: str, description: str) -> str:
|
||||
if name.startswith("platform:"):
|
||||
app_name = description.removesuffix(" Integration").replace(" ", "")
|
||||
return f"{description:<48s} Platform: {app_name.replace(' ', '')}Integration"
|
||||
return f"{description:<48s} {name}"
|
||||
|
||||
|
||||
@@ -351,6 +356,7 @@ def _select_tools() -> list[str]:
|
||||
selected: set[str] = set()
|
||||
expanded: str | None = None
|
||||
focus_category: str | None = None
|
||||
first_render = True
|
||||
|
||||
while True:
|
||||
labels: list[str] = []
|
||||
@@ -387,13 +393,14 @@ def _select_tools() -> list[str]:
|
||||
labels.append(_tool_label(name, desc))
|
||||
|
||||
indices, action = pick_many(
|
||||
"Tools (space to toggle, enter to confirm):",
|
||||
"Tools (space to toggle, enter to confirm):" if first_render else "",
|
||||
labels,
|
||||
action_indices=action_indices,
|
||||
separator_indices=separator_indices,
|
||||
preselected=preselected,
|
||||
initial_cursor=initial_cursor,
|
||||
)
|
||||
first_render = False
|
||||
|
||||
# Carry over toggles made on this screen; tools not visible in this
|
||||
# render keep their previous state.
|
||||
|
||||
29
lib/cli/src/crewai_cli/platform_tools_catalog.py
Normal file
29
lib/cli/src/crewai_cli/platform_tools_catalog.py
Normal file
@@ -0,0 +1,29 @@
|
||||
"""CrewAI AMP platform tools exposed by the crew-creation wizard."""
|
||||
|
||||
from crewai_core.platform_apps import PLATFORM_APPS
|
||||
|
||||
|
||||
PLATFORM_TOOL_PREFIX = "platform:"
|
||||
|
||||
_APP_DESCRIPTIONS: dict[str, str] = {
|
||||
"asana": "Asana Integration",
|
||||
"box": "Box Integration",
|
||||
"clickup": "ClickUp Integration",
|
||||
"github": "GitHub Integration",
|
||||
"gmail": "Gmail Integration",
|
||||
"google_calendar": "Google Calendar Integration",
|
||||
"google_sheets": "Google Sheets Integration",
|
||||
"hubspot": "HubSpot Integration",
|
||||
"jira": "Jira Integration",
|
||||
"linear": "Linear Integration",
|
||||
"notion": "Notion Integration",
|
||||
"salesforce": "Salesforce Integration",
|
||||
"shopify": "Shopify Integration",
|
||||
"slack": "Slack Integration",
|
||||
"stripe": "Stripe Integration",
|
||||
"zendesk": "Zendesk Integration",
|
||||
}
|
||||
|
||||
PLATFORM_TOOLS: list[tuple[str, str]] = [
|
||||
(f"{PLATFORM_TOOL_PREFIX}{app}", _APP_DESCRIPTIONS[app]) for app in PLATFORM_APPS
|
||||
]
|
||||
@@ -388,8 +388,9 @@ def pick_many(
|
||||
Sorted list of selected indices, or ``(indices, action_index)`` when
|
||||
``action_indices`` is provided.
|
||||
"""
|
||||
click.echo()
|
||||
click.secho(f" {title}", fg="cyan")
|
||||
if title:
|
||||
click.echo()
|
||||
click.secho(f" {title}", fg="cyan")
|
||||
|
||||
if _is_interactive():
|
||||
try:
|
||||
|
||||
@@ -7,6 +7,7 @@ from unittest import mock
|
||||
import pytest
|
||||
import tomli
|
||||
from click.testing import CliRunner
|
||||
from crewai_core.platform_apps import PLATFORM_APPS
|
||||
from packaging.requirements import Requirement
|
||||
from packaging.version import Version
|
||||
import crewai_cli.create_json_crew as json_crew
|
||||
@@ -620,6 +621,52 @@ def test_json_wizard_tool_picker_lists_builtin_tools_across_categories(monkeypat
|
||||
}.isdisjoint(tool_names)
|
||||
|
||||
|
||||
def test_json_wizard_platform_tool_selection_stays_in_agent_tools(monkeypatch):
|
||||
picker_calls = 0
|
||||
|
||||
def pick_many(title: str, labels: list[str], **kwargs):
|
||||
nonlocal picker_calls
|
||||
picker_calls += 1
|
||||
if picker_calls == 1:
|
||||
platform_row = next(
|
||||
idx for idx, label in enumerate(labels) if "CrewAI Platform" in label
|
||||
)
|
||||
return [], platform_row
|
||||
|
||||
github = next(
|
||||
idx
|
||||
for idx, label in enumerate(labels)
|
||||
if label.startswith("GitHub Integration")
|
||||
and label.endswith("Platform: GitHubIntegration")
|
||||
)
|
||||
return [github], None
|
||||
|
||||
monkeypatch.setattr(json_crew, "pick_many", pick_many)
|
||||
monkeypatch.setattr(
|
||||
json_crew, "_prompt_text", lambda label, **kwargs: label.lower()
|
||||
)
|
||||
monkeypatch.setattr(json_crew, "_select_model", lambda: "openai/gpt-5.5")
|
||||
monkeypatch.setattr(json_crew, "_confirm", lambda *_args, **_kwargs: False)
|
||||
|
||||
agent = json_crew._wizard_agent(agent_num=1, existing_names=[])
|
||||
|
||||
assert agent is not None
|
||||
assert agent["tools"] == ["platform:github"]
|
||||
assert '"tools": ["platform:github"]' in json_crew._agent_to_jsonc(agent)
|
||||
|
||||
|
||||
def test_json_wizard_platform_catalog_contains_every_supported_app():
|
||||
platform_category = next(
|
||||
tools
|
||||
for category, tools in json_crew._TOOL_CATEGORIES
|
||||
if category == "CrewAI Platform"
|
||||
)
|
||||
|
||||
assert [name for name, _description in platform_category] == [
|
||||
f"platform:{app}" for app in PLATFORM_APPS
|
||||
]
|
||||
|
||||
|
||||
def test_multi_picker_skips_separator_on_initial_cursor(monkeypatch):
|
||||
cursors: list[int] = []
|
||||
|
||||
|
||||
Reference in New Issue
Block a user