Install
$ agentstack add skill-curiouslearner-devkit-report-generator ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Report Generator Skill
Generate professional markdown and HTML reports from data with charts, tables, and analysis.
Instructions
You are a report generation expert. When invoked:
- Analyze Data:
- Understand data structure and content
- Identify key metrics and insights
- Calculate statistics and trends
- Detect patterns and anomalies
- Generate executive summaries
- Create Report Structure:
- Design clear, logical sections
- Create table of contents
- Add executive summary
- Include detailed analysis
- Provide recommendations
- Generate Visualizations:
- Create tables for structured data
- Generate charts (bar, line, pie, scatter)
- Add badges and indicators
- Include code blocks and examples
- Format numbers and percentages
- Format Output:
- Generate markdown reports
- Create HTML reports with styling
- Export to PDF
- Add branding and customization
- Ensure responsive design
Usage Examples
@report-generator data.csv
@report-generator --format html
@report-generator --template executive-summary
@report-generator --charts --pdf
@report-generator --compare baseline.json current.json
Report Types
Executive Summary Report
def generate_executive_summary(data, title="Executive Summary"):
"""
Generate high-level executive summary report
"""
from datetime import datetime
report = f"""# {title}
**Generated:** {datetime.now().strftime('%B %d, %Y at %I:%M %p')}
---
## Key Highlights
"""
# Calculate key metrics
metrics = calculate_key_metrics(data)
for metric in metrics:
icon = "✅" if metric['status'] == 'good' else "⚠️" if metric['status'] == 'warning' else "❌"
report += f"{icon} **{metric['name']}**: {metric['value']}\n"
report += f"""
---
## Performance Overview
| Metric | Current | Previous | Change |
|--------|---------|----------|--------|
"""
for metric in metrics:
if 'previous' in metric:
change = calculate_change(metric['current'], metric['previous'])
arrow = "↑" if change > 0 else "↓" if change 0 else "red" if change 0:
report += "| Column | Missing Count | Missing % |\n"
report += "|--------|---------------|----------|\n"
for col in missing[missing > 0].index:
count = missing[col]
pct = (count / len(df)) * 100
report += f"| {col} | {count:,} | {pct:.1f}% |\n"
else:
report += "✅ No missing values detected.\n"
report += "\n### Data Type Issues\n\n"
# Check for potential type issues
type_issues = []
for col in df.select_dtypes(include=['object']):
# Check if column should be numeric
try:
pd.to_numeric(df[col], errors='raise')
type_issues.append(f"- `{col}` appears to be numeric but stored as string")
except:
pass
# Check if column should be datetime
try:
pd.to_datetime(df[col], errors='raise')
if df[col].str.contains(r'\d{4}-\d{2}-\d{2}').any():
type_issues.append(f"- `{col}` appears to be datetime but stored as string")
except:
pass
if type_issues:
report += "\n".join(type_issues) + "\n"
else:
report += "✅ No data type issues detected.\n"
report += """
---
## Statistical Summary
### Numeric Columns
"""
# Add statistics for numeric columns
numeric_cols = df.select_dtypes(include=[np.number]).columns
if len(numeric_cols) > 0:
stats = df[numeric_cols].describe()
report += stats.to_markdown() + "\n"
# Add additional statistics
report += "\n### Additional Statistics\n\n"
report += "| Column | Median | Mode | Std Dev | Variance |\n"
report += "|--------|--------|------|---------|----------|\n"
for col in numeric_cols:
median = df[col].median()
mode = df[col].mode().iloc[0] if not df[col].mode().empty else "N/A"
std = df[col].std()
var = df[col].var()
report += f"| {col} | {median:.2f} | {mode} | {std:.2f} | {var:.2f} |\n"
report += """
### Categorical Columns
"""
categorical_cols = df.select_dtypes(include=['object']).columns
if len(categorical_cols) > 0:
for col in categorical_cols[:5]: # Limit to first 5
report += f"\n#### {col}\n\n"
value_counts = df[col].value_counts().head(10)
report += "| Value | Count | Percentage |\n"
report += "|-------|-------|------------|\n"
for value, count in value_counts.items():
pct = (count / len(df)) * 100
report += f"| {value} | {count:,} | {pct:.1f}% |\n"
report += """
---
## Distributions
"""
# Analyze distributions of numeric columns
for col in numeric_cols[:5]: # Limit to first 5
report += f"\n### {col} Distribution\n\n"
q1 = df[col].quantile(0.25)
q2 = df[col].quantile(0.50)
q3 = df[col].quantile(0.75)
iqr = q3 - q1
# Detect outliers
lower_bound = q1 - 1.5 * iqr
upper_bound = q3 + 1.5 * iqr
outliers = df[(df[col] upper_bound)]
report += f"""
**Quartiles:**
- Q1 (25%): {q1:.2f}
- Q2 (50%, Median): {q2:.2f}
- Q3 (75%): {q3:.2f}
- IQR: {iqr:.2f}
**Outliers:** {len(outliers)} ({len(outliers)/len(df)*100:.1f}%)
- Lower bound: {lower_bound:.2f}
- Upper bound: {upper_bound:.2f}
"""
report += """
---
## Correlations
"""
if len(numeric_cols) > 1:
corr_matrix = df[numeric_cols].corr()
report += "\n### Correlation Matrix\n\n"
report += corr_matrix.to_markdown() + "\n"
# Find strong correlations
report += "\n### Strong Correlations (|r| > 0.7)\n\n"
strong_corr = []
for i in range(len(corr_matrix.columns)):
for j in range(i+1, len(corr_matrix.columns)):
corr_val = corr_matrix.iloc[i, j]
if abs(corr_val) > 0.7:
col1 = corr_matrix.columns[i]
col2 = corr_matrix.columns[j]
strong_corr.append((col1, col2, corr_val))
if strong_corr:
for col1, col2, corr_val in strong_corr:
direction = "positive" if corr_val > 0 else "negative"
report += f"- **{col1}** ↔ **{col2}**: {corr_val:.3f} ({direction})\n"
else:
report += "No strong correlations found.\n"
report += """
---
## Insights
"""
# Generate insights
insights = generate_insights(df)
for insight in insights:
report += f"### {insight['title']}\n\n"
report += f"{insight['description']}\n\n"
if 'details' in insight:
for detail in insight['details']:
report += f"- {detail}\n"
report += "\n"
return report
def generate_insights(df):
"""Generate data insights"""
insights = []
# Insight: Completeness
missing_pct = (df.isnull().sum().sum() / (len(df) * len(df.columns))) * 100
if missing_pct 0:
insights.append({
"title": f"⚠️ Duplicate Records Found",
"description": f"Found {dup_count:,} duplicate rows ({dup_count/len(df)*100:.1f}% of dataset)",
"details": [
"Consider removing duplicates for accurate analysis",
"Review business logic for duplicate handling"
]
})
return insights
Performance Report
def generate_performance_report(metrics, baseline=None):
"""
Generate performance comparison report
"""
report = f"""# Performance Report
**Generated:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
---
## Summary
"""
if baseline:
report += "### Comparison with Baseline\n\n"
report += "| Metric | Current | Baseline | Change | Status |\n"
report += "|--------|---------|----------|--------|--------|\n"
for metric_name, current_value in metrics.items():
if metric_name in baseline:
baseline_value = baseline[metric_name]
change = ((current_value - baseline_value) / baseline_value) * 100
if abs(change) 0:
status = "🟢 Improved" if is_improvement(metric_name, change) else "🔴 Degraded"
else:
status = "🔴 Degraded" if is_improvement(metric_name, change) else "🟢 Improved"
report += f"| {metric_name} | {current_value:.2f} | {baseline_value:.2f} | {change:+.1f}% | {status} |\n"
else:
report += "### Current Metrics\n\n"
report += "| Metric | Value | Status |\n"
report += "|--------|-------|--------|\n"
for metric_name, value in metrics.items():
threshold = get_threshold(metric_name)
status = evaluate_metric(value, threshold)
report += f"| {metric_name} | {value:.2f} | {status} |\n"
report += """
---
## Detailed Analysis
"""
for metric_name, value in metrics.items():
report += f"### {metric_name}\n\n"
if baseline and metric_name in baseline:
baseline_value = baseline[metric_name]
change = ((value - baseline_value) / baseline_value) * 100
report += f"- **Current:** {value:.2f}\n"
report += f"- **Baseline:** {baseline_value:.2f}\n"
report += f"- **Change:** {change:+.1f}%\n\n"
if abs(change) > 10:
report += f"⚠️ Significant change detected. "
report += "Review recent changes that may have impacted this metric.\n\n"
else:
report += f"- **Value:** {value:.2f}\n\n"
return report
def is_improvement(metric_name, change):
"""Determine if change is improvement based on metric type"""
# Lower is better for these metrics
lower_is_better = ['response_time', 'error_rate', 'latency', 'load_time']
for pattern in lower_is_better:
if pattern in metric_name.lower():
return change 0
HTML Report Generation
def generate_html_report(data, title="Report", template="default"):
"""
Generate styled HTML report
"""
# CSS styles
css = """
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
line-height: 1.6;
color: #333;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f5f5f5;
}
.report-container {
background: white;
padding: 40px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
h1 {
color: #2c3e50;
border-bottom: 3px solid #3498db;
padding-bottom: 10px;
}
h2 {
color: #34495e;
margin-top: 30px;
border-left: 4px solid #3498db;
padding-left: 10px;
}
h3 {
color: #7f8c8d;
}
table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
}
th {
background: #3498db;
color: white;
padding: 12px;
text-align: left;
font-weight: 600;
}
td {
padding: 10px 12px;
border-bottom: 1px solid #ecf0f1;
}
tr:hover {
background: #f8f9fa;
}
.metric-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px;
border-radius: 8px;
margin: 10px 0;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.metric-value {
font-size: 2em;
font-weight: bold;
}
.metric-label {
font-size: 0.9em;
opacity: 0.9;
}
.badge {
display: inline-block;
padding: 4px 12px;
border-radius: 12px;
font-size: 0.85em;
font-weight: 600;
}
.badge-success {
background: #2ecc71;
color: white;
}
.badge-warning {
background: #f39c12;
color: white;
}
.badge-danger {
background: #e74c3c;
color: white;
}
.chart-container {
margin: 30px 0;
padding: 20px;
background: #f8f9fa;
border-radius: 8px;
}
code {
background: #f4f4f4;
padding: 2px 6px;
border-radius: 3px;
font-family: 'Courier New', monospace;
}
pre {
background: #2c3e50;
color: #ecf0f1;
padding: 15px;
border-radius: 5px;
overflow-x: auto;
}
.timestamp {
color: #7f8c8d;
font-size: 0.9em;
}
"""
# Generate HTML content
html = f"""
{title}
{css}
{title}
Generated: {datetime.now().strftime('%B %d, %Y at %I:%M %p')}
{generate_html_content(data)}
"""
return html
def generate_html_content(data):
"""Generate HTML content from data"""
html = ""
# Key metrics section
if 'metrics' in data:
html += "Key Metrics"
html += ''
for metric in data['metrics']:
html += f"""
{metric['name']}
{metric['value']}
"""
html += ""
# Table data
if 'table' in data:
html += "Data Table"
html += generate_html_table(data['table'])
# Charts
if 'charts' in data:
for chart in data['charts']:
html += f'{chart["title"]}'
html += ''
html += generate_chart_html(chart)
html += ''
return html
def generate_html_table(table_data):
"""Generate HTML table from data"""
html = ""
# Header
if 'headers' in table_data:
html += ""
for header in table_data['headers']:
html += f"{header}"
html += ""
# Rows
html += ""
for row in table_data.get('rows', []):
html += ""
for cell in row:
html += f"{cell}"
html += ""
html += ""
html += ""
return html
def generate_chart_html(chart_data):
"""Generate Chart.js chart"""
chart_id = f"chart_{abs(hash(chart_data['title']))}"
html = f''
html += f"""
var ctx = document.getElementById('{chart_id}').getContext('2d');
var chart = new Chart(ctx, {{
type: '{chart_data.get('type', 'bar')}',
data: {{
labels: {chart_data['labels']},
datasets: [{{
label: '{chart_data['title']}',
data: {chart_data['data']},
backgroundColor: 'rgba(54, 162, 235, 0.5)',
borderColor: 'rgba(54, 162, 235, 1)',
borderWidth: 2
}}]
}},
options: {{
responsive: true,
maintainAspectRatio: true,
scales: {{
y: {{
beginAtZero: true
}}
}}
}}
}});
"""
return html
Markdown Tables
def generate_markdown_table(data, headers=None, alignment=None):
"""
Generate markdown table from data
alignment: list of 'left', 'center', 'right'
"""
if not data:
return ""
# Auto-detect headers if not pro
…
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [CuriousLearner](https://github.com/CuriousLearner)
- **Source:** [CuriousLearner/devkit](https://github.com/CuriousLearner/devkit)
- **License:** MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.