Skip to content
Merged
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
2 changes: 1 addition & 1 deletion lib/crewai/src/crewai/cli/create_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def create_flow(name: str) -> None:
tools_template_files = ["tools/__init__.py", "tools/custom_tool.py"]

crew_folders = [
"poem_crew",
"content_crew",
]

def process_file(src_file: Path, dst_file: Path) -> None:
Expand Down
4 changes: 2 additions & 2 deletions lib/crewai/src/crewai/cli/templates/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,11 @@ my_crew/
my_flow/
├── src/my_flow/
│ ├── crews/ # Multiple crew definitions
│ │ └── poem_crew/
│ │ └── content_crew/
│ │ ├── config/
│ │ │ ├── agents.yaml
│ │ │ └── tasks.yaml
│ │ └── poem_crew.py
│ │ └── content_crew.py
│ ├── tools/ # Custom tools
│ ├── main.py # Flow orchestration
│ └── ...
Expand Down
2 changes: 1 addition & 1 deletion lib/crewai/src/crewai/cli/templates/flow/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ crewai run

This command initializes the {{name}} Flow as defined in your configuration.

This example, unmodified, will run the create a `report.md` file with the output of a research on LLMs in the root folder.
This example, unmodified, will run a content creation flow on AI Agents and save the output to `output/post.md`.

## Understanding Your Crew

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
planner:
role: >
Content Planner
goal: >
Plan a detailed and engaging blog post outline on {topic}
backstory: >
You're an experienced content strategist who excels at creating
structured outlines for blog posts. You know how to organize ideas
into a logical flow that keeps readers engaged from start to finish.

writer:
role: >
Content Writer
goal: >
Write a compelling and well-structured blog post on {topic}
based on the provided outline
backstory: >
You're a skilled writer with a talent for turning outlines into
engaging, informative blog posts. Your writing is clear, conversational,
and backed by solid reasoning. You adapt your tone to the subject matter
while keeping things accessible to a broad audience.

editor:
role: >
Content Editor
goal: >
Review and polish the blog post on {topic} to ensure it is
publication-ready
backstory: >
You're a meticulous editor with years of experience refining written
content. You have an eye for clarity, flow, grammar, and consistency.
You improve prose without changing the author's voice and ensure every
piece you touch is polished and professional.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
planning_task:
description: >
Create a detailed outline for a blog post about {topic}.

The outline should include:
- A compelling title
- An introduction hook
- 3-5 main sections with key points to cover in each
- A conclusion with a call to action

Make the outline detailed enough that a writer can produce
a full blog post from it without additional research.
expected_output: >
A structured blog post outline with a title, introduction notes,
detailed section breakdowns, and conclusion notes.
agent: planner

writing_task:
description: >
Using the outline provided, write a full blog post about {topic}.

Requirements:
- Follow the outline structure closely
- Write in a clear, engaging, and conversational tone
- Each section should be 2-3 paragraphs
- Include a strong introduction and conclusion
- Target around 800-1200 words
expected_output: >
A complete blog post in markdown format, ready for editing.
The post should follow the outline and be well-written with
clear transitions between sections.
agent: writer

editing_task:
description: >
Review and edit the blog post about {topic}.

Focus on:
- Fixing any grammar or spelling errors
- Improving sentence clarity and flow
- Ensuring consistent tone throughout
- Strengthening the introduction and conclusion
- Removing any redundancy

Do not rewrite the post — refine and polish it.
expected_output: >
The final, polished blog post in markdown format without '```'.
Publication-ready with clean formatting and professional prose.
agent: editor
output_file: output/post.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@


@CrewBase
class PoemCrew:
"""Poem Crew"""
class ContentCrew:
"""Content Crew"""

agents: list[BaseAgent]
tasks: list[Task]
Expand All @@ -20,26 +20,50 @@ class PoemCrew:
agents_config = "config/agents.yaml"
tasks_config = "config/tasks.yaml"

# If you would lik to add tools to your crew, you can learn more about it here:
# If you would like to add tools to your crew, you can learn more about it here:
# https://docs.crewai.com/concepts/agents#agent-tools
@agent
def poem_writer(self) -> Agent:
def planner(self) -> Agent:
return Agent(
config=self.agents_config["poem_writer"], # type: ignore[index]
config=self.agents_config["planner"], # type: ignore[index]
)

@agent
def writer(self) -> Agent:
return Agent(
config=self.agents_config["writer"], # type: ignore[index]
)

@agent
def editor(self) -> Agent:
return Agent(
config=self.agents_config["editor"], # type: ignore[index]
)

# To learn more about structured task outputs,
# task dependencies, and task callbacks, check out the documentation:
# https://docs.crewai.com/concepts/tasks#overview-of-a-task
@task
def write_poem(self) -> Task:
def planning_task(self) -> Task:
return Task(
config=self.tasks_config["planning_task"], # type: ignore[index]
)

