Create and manage reusable prompt templates
Create and manage reusable prompt templates with variable interpolation and composition. Claude will use this when you need to standardize prompts across multiple agents or tasks, triggered by creating `.yaml` template files in `prompts/templates/`.
/plugin marketplace add mindmorass/reflex/plugin install reflex@mindmorass-reflexThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Standardize prompt construction for agents with reusable, composable templates.
Consistent, well-structured prompts lead to better agent behavior. This skill provides:
# prompts/templates/example.yaml
name: example_template
version: "1.0"
description: Brief description of this template's purpose
# Variables that must be provided
variables:
- name: task_description
required: true
description: What the agent should do
- name: context
required: false
default: ""
description: Additional context
# The actual prompt template
template: |
You are a helpful assistant.
## Task
{task_description}
## Context
{context}
## Instructions
- Be concise
- Cite sources when possible
prompts/
├── templates/ # Reusable templates
│ ├── base/ # Foundation templates
│ │ ├── researcher.yaml
│ │ ├── coder.yaml
│ │ └── writer.yaml
│ ├── components/ # Composable parts
│ │ ├── output_format.yaml
│ │ ├── safety_guidelines.yaml
│ │ └── tool_usage.yaml
│ └── agents/ # Full agent prompts
│ ├── research_agent.yaml
│ └── code_review_agent.yaml
├── rendered/ # Compiled prompts (git-ignored)
└── tests/ # Prompt tests
#!/usr/bin/env python3
"""Prompt template engine."""
import re
import yaml
from pathlib import Path
from typing import Dict, Any, Optional, List
from dataclasses import dataclass
@dataclass
class TemplateVariable:
name: str
required: bool
default: Optional[str] = None
description: str = ""
class PromptTemplate:
"""A reusable prompt template."""
def __init__(self, path: str):
with open(path) as f:
data = yaml.safe_load(f)
self.name = data["name"]
self.version = data.get("version", "1.0")
self.description = data.get("description", "")
self.template = data["template"]
self.path = path
# Parse variables
self.variables = []
for var in data.get("variables", []):
self.variables.append(TemplateVariable(
name=var["name"],
required=var.get("required", False),
default=var.get("default"),
description=var.get("description", "")
))
# Parse includes
self.includes = data.get("includes", [])
def render(self, **kwargs) -> str:
"""Render the template with provided variables."""
# Check required variables
for var in self.variables:
if var.required and var.name not in kwargs:
raise ValueError(f"Missing required variable: {var.name}")
# Apply defaults
context = {}
for var in self.variables:
if var.name in kwargs:
context[var.name] = kwargs[var.name]
elif var.default is not None:
context[var.name] = var.default
else:
context[var.name] = ""
# Handle includes
result = self.template
for include in self.includes:
include_template = PromptTemplate(include)
include_content = include_template.render(**kwargs)
result = result.replace(f"{{{{include:{include}}}}}", include_content)
# Substitute variables
for key, value in context.items():
result = result.replace(f"{{{key}}}", str(value))
return result.strip()
def get_variable_names(self) -> List[str]:
"""Get all variable names in template."""
# Find {variable_name} patterns
pattern = r'\{(\w+)\}'
found = set(re.findall(pattern, self.template))
return list(found)
def validate(self) -> List[str]:
"""Validate template structure."""
errors = []
# Check all referenced variables are defined
referenced = set(self.get_variable_names())
defined = {v.name for v in self.variables}
undefined = referenced - defined
if undefined:
errors.append(f"Undefined variables: {undefined}")
unused = defined - referenced
if unused:
errors.append(f"Unused variables: {unused}")
# Check includes exist
for include in self.includes:
if not Path(include).exists():
errors.append(f"Include not found: {include}")
return errors
class TemplateRegistry:
"""Registry of all available templates."""
def __init__(self, templates_dir: str = "prompts/templates"):
self.templates_dir = Path(templates_dir)
self.templates: Dict[str, PromptTemplate] = {}
self._load_templates()
def _load_templates(self):
"""Load all templates from directory."""
for yaml_file in self.templates_dir.rglob("*.yaml"):
try:
template = PromptTemplate(str(yaml_file))
self.templates[template.name] = template
except Exception as e:
print(f"Warning: Failed to load {yaml_file}: {e}")
def get(self, name: str) -> Optional[PromptTemplate]:
"""Get a template by name."""
return self.templates.get(name)
def list(self) -> List[str]:
"""List all template names."""
return list(self.templates.keys())
def render(self, name: str, **kwargs) -> str:
"""Render a template by name."""
template = self.get(name)
if not template:
raise ValueError(f"Template not found: {name}")
return template.render(**kwargs)
# prompts/templates/base/agent_base.yaml
name: agent_base
version: "1.0"
description: Base template for all agents
variables:
- name: role
required: true
description: The agent's role (e.g., "researcher", "coder")
- name: capabilities
required: true
description: What the agent can do
- name: constraints
required: false
default: ""
description: Limitations or rules
template: |
You are a {role} agent.
## Capabilities
{capabilities}
## Constraints
{constraints}
## Response Format
- Be concise and actionable
- Structure responses with clear sections
- Cite sources when making claims
# prompts/templates/tasks/code_review.yaml
name: code_review_task
version: "1.0"
description: Template for code review tasks
variables:
- name: code
required: true
description: The code to review
- name: language
required: true
description: Programming language
- name: focus_areas
required: false
default: "security, performance, readability"
description: Areas to focus on
template: |
Review the following {language} code:
```{language}
{code}
{focus_areas}
Provide specific, actionable feedback with line references.
### Pattern 3: Composable Components
```yaml
# prompts/templates/components/output_json.yaml
name: output_json
version: "1.0"
description: JSON output format instructions
variables:
- name: schema
required: true
description: JSON schema to follow
template: |
## Output Format
Respond with valid JSON matching this schema:
```json
{schema}
```yaml
# prompts/templates/components/rag_context.yaml
name: rag_context
version: "1.0"
description: RAG retrieved context section
variables:
- name: retrieved_docs
required: true
description: Documents retrieved from RAG
- name: query
required: true
description: Original query
template: |
## Retrieved Context
The following information was retrieved for: "{query}"
{retrieved_docs}
Use this context to inform your response. If the context doesn't contain
relevant information, say so rather than making things up.
# prompts/templates/agents/research_agent.yaml
name: research_agent
version: "1.2"
description: Full prompt for research agent
includes:
- prompts/templates/base/agent_base.yaml
- prompts/templates/components/rag_context.yaml
variables:
- name: topic
required: true
description: Research topic
- name: depth
required: false
default: "comprehensive"
description: Research depth (brief, comprehensive, exhaustive)
- name: retrieved_docs
required: false
default: ""
description: RAG context if available
template: |
{{include:prompts/templates/base/agent_base.yaml}}
## Research Task
Research the following topic: {topic}
Depth: {depth}
{{include:prompts/templates/components/rag_context.yaml}}
## Research Process
1. Review any provided context
2. Identify key concepts and questions
3. Synthesize findings into clear insights
4. Note any gaps or areas needing more research
## Output Structure
- Summary (2-3 sentences)
- Key Findings (bullet points)
- Supporting Evidence (with sources)
- Open Questions
class PromptBuilder:
"""Build complex prompts programmatically."""
def __init__(self):
self.sections = []
self.variables = {}
def add_role(self, role: str) -> 'PromptBuilder':
"""Add role definition."""
self.sections.append(f"You are a {role}.")
return self
def add_context(self, context: str) -> 'PromptBuilder':
"""Add context section."""
self.sections.append(f"## Context
{context}")
return self
def add_task(self, task: str) -> 'PromptBuilder':
"""Add task description."""
self.sections.append(f"## Task
{task}")
return self
def add_constraints(self, constraints: List[str]) -> 'PromptBuilder':
"""Add constraints."""
constraint_list = "
".join(f"- {c}" for c in constraints)
self.sections.append(f"## Constraints
{constraint_list}")
return self
def add_examples(self, examples: List[Dict]) -> 'PromptBuilder':
"""Add few-shot examples."""
example_text = "## Examples
"
for i, ex in enumerate(examples, 1):
example_text += f"
### Example {i}
"
example_text += f"Input: {ex['input']}
"
example_text += f"Output: {ex['output']}
"
self.sections.append(example_text)
return self
def add_output_format(self, format_spec: str) -> 'PromptBuilder':
"""Add output format specification."""
self.sections.append(f"## Output Format
{format_spec}")
return self
def add_rag_context(self, docs: List[str]) -> 'PromptBuilder':
"""Add RAG retrieved documents."""
if docs:
doc_text = "
".join(f"[{i+1}] {doc}" for i, doc in enumerate(docs))
self.sections.append(f"## Retrieved Context
{doc_text}")
return self
def build(self) -> str:
"""Build final prompt."""
return "
".join(self.sections)
# Usage example
def build_research_prompt(topic: str, rag_docs: List[str] = None) -> str:
return (
PromptBuilder()
.add_role("research analyst specializing in technology trends")
.add_context("You have access to a knowledge base and web search.")
.add_task(f"Research and summarize: {topic}")
.add_rag_context(rag_docs or [])
.add_constraints([
"Cite sources for all claims",
"Distinguish between facts and speculation",
"Note confidence levels for findings"
])
.add_output_format("Markdown with headers and bullet points")
.build()
)
#!/usr/bin/env python3
"""Test framework for prompts."""
import yaml
from typing import List, Dict
from prompt_template import PromptTemplate, TemplateRegistry
class PromptTest:
"""A test case for a prompt template."""
def __init__(self, template: PromptTemplate, test_case: Dict):
self.template = template
self.name = test_case["name"]
self.variables = test_case["variables"]
self.assertions = test_case.get("assertions", [])
def run(self) -> Dict:
"""Run the test."""
results = {"name": self.name, "passed": True, "errors": []}
try:
rendered = self.template.render(**self.variables)
for assertion in self.assertions:
if assertion["type"] == "contains":
if assertion["value"] not in rendered:
results["passed"] = False
results["errors"].append(
f"Expected to contain: {assertion['value']}"
)
elif assertion["type"] == "not_contains":
if assertion["value"] in rendered:
results["passed"] = False
results["errors"].append(
f"Should not contain: {assertion['value']}"
)
elif assertion["type"] == "length_max":
if len(rendered) > assertion["value"]:
results["passed"] = False
results["errors"].append(
f"Length {len(rendered)} exceeds max {assertion['value']}"
)
except Exception as e:
results["passed"] = False
results["errors"].append(str(e))
return results
def run_prompt_tests(test_file: str) -> List[Dict]:
"""Run all tests in a test file."""
with open(test_file) as f:
test_config = yaml.safe_load(f)
template = PromptTemplate(test_config["template"])
results = []
for test_case in test_config["tests"]:
test = PromptTest(template, test_case)
results.append(test.run())
return results
# prompts/tests/test_research_agent.yaml
template: prompts/templates/agents/research_agent.yaml
tests:
- name: basic_render
variables:
role: researcher
capabilities: "Search web, analyze documents"
topic: "machine learning"
assertions:
- type: contains
value: "researcher"
- type: contains
value: "machine learning"
- name: with_constraints
variables:
role: researcher
capabilities: "Search web"
constraints: "Do not make claims without sources"
topic: "AI safety"
assertions:
- type: contains
value: "Do not make claims"
- name: default_values
variables:
role: analyst
capabilities: "Analyze data"
topic: "market trends"
assertions:
- type: not_contains
value: "{constraints}" # Should use default, not raw variable
# prompts/CHANGELOG.yaml
research_agent:
- version: "1.2"
date: "2024-01-15"
changes:
- Added RAG context section
- Improved output structure
- version: "1.1"
date: "2024-01-10"
changes:
- Added depth parameter
- version: "1.0"
date: "2024-01-01"
changes:
- Initial version
def migrate_prompt_version(
template_name: str,
old_version: str,
new_version: str
):
"""Migrate agents using old prompt version to new version."""
# Find agents using this template
agents_dir = Path("agents/definitions")
for agent_file in agents_dir.glob("*.yaml"):
with open(agent_file) as f:
agent = yaml.safe_load(f)
if agent.get("prompt_template") == template_name:
if agent.get("prompt_version") == old_version:
agent["prompt_version"] = new_version
with open(agent_file, "w") as f:
yaml.dump(agent, f)
print(f"Updated {agent_file}")
Track improvements as you develop prompts.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.