Compare commits

...

4 Commits

Author SHA1 Message Date
Vidit-Ostwal
2c8a3d2580 docs(cli): clarify @listen self-reference fails at validation
Document that matching @listen labels to handler names raises a validation
error at flow instantiation, and that the runtime loop only occurs if
validation is bypassed.
2026-08-05 23:16:25 +05:30
Vidit-Ostwal
b5c9df3d93 docs: update scaffold AGENTS.md for unified create CLI
Document crewai create tool/skill/template, lifecycle commands, and
deprecated scaffolding aliases in the project template AGENTS.md.
2026-08-05 23:10:35 +05:30
Vidit Ostwal
fef32f43a0 feat(cli): unify scaffolding under crewai create <resource> (#6821)
* feat(cli): add canonical `crewai create tool` command

Unify tool scaffolding under the create verb and deprecate
`crewai tool create` with a yellow warning while keeping backward
compatibility.

* feat(cli): add canonical `crewai create skill` command

Unify skill scaffolding under the create verb and deprecate
`crewai skill create` with a yellow warning while keeping backward
compatibility.

* feat(cli): add canonical `crewai create template` command

Unify template scaffolding under the create verb and deprecate
`crewai template add` with a yellow warning while keeping backward
compatibility.

* docs: document unified `crewai create` scaffolding commands

Document canonical create forms for tool, skill, and template projects,
note deprecated aliases, and update skills and agents-md guides.

* feat(cli): extend create picker and DMN guidance for all types

Show tool, skill, and template in the interactive create picker and
list every supported type in the CREWAI_DMN usage error.

* fix(cli): allow create tool/skill/template in CREWAI_DMN mode

Only set skip_provider in DMN mode for crew creation, since tool,
skill, and template paths reject that flag as a crew-only option.

* test(cli): patch TemplateCommand at cli lookup site in DMN test

create() resolves TemplateCommand from crewai_cli.cli, not from
remote_template.main directly.
2026-08-05 16:41:27 +00:00
Rip&Tear
a3351d153d ci: report required test check names on non-code PRs (#6822)
Some checks failed
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Check Documentation Broken Links / Check broken links (push) Has been cancelled
Vulnerability Scan / Detect changes (push) Has been cancelled
Vulnerability Scan / pip-audit (push) Has been cancelled
Skipped matrix jobs never post status for branch-protection names like
tests (3.10). Add lightweight skip jobs with the same names so Actions-only
and docs-only PRs can merge without waiting forever.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Rip&Tear <theCyberTech@users.noreply.github.com>
2026-08-05 19:46:19 +08:00
10 changed files with 456 additions and 22 deletions

View File

@@ -122,11 +122,25 @@ jobs:
.venv
key: uv-main-py${{ matrix.python-version }}-${{ hashFiles('uv.lock') }}
# Report the required check names (tests 3.103.13) when the matrix is skipped.
# Branch protection expects these names; a skipped matrix never reports them.
tests-skip:
name: tests (${{ matrix.python-version }})
needs: changes
if: needs.changes.outputs.code != 'true'
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12', '3.13']
steps:
- name: Skip non-code change
run: echo "Non-code change, skipping tests"
# Summary job to provide single status for branch protection
tests:
name: tests
runs-on: ubuntu-latest
needs: [changes, tests-matrix]
needs: [changes, tests-matrix, tests-skip]
if: always()
steps:
- name: Check results

View File

@@ -76,11 +76,25 @@ jobs:
.venv
key: uv-main-py${{ matrix.python-version }}-${{ hashFiles('uv.lock') }}
# Report the required check names when the matrix is skipped.
# Branch protection expects these names; a skipped matrix never reports them.
type-checker-skip:
name: type-checker (${{ matrix.python-version }})
needs: changes
if: needs.changes.outputs.code != 'true'
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- name: Skip non-code change
run: echo "Non-code change, skipping type checks"
# Summary job to provide single status for branch protection
type-checker:
name: type-checker
runs-on: ubuntu-latest
needs: [changes, type-checker-matrix]
needs: [changes, type-checker-matrix, type-checker-skip]
if: always()
steps:
- name: Check results

View File

@@ -36,24 +36,73 @@ crewai [COMMAND] [OPTIONS] [ARGUMENTS]
### 1. Create
Create a new crew or flow.
Create a new crew, flow, tool, skill, or template project.
```shell Terminal
crewai create [OPTIONS] TYPE NAME
```
- `TYPE`: Choose between "crew" or "flow"
- `NAME`: Name of the crew or flow
- `TYPE`: `crew`, `flow`, `tool`, `skill`, or `template`
- `NAME`: Name of the project, tool handle, skill, or template
Example:
#### Crew
```shell Terminal
crewai create crew my_new_crew
crewai create flow my_new_flow
crewai create crew my_new_crew --classic
```
By default, `crewai create crew` creates a JSON-first crew project with `crew.jsonc` and `agents/*.jsonc`. Use `crewai create crew my_new_crew --classic` only when you want the older Python/YAML scaffold with `crew.py`, `config/agents.yaml`, and `config/tasks.yaml`.
#### Flow
```shell Terminal
crewai create flow my_new_flow
crewai create flow my_new_flow --declarative
```
#### Tool
Scaffold a custom tool repository:
```shell Terminal
crewai create tool my_tool
```
#### Skill
Scaffold an agent skill. Inside a crew project (where `pyproject.toml` exists), the skill is created under `./skills/`:
```shell Terminal
crewai create skill my-skill
crewai create skill my-skill --no-project
```
Use `--no-project` to create the skill in the current directory instead of `./skills/`.
#### Template
Add a remote project template to the current directory:
```shell Terminal
crewai create template my-template
crewai create template my-template --output-dir custom_dir
```
Use `--output-dir` to override the output folder name (defaults to the template name).
#### Deprecated create aliases
These older commands still work but print a yellow deprecation warning. Prefer the `crewai create <type>` forms above.
| Deprecated | Use instead |
| :--- | :--- |
| `crewai tool create <handle>` | `crewai create tool <handle>` |
| `crewai skill create <name>` | `crewai create skill <name>` |
| `crewai template add <name>` | `crewai create template <name>` |
Lifecycle commands are unchanged — for example `crewai tool install`, `crewai skill publish`, and `crewai template list` stay under their resource groups.
### 2. Version
Show the installed version of CrewAI.

View File

@@ -32,10 +32,10 @@ You often need **both**: skills for expertise, tools for action. They are config
The CLI is the supported way to create a skill — it scaffolds the directory layout and a valid `SKILL.md` for you:
```shell Terminal
crewai skill create code-review
crewai create skill code-review
```
Inside a crew project (where `pyproject.toml` lives) this creates `./skills/code-review/`; outside a project it creates `./code-review/` in the current directory (you can force that behavior with `--no-project`):
Inside a crew project (where `pyproject.toml` lives) this creates `./skills/code-review/`; outside a project it creates `./code-review/` in the current directory (you can force that behavior with `--no-project` on `crewai create skill`):
```
skills/
@@ -178,12 +178,16 @@ agent = Agent(
## Creating, Publishing, and Installing Skills
Skills have a full lifecycle managed by the CLI: **create them with `crewai skill create`, publish them with `crewai skill publish`** — hand-rolling directories works for local experiments, but the CLI is the intended workflow and keeps your skill layout and frontmatter valid.
Skills have a full lifecycle managed by the CLI: **create them with `crewai create skill`, publish them with `crewai skill publish`** — hand-rolling directories works for local experiments, but the CLI is the intended workflow and keeps your skill layout and frontmatter valid.
<Note>
`crewai skill create` is deprecated and still works with a warning. Use `crewai create skill` instead.
</Note>
### Create
```shell Terminal
crewai skill create my-skill
crewai create skill my-skill
```
Scaffolds the directory (into `./skills/` inside a crew project) with a template `SKILL.md`, plus empty `scripts/`, `references/`, and `assets/` directories. Edit `SKILL.md` to define the instructions.

View File

@@ -21,9 +21,13 @@ crewai create crew my_crew
crewai create flow my_flow
# Tool repository
crewai tool create my_tool
crewai create tool my_tool
```
<Note>
`crewai tool create` is deprecated and still works with a warning. Use `crewai create tool` instead.
</Note>
## Tool Setup: Point Assistants to AGENTS.md
### Codex

View File

@@ -19,6 +19,7 @@ from crewai_cli.utils import (
enable_prompt_line_editing,
is_dmn_mode_enabled,
read_toml,
warn_deprecated_command,
)
@@ -137,7 +138,10 @@ def uv(uv_args: tuple[str, ...]) -> None:
@crewai.command()
@click.argument(
"type", required=False, default=None, type=click.Choice(["crew", "flow"])
"type",
required=False,
default=None,
type=click.Choice(["crew", "flow", "tool", "skill", "template"]),
)
@click.argument("name", required=False, default=None)
@click.option("--provider", type=str, help="The provider to use for the crew")
@@ -152,6 +156,21 @@ def uv(uv_args: tuple[str, ...]) -> None:
is_flag=True,
help="Create a declarative Flow project instead of a Python Flow project",
)
@click.option(
"--no-project",
"in_project",
is_flag=True,
default=True,
flag_value=False,
help="Skill only: create in current dir instead of ./skills/",
)
@click.option(
"-o",
"--output-dir",
type=str,
default=None,
help="Template only: directory name for the template (defaults to template name)",
)
def create(
type: str | None,
name: str | None,
@@ -159,14 +178,17 @@ def create(
skip_provider: bool = False,
classic: bool = False,
declarative: bool = False,
in_project: bool = True,
output_dir: str | None = None,
) -> None:
"""Create a new crew, or flow."""
"""Create a new crew, flow, tool, skill, or template."""
dmn_mode = is_dmn_mode_enabled()
if not type:
if dmn_mode:
raise click.UsageError(
"TYPE is required when CREWAI_DMN is set. "
"Use `crewai create crew <name>` or `crewai create flow <name>`."
"Use `crewai create <type> <name>` where type is one of: "
"crew, flow, tool, skill, template."
)
from crewai_cli.tui_picker import pick
@@ -176,6 +198,9 @@ def create(
"flow",
"A deterministic workflow with full control over agents and crews",
),
("tool", "A custom tool for the CrewAI Tool Repository"),
("skill", "An agent skill with instructions and optional assets"),
("template", "A remote project template from the CrewAI gallery"),
]
type = pick("What would you like to create?", options)
if type is None:
@@ -189,9 +214,36 @@ def create(
click.style(f" Name of your {type}", fg="cyan", bold=True),
prompt_suffix=click.style(" ", fg="bright_white"), # noqa: RUF001
)
if dmn_mode:
if dmn_mode and type == "crew":
skip_provider = True
if type == "crew":
if not in_project and type != "skill":
raise click.UsageError("--no-project can only be used with skill projects.")
if output_dir is not None and type != "template":
raise click.UsageError("--output-dir can only be used with template projects.")
if type == "tool":
if declarative or classic or provider is not None or skip_provider:
raise click.UsageError(
"Crew and flow options cannot be used with tool projects."
)
from crewai_cli.tools.main import ToolCommand
ToolCommand().create(name)
elif type == "skill":
if declarative or classic or provider is not None or skip_provider:
raise click.UsageError(
"Crew and flow options cannot be used with skill projects."
)
from crewai_cli.skills.main import SkillCommand
SkillCommand().create(name, in_project=in_project)
elif type == "template":
if declarative or classic or provider is not None or skip_provider:
raise click.UsageError(
"Crew and flow options cannot be used with template projects."
)
template_cmd = TemplateCommand()
template_cmd.add_template(name, output_dir)
elif type == "crew":
if declarative:
raise click.UsageError("--declarative can only be used with flow projects")
if classic:
@@ -207,7 +259,10 @@ def create(
create_flow(name, declarative=declarative)
else:
click.secho("Error: Invalid type. Must be 'crew' or 'flow'.", fg="red")
click.secho(
"Error: Invalid type. Must be 'crew', 'flow', 'tool', 'skill', or 'template'.",
fg="red",
)
@crewai.command()
@@ -652,6 +707,8 @@ def tool() -> None:
@tool.command(name="create")
@click.argument("handle")
def tool_create(handle: str) -> None:
"""[Deprecated: use `crewai create tool`] Create a custom tool project."""
warn_deprecated_command(old="crewai tool create", new="crewai create tool")
from crewai_cli.tools.main import ToolCommand
tool_cmd = ToolCommand()
@@ -702,6 +759,8 @@ def skill() -> None:
help="Create skill in current dir instead of ./skills/",
)
def skill_create(name: str, in_project: bool) -> None:
"""[Deprecated: use `crewai create skill`] Create a new agent skill."""
warn_deprecated_command(old="crewai skill create", new="crewai create skill")
from crewai_cli.skills.main import SkillCommand
skill_cmd = SkillCommand()
@@ -765,7 +824,8 @@ def template_list() -> None:
help="Directory name for the template (defaults to template name)",
)
def template_add(name: str, output_dir: str | None) -> None:
"""Add a template to the current directory."""
"""[Deprecated: use `crewai create template`] Add a template to the current directory."""
warn_deprecated_command(old="crewai template add", new="crewai create template")
template_cmd = TemplateCommand()
template_cmd.add_template(name, output_dir)

