mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-09-23 03:11:05 +00:00
* feat: scaffold project with assistant instruction files - Updated project creation to include `CLAUDE.md` and `GEMINI.md` that import `AGENTS.md`, ensuring consistent guidance across coding assistants. - Implemented utility functions to copy assistant instruction files during project setup. - Enhanced documentation in `AGENTS.md` to emphasize the importance of keeping telemetry enabled for optimal performance. - Added tests to verify the correct scaffolding of assistant instruction files and their contents. * fix(cli): neutral observability guidance in scaffolded AGENTS.md - State the observability rule as the user's decision, never a fix for console warnings, speed, or a "clean" configuration - Rewrite the AMP section as built-in capabilities: no "free", "proactively", "sales pitch", or scripted pitches - Turn the research mandate into a list of sources to consult when version details matter - Retarget the scaffold tests to the new wording Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(cli): scaffold assistant files for JSON crews and harden telemetry tests - create_json_crew, the default `crewai create crew` path, now copies AGENTS.md, CLAUDE.md and GEMINI.md; AGENTS.md documents the JSON layout - span helper no longer depends on OTEL_SDK_DISABLED being popped by an earlier test; thread-scope test stops its worker before leaving the mock - single import style in the shutdown test Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
162 lines
6.0 KiB
Python
162 lines
6.0 KiB
Python
from pathlib import Path
|
|
|
|
import click
|
|
from crewai_core.telemetry import Telemetry
|
|
|
|
from crewai_cli.git import initialize_if_git_available
|
|
from crewai_cli.utils import (
|
|
copy_assistant_imports,
|
|
copy_assistant_instructions,
|
|
get_or_create_project_id,
|
|
)
|
|
from crewai_cli.version import get_crewai_tools_dependency
|
|
|
|
|
|
DECLARATIVE_FLOW_FOLDERS = ("crews", "tools", "knowledge", "skills")
|
|
|
|
|
|
def create_flow(name: str, *, declarative: bool = False) -> None:
|
|
"""Create a new flow."""
|
|
folder_name = name.replace(" ", "_").replace("-", "_").lower()
|
|
class_name = name.replace("_", " ").replace("-", " ").title().replace(" ", "")
|
|
|
|
click.secho(f"Creating flow {folder_name}...", fg="green", bold=True)
|
|
|
|
project_root = Path(folder_name)
|
|
if project_root.exists():
|
|
click.secho(f"Error: Folder {folder_name} already exists.", fg="red")
|
|
return
|
|
|
|
telemetry = Telemetry()
|
|
telemetry.flow_creation_span(class_name)
|
|
|
|
if declarative:
|
|
_create_declarative_flow(name, class_name, folder_name, project_root)
|
|
else:
|
|
_create_python_flow(name, class_name, folder_name, project_root)
|
|
|
|
# Minted at creation so the project has a stable identity from run one.
|
|
project_id = get_or_create_project_id(project_root / "pyproject.toml")
|
|
# After the mint, unlike flow_creation_span above, which fires before the id exists.
|
|
telemetry.project_created_span("flow", project_id)
|
|
initialize_if_git_available(project_root)
|
|
|
|
click.secho(f"Flow {name} created successfully!", fg="green", bold=True)
|
|
|
|
|
|
def _create_python_flow(
|
|
name: str, class_name: str, folder_name: str, project_root: Path
|
|
) -> None:
|
|
(project_root / "src" / folder_name).mkdir(parents=True)
|
|
(project_root / "src" / folder_name / "crews").mkdir(parents=True)
|
|
(project_root / "src" / folder_name / "tools").mkdir(parents=True)
|
|
(project_root / "tests").mkdir(exist_ok=True)
|
|
|
|
with open(project_root / ".env", "w") as file:
|
|
file.write("OPENAI_API_KEY=YOUR_API_KEY")
|
|
|
|
package_dir = Path(__file__).parent
|
|
templates_dir = package_dir / "templates" / "flow"
|
|
|
|
copy_assistant_instructions(project_root)
|
|
|
|
root_template_files = [".gitignore", "pyproject.toml", "README.md"]
|
|
src_template_files = ["__init__.py", "main.py"]
|
|
tools_template_files = ["tools/__init__.py", "tools/custom_tool.py"]
|
|
|
|
crew_folders = [
|
|
"content_crew",
|
|
]
|
|
|
|
def process_file(src_file: Path, dst_file: Path) -> None:
|
|
if src_file.suffix in [".pyc", ".pyo", ".pyd"]:
|
|
return
|
|
|
|
try:
|
|
with open(src_file, "r", encoding="utf-8") as file:
|
|
content = file.read()
|
|
except Exception as e:
|
|
click.secho(f"Error processing file {src_file}: {e}", fg="red")
|
|
return
|
|
|
|
content = content.replace("{{name}}", name)
|
|
content = content.replace("{{flow_name}}", class_name)
|
|
content = content.replace("{{folder_name}}", folder_name)
|
|
content = content.replace(
|
|
"{{crewai_tools_dependency}}", get_crewai_tools_dependency()
|
|
)
|
|
|
|
with open(dst_file, "w") as file:
|
|
file.write(content)
|
|
|
|
for file_name in root_template_files:
|
|
src_file = templates_dir / file_name
|
|
dst_file = project_root / file_name
|
|
process_file(src_file, dst_file)
|
|
|
|
for file_name in src_template_files:
|
|
src_file = templates_dir / file_name
|
|
dst_file = project_root / "src" / folder_name / file_name
|
|
process_file(src_file, dst_file)
|
|
|
|
for file_name in tools_template_files:
|
|
src_file = templates_dir / file_name
|
|
dst_file = project_root / "src" / folder_name / file_name
|
|
process_file(src_file, dst_file)
|
|
|
|
for crew_folder in crew_folders:
|
|
src_crew_folder = templates_dir / "crews" / crew_folder
|
|
dst_crew_folder = project_root / "src" / folder_name / "crews" / crew_folder
|
|
if src_crew_folder.exists():
|
|
for src_file in src_crew_folder.rglob("*"):
|
|
if src_file.is_file():
|
|
relative_path = src_file.relative_to(src_crew_folder)
|
|
dst_file = dst_crew_folder / relative_path
|
|
dst_file.parent.mkdir(parents=True, exist_ok=True)
|
|
process_file(src_file, dst_file)
|
|
else:
|
|
click.secho(
|
|
f"Warning: Crew folder {crew_folder} not found in template.",
|
|
fg="yellow",
|
|
)
|
|
|
|
|
|
def _create_declarative_flow(
|
|
name: str, class_name: str, folder_name: str, project_root: Path
|
|
) -> None:
|
|
project_root.mkdir(parents=True)
|
|
package_root = project_root / "src" / folder_name
|
|
package_root.mkdir(parents=True)
|
|
for folder in DECLARATIVE_FLOW_FOLDERS:
|
|
(package_root / folder).mkdir()
|
|
|
|
package_dir = Path(__file__).parent
|
|
templates_dir = package_dir / "templates" / "declarative_flow"
|
|
root_template_files = {".gitignore", "AGENTS.md", "README.md", "pyproject.toml"}
|
|
|
|
for src_file in templates_dir.rglob("*"):
|
|
if not src_file.is_file():
|
|
continue
|
|
|
|
relative_path = src_file.relative_to(templates_dir)
|
|
dst_file = (
|
|
project_root / relative_path
|
|
if relative_path.name in root_template_files
|
|
else package_root / relative_path
|
|
)
|
|
dst_file.parent.mkdir(parents=True, exist_ok=True)
|
|
content = src_file.read_text(encoding="utf-8")
|
|
content = content.replace("{{name}}", name)
|
|
content = content.replace("{{flow_name}}", class_name)
|
|
content = content.replace("{{folder_name}}", folder_name)
|
|
content = content.replace(
|
|
"{{crewai_tools_dependency}}", get_crewai_tools_dependency()
|
|
)
|
|
dst_file.write_text(content, encoding="utf-8")
|
|
|
|
copy_assistant_imports(project_root)
|
|
(project_root / ".env").write_text("OPENAI_API_KEY=YOUR_API_KEY", encoding="utf-8")
|
|
(package_root / "__init__.py").write_text("", encoding="utf-8")
|
|
for folder in DECLARATIVE_FLOW_FOLDERS:
|
|
(package_root / folder / ".gitkeep").write_text("", encoding="utf-8")
|