Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Email generation for construction workflows: RFI responses, submittal transmittals, meeting notices, change order notifications. Professional templates with context-aware content.
.claude/skills/datadrivenconstruction-email-construction/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 435% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 130% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 104% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 349% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 156% | 0% |
Generate professional construction emails with proper formatting, context, and attachments handling. Templates for common construction communication workflows.
Generate professional RFI response emails.
pythonfrom dataclasses import dataclass from datetime import datetime from typing import Optional, List @dataclass class RFIResponse: rfi_number: str project_name: str subject: str question: str response: str responder_name: str responder_title: str attachments: List[str] = None cc_list: List[str] = None def generate_rfi_response_email(rfi: RFIResponse) -> dict: """Generate RFI response email.""" subject = f"RE: RFI #{rfi.rfi_number} - {rfi.subject}" body = f"""Dear Project Team, Please find below our response to RFI #{rfi.rfi_number}. **Project:** {rfi.project_name} **RFI Number:** {rfi.rfi_number} **Subject:** {rfi.subject} **Date:** {datetime.now().strftime('%B %d, %Y')} --- **QUESTION:** {rfi.question} --- **RESPONSE:** {rfi.response} --- Please proceed accordingly. If you have any questions regarding this response, please contact us. {"**Attachments:**" + chr(10) + chr(10).join(f"- {a}" for a in rfi.attachments) if rfi.attachments else ""} Best regards, {rfi.responder_name} {rfi.responder_title} """ return { 'subject': subject, 'body': body, 'cc': rfi.cc_list or [], 'attachments': rfi.attachments or [] }
Generate submittal transmittal emails.
python@dataclass class SubmittalTransmittal: submittal_number: str project_name: str spec_section: str description: str items: List[dict] action_required: str due_date: str sender_name: str sender_company: str def generate_submittal_email(submittal: SubmittalTransmittal) -> dict: """Generate submittal transmittal email.""" subject = f"Submittal {submittal.submittal_number} - {submittal.spec_section} - {submittal.description}" items_list = "\n".join( f" {i+1}. {item['description']} ({item.get('copies', 1)} copies)" for i, item in enumerate(submittal.items) ) body = f"""Dear Design Team, Please find attached Submittal {submittal.submittal_number} for your review. **Project:** {submittal.project_name} **Submittal No:** {submittal.submittal_number} **Spec Section:** {submittal.spec_section} **Description:** {submittal.description} **Items Transmitted:** {items_list} **Action Required:** {submittal.action_required} **Response Requested By:** {submittal.due_date} Please review and return with your comments at your earliest convenience. If you have any questions, please don't hesitate to contact us. Best regards, {submittal.sender_name} {submittal.sender_company} """ return { 'subject': subject, 'body': body, 'priority': 'normal' }
Generate meeting invitation emails.
python@dataclass class MeetingNotice: meeting_type: str # 'OAC', 'Subcontractor', 'Safety', 'Coordination' project_name: str date: str time: str location: str virtual_link: Optional[str] agenda_items: List[str] attendees: List[str] organizer_name: str def generate_meeting_notice(meeting: MeetingNotice) -> dict: """Generate meeting notice email.""" subject = f"{meeting.meeting_type} Meeting - {meeting.project_name} - {meeting.date}" agenda = "\n".join(f" {i+1}. {item}" for i, item in enumerate(meeting.agenda_items)) location_info = meeting.location if meeting.virtual_link: location_info += f"\n Virtual Option: {meeting.virtual_link}" body = f"""Dear Team, You are invited to the {meeting.meeting_type} Meeting for {meeting.project_name}. **Meeting Details:** - **Date:** {meeting.date} - **Time:** {meeting.time} - **Location:** {location_info} **Agenda:** {agenda} **Attendees:** {', '.join(meeting.attendees)} Please confirm your attendance by replying to this email. If you cannot attend, please send a delegate and notify the organizer. Regards, {meeting.organizer_name} Project Manager """ return { 'subject': subject, 'body': body, 'to': meeting.attendees, 'calendar_invite': { 'start': f"{meeting.date} {meeting.time}", 'duration': 60, 'location': meeting.location } }
Generate change order notification emails.
python@dataclass class ChangeOrderNotification: co_number: str project_name: str description: str amount: float schedule_impact: str reason: str status: str # 'Pending', 'Approved', 'Rejected' sender_name: str sender_title: str def generate_change_order_email(co: ChangeOrderNotification) -> dict: """Generate change order notification email.""" subject = f"Change Order #{co.co_number} - {co.status} - {co.project_name}" amount_str = f"${co.amount:,.2f}" if co.amount < 0: amount_str = f"(${abs(co.amount):,.2f}) Credit" body = f"""Dear Project Team, This email is to notify you of Change Order #{co.co_number} for {co.project_name}. **Change Order Details:** - **CO Number:** {co.co_number} - **Status:** {co.status} - **Description:** {co.description} **Financial Impact:** - **Amount:** {amount_str} **Schedule Impact:** - {co.schedule_impact} **Reason for Change:** {co.reason} {"Please review the attached documentation and provide your approval." if co.status == 'Pending' else ""} {"This change order has been approved. Please proceed accordingly." if co.status == 'Approved' else ""} If you have any questions, please contact the project team. Best regards, {co.sender_name} {co.sender_title} """ return { 'subject': subject, 'body': body, 'priority': 'high' if co.status == 'Pending' else 'normal', 'flag': co.status == 'Pending' }
Generate daily report distribution email.
python@dataclass class DailyReportEmail: project_name: str report_date: str report_number: int weather: str workforce_total: int work_summary: List[str] issues: List[str] sender_name: str def generate_daily_report_email(report: DailyReportEmail) -> dict: """Generate daily report distribution email.""" subject = f"Daily Report #{report.report_number} - {report.project_name} - {report.report_date}" work_items = "\n".join(f"• {item}" for item in report.work_summary) issues_text = "\n".join(f"• {issue}" for issue in report.issues) if report.issues else "None" body = f"""Daily Construction Report **Project:** {report.project_name} **Date:** {report.report_date} **Report #:** {report.report_number} --- **Weather:** {report.weather} **Total Workforce:** {report.workforce_total} workers on-site --- **Work Completed:** {work_items} --- **Issues/Delays:** {issues_text} --- Full report attached. Please contact the site office with any questions. {report.sender_name} Site Superintendent """ return { 'subject': subject, 'body': body, 'attachments': [f'Daily_Report_{report.report_number}.pdf'] }
Generate formal delay notification.
python@dataclass class DelayNotice: project_name: str contract_number: str delay_type: str # 'Excusable', 'Non-Excusable', 'Compensable' cause: str affected_activities: List[str] original_completion: str revised_completion: str days_impacted: int mitigation_plan: str sender_name: str sender_title: str def generate_delay_notice_email(delay: DelayNotice) -> dict: """Generate formal delay notice email.""" subject = f"NOTICE OF DELAY - {delay.project_name} - {delay.days_impacted} Days" activities = "\n".join(f" - {a}" for a in delay.affected_activities) body = f"""NOTICE OF DELAY **Project:** {delay.project_name} **Contract No:** {delay.contract_number} **Date:** {datetime.now().strftime('%B %d, %Y')} --- Dear Owner/Owner's Representative, In accordance with the contract requirements, this letter serves as formal notice of a delay to the project schedule. **Delay Classification:** {delay.delay_type} **Cause of Delay:** {delay.cause} **Affected Activities:** {activities} **Schedule Impact:** - Original Completion Date: {delay.original_completion} - Revised Completion Date: {delay.revised_completion} - Calendar Days Impacted: {delay.days_impacted} **Mitigation Plan:** {delay.mitigation_plan} We request a meeting to discuss this matter and coordinate recovery efforts. Please contact us at your earliest convenience. This notice is provided without prejudice to any rights or remedies available under the contract. Respectfully, {delay.sender_name} {delay.sender_title} """ return { 'subject': subject, 'body': body, 'priority': 'high', 'read_receipt': True, 'delivery_receipt': True }
pythonimport smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.mime.application import MIMEApplication class ConstructionEmailSender: """Send construction emails via SMTP.""" def __init__(self, smtp_server: str, smtp_port: int, username: str, password: str): self.smtp_server = smtp_server self.smtp_port = smtp_port self.username = username self.password = password def send(self, to: List[str], email_data: dict, from_addr: str = None): """Send email with optional attachments.""" msg = MIMEMultipart() msg['From'] = from_addr or self.username msg['To'] = ', '.join(to) msg['Subject'] = email_data['subject'] if email_data.get('cc'): msg['Cc'] = ', '.join(email_data['cc']) if email_data.get('priority') == 'high': msg['X-Priority'] = '1' msg.attach(MIMEText(email_data['body'], 'plain')) # Add attachments for attachment_path in email_data.get('attachments', []): with open(attachment_path, 'rb') as f: part = MIMEApplication(f.read()) part.add_header('Content-Disposition', 'attachment', filename=attachment_path.split('/')[-1]) msg.attach(part) # Send with smtplib.SMTP(self.smtp_server, self.smtp_port) as server: server.starttls() server.login(self.username, self.password) recipients = to + email_data.get('cc', []) server.sendmail(msg['From'], recipients, msg.as_string())
python# Example: Auto-generate RFI response email from RFI log import pandas as pd # Load RFI log rfi_log = pd.read_excel("RFI_Log.xlsx") # Get pending RFI pending_rfi = rfi_log[rfi_log['status'] == 'Response Ready'].iloc[0] # Generate email rfi = RFIResponse( rfi_number=pending_rfi['rfi_number'], project_name=pending_rfi['project'], subject=pending_rfi['subject'], question=pending_rfi['question'], response=pending_rfi['response'], responder_name='John Smith', responder_title='Project Architect', attachments=['SK-001.pdf'] ) email_data = generate_rfi_response_email(rfi) print(f"Subject: {email_data['subject']}") print(email_data['body'])
Common email templates for construction:
| Template | Use Case | |----------|----------| | RFI Response | Responding to Requests for Information | | Submittal Transmittal | Sending submittals for review | | Meeting Notice | OAC, subcontractor, safety meetings | | Change Order | CO notifications and approvals | | Daily Report | Daily report distribution | | Delay Notice | Formal delay notifications | | Payment Application | Pay app submissions | | Punch List | Punch list item notifications | | Closeout | Warranty and closeout docs |
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 4,507 | 9,153 | +103% | 1 | 1 | 0% | 979 | 5,235 | +435% | 0 | 0 | — |
case-02 | fail→fail | 6,522 | 10,327 | +58% | 1 | 1 | 0% | 1,162 | 5,640 | +385% | 0 | 0 | — |
case-03 | fail→fail | 5,971 | 6,633 | +11% | 1 | 1 | 0% | 1,139 | 4,898 | +330% | 0 | 0 | — |
case-04 | fail→pass | 10,949 | 7,061 | -36% | 1 | 1 | 0% | 2,184 | 5,017 | +130% | 0 | 0 | — |
case-05 | fail→pass | 30,006 | 5,038 | -83% | 1 | 1 | 0% | 2,217 | 4,513 | +104% | 0 | 0 | — |
case-06 | fail→pass | 5,164 | 6,326 | +23% | 1 | 1 | 0% | 1,094 | 4,917 | +349% | 0 | 0 | — |
case-07 | fail→pass | 8,773 | 6,485 | -26% | 1 | 1 | 0% | 1,922 | 4,915 | +156% | 0 | 0 | — |
case-08 | fail→pass | 8,542 | 7,262 | -15% | 1 | 1 | 0% | 1,844 | 5,166 | +180% | 0 | 0 | — |
case-09 | fail→pass | 14,625 | 7,345 | -50% | 1 | 1 | 0% | 2,835 | 5,158 | +82% | 0 | 0 | — |
case-10 | pass→pass | 6,923 | 7,787 | +12% | 1 | 1 | 0% | 1,340 | 5,243 | +291% | 0 | 0 | — |
case-11 | fail→pass | 6,290 | 6,029 | -4% | 1 | 1 | 0% | 1,223 | 4,738 | +287% | 0 | 0 | — |
case-12 | fail→pass | 4,987 | 7,765 | +56% | 1 | 1 | 0% | 1,031 | 5,277 | +412% | 0 | 0 | — |
case-13 | fail→pass | 5,820 | 7,428 | +28% | 1 | 1 | 0% | 1,252 | 5,224 | +317% | 0 | 0 | — |
case-14 | fail→fail | 10,116 | 8,051 | -20% | 1 | 1 | 0% | 1,733 | 5,183 | +199% | 0 | 0 | — |
case-15 | fail→pass | 13,725 | 10,085 | -27% | 1 | 1 | 0% | 2,230 | 5,483 | +146% | 0 | 0 | — |
case-16 | fail→pass | 5,566 | 7,334 | +32% | 1 | 1 | 0% | 1,161 | 5,050 | +335% | 0 | 0 | — |
case-17 | fail→pass | 13,376 | 13,023 | -3% | 1 | 1 | 0% | 2,848 | 6,186 | +117% | 0 | 0 | — |
case-18 | pass→pass | 10,470 | 7,946 | -24% | 1 | 1 | 0% | 2,163 | 5,163 | +139% | 0 | 0 | — |
case-19 | fail→fail | 9,585 | 7,316 | -24% | 1 | 1 | 0% | 2,019 | 5,056 | +150% | 0 | 0 | — |
case-20 | pass→pass | 14,834 | 15,722 | +6% | 1 | 1 | 0% | 2,677 | 6,323 | +136% | 0 | 0 | — |
case-21 | pass→pass | 16,971 | 18,848 | +11% | 1 | 1 | 0% | 2,715 | 6,295 | +132% | 0 | 0 | — |
case-22 | pass→pass | 20,629 | 23,490 | +14% | 1 | 1 | 0% | 4,024 | 8,729 | +117% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +59 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.