View File

@@ -40,6 +40,14 @@ This ensures generated code always matches the version actually installed, not s
-`Agent(llm=ChatOpenAI(...))` → ✅ `Agent(llm="openai/gpt-4o")` or `Agent(llm=LLM(model="..."))`
- ❌ Passing raw OpenAI client objects → ✅ Use `crewai.LLM` wrapper
### Deprecated CLI scaffolding aliases (still supported)
These commands remain supported but print a yellow deprecation warning. Prefer the canonical forms:
- ⚠️ `crewai tool create <handle>` → ✅ `crewai create tool <handle>`
- ⚠️ `crewai skill create <name>` → ✅ `crewai create skill <name>`
- ⚠️ `crewai template add <name>` → ✅ `crewai create template <name>`
### How to verify you're using current patterns:
1. You ran the version check and docs lookup steps above before writing code
2. All LLM references use `crewai.LLM` or string shorthand (`"openai/gpt-4o"`)
@@ -137,8 +145,26 @@ uv sync # Sync dependencies
uv lock # Lock dependencies
# Project scaffolding
crewai create crew <name> --skip_provider # New crew project
crewai create flow <name> --skip_provider # New flow project
crewai create crew <name> --skip_provider # New crew project
crewai create flow <name> # New flow project
crewai create tool <handle> # Custom tool repository
crewai create skill <name> # Agent skill (./skills/ in crew projects)
crewai create skill <name> --no-project # Skill in current directory
crewai create template <name> # Remote project template
crewai create template <name> -o <output_dir> # Template with custom output directory
# Deprecated scaffolding aliases (still work; print a yellow warning)
# crewai tool create <handle> → crewai create tool <handle>
# crewai skill create <name> → crewai create skill <name>
# crewai template add <name> → crewai create template <name>
# Tool, skill, and template lifecycle (unchanged)
crewai tool install <handle>
crewai tool publish
crewai skill install @org/name
crewai skill publish
crewai skill list
crewai template list
# Running
crewai run # Run crew or flow (auto-detects from pyproject.toml)
@@ -627,6 +653,26 @@ class MyFlow(Flow):
| `@listen(method)` | Triggers when specified method completes. Receives output as argument |
| `@router(method)` | Conditional branching. Returns string labels that trigger `@listen("label")` |
### `@listen` labels vs handler names
The string in `@listen("...")` is an **event or route label**, not the Python method name. Router return values, route labels, and method completion events share one trigger namespace.
**Never** use the same name for the `@listen` label and the handler method:
```python
# ❌ Wrong — raises a validation error when the flow is instantiated
@listen("create_video")
def create_video(self):
...
# ✅ Correct — distinct handler name (handle_* prefix is a common pattern)
@listen("create_video")
def handle_create_video(self):
...
```
If validation were bypassed, matching names would also cause the handler to re-trigger itself in a loop at runtime. This applies to all flows; it is especially common in **conversational flows** (`conversational = True`), where `@listen("...")` is a router intent name.
### Structured State
```python
from pydantic import BaseModel
@@ -1113,7 +1159,9 @@ Python >=3.10, <3.14
```bash
uv tool install crewai # Install CrewAI CLI
uv tool list # Verify installation
crewai create crew my_crew --skip_provider # Scaffold a new project
crewai create crew my_crew --skip_provider # Scaffold a crew project
crewai create tool my_tool # Scaffold a tool repository
crewai create skill my_skill # Scaffold an agent skill
crewai install # Install project dependencies
crewai run # Execute
```
@@ -1148,3 +1196,4 @@ crewai run # Execute
- Using `process=Process.hierarchical` without setting `manager_llm` or `manager_agent`
- Circular delegation: set `allow_delegation=False` on specialist agents
- Not installing tools package: `uv add crewai-tools`
- **Matching `@listen("label")` to the handler method name** — raises a validation error at flow instantiation; would re-trigger in an infinite loop at runtime only if validation is bypassed. Use a different method name (e.g. `handle_create_video` for `@listen("create_video")`)

View File

@@ -44,10 +44,19 @@ __all__ = [
"render_template",
"tree_copy",
"tree_find_and_replace",
"warn_deprecated_command",
"write_env_file",
]
def warn_deprecated_command(*, old: str, new: str) -> None:
"""Print a yellow deprecation warning for a legacy CLI command path."""
click.secho(
f"Warning: The command '{old}' is deprecated. Use '{new}' instead.",
fg="yellow",
)
console = Console()
_TEMPLATE_TOKEN_RE = re.compile(r"{{([a-zA-Z_][a-zA-Z0-9_]*)}}")

View File

@@ -228,6 +228,7 @@ def test_create_requires_type_in_dmn_mode(runner):
assert result.exit_code == 2
assert "TYPE is required when CREWAI_DMN is set" in result.output
assert "crew, flow, tool, skill, template" in result.output
def test_create_requires_name_in_dmn_mode(runner):

View File

@@ -0,0 +1,230 @@
"""Tests for unified `crewai create <resource>` scaffolding commands."""
from unittest import mock
import pytest
from click.testing import CliRunner
from crewai_cli.cli import create, crewai
@pytest.fixture
def runner():
return CliRunner()
@mock.patch("crewai_cli.tools.main.ToolCommand")
def test_create_tool_invokes_tool_command(mock_tool_command_cls, runner):
result = runner.invoke(create, ["tool", "my_tool"])
assert result.exit_code == 0, result.output
mock_tool_command_cls.return_value.create.assert_called_once_with("my_tool")
assert "deprecated" not in result.output.lower()
@mock.patch("crewai_cli.tools.main.ToolCommand")
def test_tool_create_is_deprecated_and_still_works(mock_tool_command_cls, runner):
result = runner.invoke(crewai, ["tool", "create", "my_tool"])
assert result.exit_code == 0, result.output
mock_tool_command_cls.return_value.create.assert_called_once_with("my_tool")
assert (
"Warning: The command 'crewai tool create' is deprecated. "
"Use 'crewai create tool' instead."
in result.output
)
@mock.patch("crewai_cli.tools.main.ToolCommand")
@pytest.mark.parametrize("extra_args", [["--classic"], ["--declarative"], ["--provider", "openai"], ["--skip_provider"]])
def test_create_tool_rejects_crew_and_flow_flags(mock_tool_command_cls, runner, extra_args):
result = runner.invoke(create, ["tool", "my_tool", *extra_args])
assert result.exit_code == 2, result.output
assert "Crew and flow options cannot be used with tool projects." in result.output
mock_tool_command_cls.return_value.create.assert_not_called()
@mock.patch("crewai_cli.skills.main.SkillCommand")
def test_create_skill_invokes_skill_command(mock_skill_command_cls, runner):
result = runner.invoke(create, ["skill", "my-skill"])
assert result.exit_code == 0, result.output
mock_skill_command_cls.return_value.create.assert_called_once_with(
"my-skill", in_project=True
)
assert "deprecated" not in result.output.lower()
@mock.patch("crewai_cli.skills.main.SkillCommand")
def test_create_skill_no_project_flag(mock_skill_command_cls, runner):
result = runner.invoke(create, ["skill", "my-skill", "--no-project"])
assert result.exit_code == 0, result.output
mock_skill_command_cls.return_value.create.assert_called_once_with(
"my-skill", in_project=False
)
@mock.patch("crewai_cli.skills.main.SkillCommand")
def test_skill_create_is_deprecated_and_still_works(mock_skill_command_cls, runner):
result = runner.invoke(crewai, ["skill", "create", "my-skill"])
assert result.exit_code == 0, result.output
mock_skill_command_cls.return_value.create.assert_called_once_with(
"my-skill", in_project=True
)
assert (
"Warning: The command 'crewai skill create' is deprecated. "
"Use 'crewai create skill' instead."
in result.output
)
@mock.patch("crewai_cli.skills.main.SkillCommand")
@pytest.mark.parametrize(
"extra_args",
[["--classic"], ["--declarative"], ["--provider", "openai"], ["--skip_provider"]],
)
def test_create_skill_rejects_crew_and_flow_flags(
mock_skill_command_cls, runner, extra_args
):
result = runner.invoke(create, ["skill", "my-skill", *extra_args])
assert result.exit_code == 2, result.output
assert "Crew and flow options cannot be used with skill projects." in result.output
mock_skill_command_cls.return_value.create.assert_not_called()
@mock.patch("crewai_cli.skills.main.SkillCommand")
def test_create_crew_rejects_no_project_flag(mock_skill_command_cls, runner):
result = runner.invoke(create, ["crew", "my-crew", "--no-project"])
assert result.exit_code == 2, result.output
assert "--no-project can only be used with skill projects." in result.output
mock_skill_command_cls.return_value.create.assert_not_called()
@mock.patch("crewai_cli.remote_template.main.TemplateCommand")
def test_create_template_invokes_template_command(mock_template_command_cls, runner):
result = runner.invoke(create, ["template", "my-template"])
assert result.exit_code == 0, result.output
mock_template_command_cls.return_value.add_template.assert_called_once_with(
"my-template", None
)
assert "deprecated" not in result.output.lower()
@mock.patch("crewai_cli.remote_template.main.TemplateCommand")
def test_create_template_output_dir_flag(mock_template_command_cls, runner):
result = runner.invoke(
create, ["template", "my-template", "--output-dir", "custom_dir"]
)
assert result.exit_code == 0, result.output
mock_template_command_cls.return_value.add_template.assert_called_once_with(
"my-template", "custom_dir"
)
@mock.patch("crewai_cli.remote_template.main.TemplateCommand")
def test_template_add_is_deprecated_and_still_works(mock_template_command_cls, runner):
result = runner.invoke(
crewai, ["template", "add", "my-template", "--output-dir", "custom_dir"]
)
assert result.exit_code == 0, result.output
mock_template_command_cls.return_value.add_template.assert_called_once_with(
"my-template", "custom_dir"
)
assert (
"Warning: The command 'crewai template add' is deprecated. "
"Use 'crewai create template' instead."
in result.output
)
@mock.patch("crewai_cli.remote_template.main.TemplateCommand")
@pytest.mark.parametrize(
"extra_args",
[["--classic"], ["--declarative"], ["--provider", "openai"], ["--skip_provider"]],
)
def test_create_template_rejects_crew_and_flow_flags(
mock_template_command_cls, runner, extra_args
):
result = runner.invoke(create, ["template", "my-template", *extra_args])
assert result.exit_code == 2, result.output
assert (
"Crew and flow options cannot be used with template projects."
in result.output
)
mock_template_command_cls.return_value.add_template.assert_not_called()
@mock.patch("crewai_cli.remote_template.main.TemplateCommand")
def test_create_crew_rejects_output_dir_flag(mock_template_command_cls, runner):
result = runner.invoke(create, ["crew", "my-crew", "--output-dir", "custom_dir"])
assert result.exit_code == 2, result.output
assert "--output-dir can only be used with template projects." in result.output
mock_template_command_cls.return_value.add_template.assert_not_called()
@mock.patch("crewai_cli.cli.enable_prompt_line_editing")
@mock.patch("crewai_cli.cli.click.prompt", return_value="picked-tool")
@mock.patch("crewai_cli.tui_picker.pick", return_value="tool")
@mock.patch("crewai_cli.tools.main.ToolCommand")
def test_create_picker_supports_tool_skill_and_template(
mock_tool_command_cls,
mock_pick,
mock_prompt,
mock_enable_prompt,
runner,
):
result = runner.invoke(create, [])
assert result.exit_code == 0, result.output
mock_pick.assert_called_once()
picker_options = mock_pick.call_args[0][1]
assert {option[0] for option in picker_options} == {
"crew",
"flow",
"tool",
"skill",
"template",
}
mock_prompt.assert_called_once()
mock_tool_command_cls.return_value.create.assert_called_once_with("picked-tool")
_DMN_ENV = {"CREWAI_DMN": "True"}
@mock.patch("crewai_cli.tools.main.ToolCommand")
def test_create_tool_works_in_dmn_mode(mock_tool_command_cls, runner):
result = runner.invoke(create, ["tool", "my_tool"], env=_DMN_ENV)
assert result.exit_code == 0, result.output
mock_tool_command_cls.return_value.create.assert_called_once_with("my_tool")
@mock.patch("crewai_cli.skills.main.SkillCommand")
def test_create_skill_works_in_dmn_mode(mock_skill_command_cls, runner):
result = runner.invoke(create, ["skill", "my-skill"], env=_DMN_ENV)
assert result.exit_code == 0, result.output
mock_skill_command_cls.return_value.create.assert_called_once_with(
"my-skill", in_project=True
)
@mock.patch("crewai_cli.cli.TemplateCommand")
def test_create_template_works_in_dmn_mode(mock_template_command_cls, runner):
result = runner.invoke(create, ["template", "my-template"], env=_DMN_ENV)
assert result.exit_code == 0, result.output
mock_template_command_cls.return_value.add_template.assert_called_once_with(
"my-template", None
)