@task
def writing_task(self) -> Task:
return Task(
config=self.tasks_config["writing_task"], # type: ignore[index]
)

@task
def editing_task(self) -> Task:
return Task(
config=self.tasks_config["write_poem"], # type: ignore[index]
config=self.tasks_config["editing_task"], # type: ignore[index]
)

@crew
def crew(self) -> Crew:
"""Creates the Research Crew"""
"""Creates the Content Crew"""
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing __init__.py for content_crew package

Medium Severity

The old poem_crew/__init__.py was deleted but no corresponding content_crew/__init__.py was created. The create_flow.py copies crew folders using rglob("*"), so the generated project's content_crew directory will lack an __init__.py. The flow's main.py imports from {{folder_name}}.crews.content_crew.content_crew import ContentCrew, which may fail or behave unexpectedly without a proper package marker depending on the Python/packaging setup.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit cf49b3f. Configure here.

# To learn how to add knowledge sources to your crew, check out the documentation:
# https://docs.crewai.com/concepts/knowledge#what-is-knowledge

Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

63 changes: 32 additions & 31 deletions lib/crewai/src/crewai/cli/templates/flow/main.py
Original file line number Diff line number Diff line change
@@ -1,59 +1,60 @@
#!/usr/bin/env python
from random import randint

from pydantic import BaseModel

from crewai.flow import Flow, listen, start

from {{folder_name}}.crews.poem_crew.poem_crew import PoemCrew
from {{folder_name}}.crews.content_crew.content_crew import ContentCrew


class PoemState(BaseModel):
sentence_count: int = 1
poem: str = ""
class ContentState(BaseModel):
topic: str = ""
outline: str = ""
draft: str = ""
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused outline and draft state fields

Low Severity

ContentState defines outline and draft fields that are never read or written anywhere in the flow. Only topic and final_post are used. Since this is a template that new users will scaffold from, these dead fields add confusion about the intended flow architecture without providing value.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit cf49b3f. Configure here.

final_post: str = ""


class PoemFlow(Flow[PoemState]):
class ContentFlow(Flow[ContentState]):

@start()
def generate_sentence_count(self, crewai_trigger_payload: dict = None):
print("Generating sentence count")
def plan_content(self, crewai_trigger_payload: dict = None):
print("Planning content")

# Use trigger payload if available
if crewai_trigger_payload:
# Example: use trigger data to influence sentence count
self.state.sentence_count = crewai_trigger_payload.get('sentence_count', randint(1, 5))
self.state.topic = crewai_trigger_payload.get("topic", "AI Agents")
print(f"Using trigger payload: {crewai_trigger_payload}")
else:
self.state.sentence_count = randint(1, 5)
self.state.topic = "AI Agents"

print(f"Topic: {self.state.topic}")

@listen(generate_sentence_count)
def generate_poem(self):
print("Generating poem")
@listen(plan_content)
def generate_content(self):
print(f"Generating content on: {self.state.topic}")
result = (
PoemCrew()
ContentCrew()
.crew()
.kickoff(inputs={"sentence_count": self.state.sentence_count})
.kickoff(inputs={"topic": self.state.topic})
)

print("Poem generated", result.raw)
self.state.poem = result.raw
print("Content generated")
self.state.final_post = result.raw

@listen(generate_poem)
def save_poem(self):
print("Saving poem")
with open("poem.txt", "w") as f:
f.write(self.state.poem)
@listen(generate_content)
def save_content(self):
print("Saving content")
with open("output/post.md", "w") as f:
f.write(self.state.final_post)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Writing to subdirectory without ensuring it exists

Medium Severity

save_content writes to output/post.md without ensuring the output/ directory exists. The old template wrote to poem.txt in the current directory, which always works. The new code will raise a FileNotFoundError if the output/ directory hasn't been created. It currently relies on the crew's editing_task output_file setting to implicitly create the directory first — a fragile coupling that breaks if someone modifies the task config.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 824586e. Configure here.

print("Post saved to output/post.md")


def kickoff():
poem_flow = PoemFlow()
poem_flow.kickoff()
content_flow = ContentFlow()
content_flow.kickoff()


def plot():
poem_flow = PoemFlow()
poem_flow.plot()
content_flow = ContentFlow()
content_flow.plot()


def run_with_trigger():
Expand All @@ -74,10 +75,10 @@ def run_with_trigger():

# Create flow and kickoff with trigger payload
# The @start() methods will automatically receive crewai_trigger_payload parameter
poem_flow = PoemFlow()
content_flow = ContentFlow()

try:
result = poem_flow.kickoff({"crewai_trigger_payload": trigger_payload})
result = content_flow.kickoff({"crewai_trigger_payload": trigger_payload})
return result
except Exception as e:
raise Exception(f"An error occurred while running the flow with trigger: {e}")
Expand Down
Loading
Loading