Skill: lexalex36/outreach-demo (version 1.0.0)

--- name: outreach-demo description: Research a business website, produce a concise prospect report, recommend concrete OpenClaw use cases, and draft a tailored outreach email. Use when demonstrating OpenClaw to a business owner, preparing personalized prospecting materials from a website URL + contact email, or turning website research into an approval-gated email draft. --- # Outreach Demo Build a draft-first outreach package from three inputs: business name, website URL, and contact email. Keep the workflow evidence-based, concise, and approval-gated for any email send. ## Workflow 1. **Intake** - Collect: - business name - contact name if available - contact email - website URL - optional business notes - If website URL or email is missing, ask only for the missing field. 2. **Website research** - Inspect the public website only. - Prefer homepage, about, services/products, contact, and selected blog/resources pages. - Extract only visible, supportable observations. - Do not guess internal systems, revenue, staffing, or pain points. 3. **OpenClaw fit analysis** - Propose up to 3 concrete OpenClaw use cases. - Rank them by: - visible fit - likely business value - lowest-friction starting point - Also produce: - one `priority_move` - a short `why_it_matters[]` list - Phrase uncertain conclusions as “likely”, “appears”, or “suggests”. 4. **Report generation** - Use `references/report-format.md` for section shape. - For plain operator review, render markdown with: - `scripts/render_outreach_report.py --input <json> --output <md>` - For visually stronger delivery, render a polished HTML one-page brief with: - `scripts/render_outreach_report_html.py --input <json> --output <html>` - Preferred showcase artifact: convert the HTML brief to PDF with: - `scripts/render_outreach_report_pdf.sh --input <html> --output <pdf>` - Prefer PDF for live outreach when visual impact matters. 5. **Email draft generation** - Default to the value-first / “free doughnut” framing: give the prospect a small concrete asset before asking for time. - Use `references/email-template.md` for default tone and structure. - Read `references/value-first-outreach.md` when you need to optimize for scannability, subject lines, or a more enticing value-first CTA. - Render a plain-text draft with: - `scripts/render_outreach_email.py --input <json> --output <txt>` - Render lightweight HTML with: - `scripts/render_outreach_email_html.py --input <json> --output <html>` - Preferred showcase send mode: **HTML email + attached brief**. - Keep email short, specific, and visually scannable. - Default to draft-only. 6. **Approval gate** - Do not send email automatically. - Sending is allowed only after explicit user approval for that recipient/message. - Build a preview manifest with: - `scripts/render_outreach_preview.py --input <json> --subject <subject> --email-text <txt> --email-html <html> --brief <brief.html> --output <preview.json>` - Before send, preview or summarize: - recipient - subject - plain-text fallback body - HTML body when used - attached brief filename/format when used - For actual HTML+brief send, use: - `scripts/send_outreach_package.sh --to <email> --subject <subject> --text <txt> --html <html> --attach <brief.pdf>` - Use a client-facing attachment name such as: - `<Business-Name>-OpenClaw-Brief.pdf` ## Output standard Produce three artifacts when possible: - structured JSON summary - markdown report - plain-text email draft Recommended JSON fields: - `business_name` - `contact_name` - `contact_email` - `website_url` - `business_summary` - `likely_audience` - `website_observations[]` - `openclaw_fits[]` with: - `use_case` - `why_it_fits` - `likely_value` - `starting_point` - `priority_move` with: - `move` - `why_first` - `expected_benefit` - `why_it_matters[]` - `demo_angle` - `outreach_summary` ## Guardrails - Research public website content only unless the user supplies additional material. - Do not claim verified need where only a hypothesis exists. - Do not overpromise outcomes. - Do not send email without explicit approval. - Use the configured sender account and sender identity for outbound workflows; do not hardcode personal branding into public distributions. ## Resources ### references/ - `references/report-format.md` — required report structure - `references/email-template.md` — default email draft shape and constraints - `references/value-first-outreach.md` — value-first / “free doughnut” outreach framing, scannability rules, and CTA guidance - `references/visual-delivery.md` — HTML email + one-page brief delivery guidance and approval-preview checklist - `references/follow-up-sequence.md` — optional follow-up templates and cadence after the initial brief send ### scripts/ - `scripts/render_outreach_report.py` — render markdown report from structured JSON - `scripts/render_outreach_report_html.py` — render polished HTML one-page brief from structured JSON - `scripts/render_outreach_report_pdf.sh` — convert the HTML brief into a client-facing PDF artifact - `scripts/render_outreach_email.py` — render plain-text outreach email from structured JSON - `scripts/render_outreach_email_html.py` — render lightweight HTML outreach email from structured JSON - `scripts/render_outreach_preview.py` — render approval preview manifest for the actual outbound package - `scripts/send_outreach_package.sh` — send HTML email plus attached brief via Gmail after approval - `scripts/config.example.json` — example config with sender account and output defaults

path: outreach-demo/references/email-template.md
content: # Outreach Demo Email Template

Default to value-first outreach.

Lead with the finished mini-asset, not the meeting ask.

Subject options:
- I made this 1-page brief for {{business_name}}
- I put together 3 ideas for {{business_name}}
- Quick OpenClaw brief for {{business_name}}

Plain-text body pattern:

Hi {{contact_name}},

I pulled together a short **OpenClaw Opportunity Brief** for {{business_name}}.

One thing that stood out: {{specific_observation}}.

Inside:
- 3 concrete OpenClaw opportunities
- the priority move I would test first
- a simple live-demo angle

Attached if you want to skim it. If useful, I can walk through it live in 10 minutes.

Best,
{{sender_name}}
{{sender_brand_line}}
{{sender_email}}

Rules:
- Keep under ~170 words.
- Prefer short paragraphs and 3 bullets max.
- Make the value feel concrete: brief, teardown, mini-audit, or 3 ideas.
- No hype.
- No unverifiable claims.
- Ask for a light next step, not a heavy commitment.
- Draft first; send only after explicit user approval.
- If HTML is allowed, keep styling light and mobile-readable.


path: outreach-demo/references/follow-up-sequence.md
content: # Follow-Up Sequence

Use only after explicit user approval.

## Follow-up 1
Send 2 to 4 business days later.

Pattern:
- mention one additional observed opportunity
- keep it under 90 words
- ask a soft question

Template:

Subject: Re: {{subject}}

Hi {{contact_name}},

One additional thing I noticed while looking at {{business_name}}: {{additional_observation}}.

If useful, I can send a revised brief or walk through the priority recommendation live.

Best,
{{sender_name}}
{{sender_email}}

## Follow-up 2
Send 4 to 7 business days after follow-up 1.

Pattern:
- short close-the-loop note
- no pressure

Template:

Subject: Re: {{subject}}

Hi {{contact_name}},

Closing the loop on the brief I sent for {{business_name}}.

If this is not a priority right now, no problem. If useful later, I can revisit it with updated observations.

Best,
{{sender_name}}
{{sender_email}}


path: outreach-demo/references/report-format.md
content: # Outreach Demo Report Format

Use this structure for each prospect report.

## 1. Business snapshot
- Business name
- Website
- What they appear to do
- Likely audience

## 2. Website observations
- clear strengths
- unclear/missing signals
- operational/marketing/service observations grounded in visible evidence

## 3. OpenClaw fit
List up to 3 concrete ways OpenClaw could help.
For each:
- use case name
- why it fits this business
- likely business value
- lowest-friction starting point

## 4. Priority move
Recommend the single first move you would prioritize.
Include:
- what to do first
- why it should come first
- expected benefit

## 5. Why it matters
Frame the business consequence without inventing numbers.
Examples:
- saves owner time
- reduces slow follow-up
- catches regressions faster
- improves lead-response consistency

## 6. Suggested demo angle
Recommend one specific live demo angle that would resonate with this business.

## 7. Outreach summary
Two to four sentences suitable for email body reuse.

Rules:
- Do not claim internal business pain unless visibly supported.
- Phrase uncertain findings as likely or apparent.
- Prefer evidence from homepage/about/services/contact pages.
- Keep total report concise and decision-useful.


path: outreach-demo/references/value-first-outreach.md
content: # Value-First Outreach Mode

Use the "free doughnut" framing:
- give a small concrete asset before asking for time
- make the value easy to picture
- reduce the ask to a light next step

## Core pattern

1. Lead with the finished asset
   - "I put together a short 1-page brief for {{business_name}}"
   - "I pulled together 3 ideas for {{business_name}}"

2. Show what is inside
   - quick business summary
   - 3 tailored opportunities
   - one lowest-friction starting point

3. Make the CTA soft
   - "If useful, I can send it over"
   - "If useful, I can walk through it in 10 minutes"

## Subject line pattern

Prefer curiosity + specificity:
- I put together 3 ideas for {{business_name}}
- Quick 1-page teardown for {{business_name}}
- A short automation brief for {{business_name}}

## Visual/scannability rules

- Keep the email under 170 words.
- Use short paragraphs.
- Prefer 3 bullets max.
- Make the asset feel concrete: brief, teardown, mini-audit, 3 ideas.
- Avoid hype words like revolutionary, game-changing, or guaranteed.

## Plain-text layout

Hi {{contact_name}},

I took a quick look at {{business_name}} and put together a short 1-page brief with a few concrete ways OpenClaw could help.

It includes:
- a quick summary of what the business appears to do
- 3 practical automation opportunities
- the lowest-friction starting point I would test first

If useful, I can send it over—or walk through it live in 10 minutes.

Best,
Alex
[REDACTED_SECRET]

## Optional HTML layout

If the workflow supports HTML email, use:
- one short intro paragraph
- a 3-bullet block
- one low-friction CTA
- no heavy styling; keep it clean and mobile-readable


path: outreach-demo/references/visual-delivery.md
content: # Visual Delivery Mode

Use this mode when the outreach needs to feel like a mini-package rather than a plain note.

## Preferred delivery stack

1. Short HTML email body
2. Attached one-page brief as PDF
3. Approval preview before send

Fallback:
- HTML brief attachment when PDF rendering is unavailable

## What creates visual punch

- short intro paragraph
- 3-item bullet block
- highlighted top fit
- clean one-page brief with section cards
- enough whitespace to scan on mobile

## Avoid

- heavy branding
- image-heavy layouts
- salesy gradients/buttons
- long walls of copy

## Approval preview checklist

Before sending, show or summarize:
- subject line
- plain-text fallback body
- HTML body preview
- attached brief filename
- recipient

## Best default sequence

- generate structured JSON
- render markdown report
- render HTML brief from the same JSON
- render plain-text email
- render HTML email
- show preview
- wait for explicit approval


path: outreach-demo/scripts/config.example.json
content: senderAccount: [REDACTED_SECRET]
senderName: Your Name
senderTitle: OpenClaw assistant
senderBrandLine: Built by Your Company · OpenClaw assistant
draftModeDefault: yes
approvalRequiredForSend: yes
defaultEmailMode: html+brief
defaultBriefFormat: pdf
reportOutputDir: /mnt/data/clawd/reports/outreach-demo
maxOpenClawFits: 3

path: outreach-demo/scripts/load_sender_config.py
content: #!/usr/bin/env python3
import json
import os
from pathlib import Path


def load_sender_config():
    cfg_path = os.environ.get('OUTREACH_DEMO_CONFIG')
    cfg = {}
    if cfg_path and Path(cfg_path).exists():
        cfg = json.loads(Path(cfg_path).read_text(encoding='utf-8'))
    return {
        'senderAccount': os.environ.get('OUTREACH_SENDER_EMAIL') or cfg.get('senderAccount') or '[REDACTED_SECRET]',
        'senderName': os.environ.get('OUTREACH_SENDER_NAME') or cfg.get('senderName') or 'Your Name',
        'senderTitle': os.environ.get('OUTREACH_SENDER_TITLE') or cfg.get('senderTitle') or 'OpenClaw assistant',
        'senderBrandLine': os.environ.get('OUTREACH_SENDER_BRAND_LINE') or cfg.get('senderBrandLine') or 'Built by Your Company · OpenClaw assistant',
    }


path: outreach-demo/scripts/render_outreach_email.py
content: #!/usr/bin/env python3
import argparse
import json
from pathlib import Path
from load_sender_config import load_sender_config


def main():
    ap = argparse.ArgumentParser(description='Render outreach email draft from structured JSON.')
    ap.add_argument('--input', required=True)
    ap.add_argument('--output', required=True)
    args = ap.parse_args()

    data = json.loads(Path(args.input).read_text(encoding='utf-8'))
    sender = load_sender_config()
    top = ''
    fits = data.get('openclaw_fits', [])
    if fits:
        top = fits[0].get('use_case', '')
    contact = data.get('contact_name') or 'there'
    business = data.get('business_name', '')
    business_line = business if str(business).rstrip().endswith(('.', '!', '?')) else f"{business}."
    observations = data.get('website_observations', [])
    observation = observations[0] if observations else 'I found a few concrete workflow opportunities worth testing.'

    body = (
        f"Hi {contact},\n\n"
        f"I pulled together a short OpenClaw Opportunity Brief for {business_line}\n\n"
        f"One thing that stood out: {observation}\n\n"
        "Inside:\n"
        "- 3 concrete OpenClaw opportunities\n"
        f"- the priority move I would test first: {top}\n"
        "- a simple live-demo angle\n\n"
        "Attached if you want to skim it. If useful, I can walk through it live in 10 minutes.\n\n"
        "Best,\n"
        f"{sender['senderName']}\n"
        f"{sender['senderBrandLine']}\n"
        f"{sender['senderAccount']}\n"
    )
    Path(args.output).write_text(body, encoding='utf-8')


if __name__ == '__main__':
    main()


path: outreach-demo/scripts/render_outreach_email_html.py
content: #!/usr/bin/env python3
import argparse
import html
import json
from pathlib import Path
from load_sender_config import load_sender_config


def main():
    ap = argparse.ArgumentParser(description='Render lightweight HTML outreach email from structured JSON.')
    ap.add_argument('--input', required=True)
    ap.add_argument('--output', required=True)
    args = ap.parse_args()

    data = json.loads(Path(args.input).read_text(encoding='utf-8'))
    sender = load_sender_config()
    contact = html.escape(data.get('contact_name') or 'there')
    business = html.escape(data.get('business_name', ''))
    top = ''
    fits = data.get('openclaw_fits', [])
    if fits:
        top = html.escape(fits[0].get('use_case', ''))
    observations = data.get('website_observations', [])
    observation = html.escape(observations[0] if observations else 'I found a few concrete workflow opportunities worth testing.')
    body = f'''<!doctype html>
<html>
<body style="margin:0;padding:24px;background:#F8FAFC;font-family:Arial,Helvetica,sans-serif;color:#111827;">
  <div style="max-width:640px;margin:0 auto;background:#FFFFFF;border:1px solid #E5E7EB;border-radius:14px;overflow:hidden;box-shadow:0 8px 24px rgba(15,23,42,0.06);">
    <div style="padding:22px 24px;background:#0F172A;color:#FFFFFF;">
      <div style="font-size:13px;letter-spacing:.08em;text-transform:uppercase;color:#DBEAFE;font-weight:700;">OpenClaw Opportunity Brief</div>
      <div style="font-size:24px;font-weight:700;margin-top:6px;">{business}</div>
    </div>
    <div style="padding:24px;">
      <p style="margin:0 0 14px 0;">Hi {contact},</p>
      <p style="margin:0 0 14px 0;line-height:1.55;">I pulled together a short <strong>OpenClaw Opportunity Brief</strong> for <strong>{business}</strong>.</p>
      <div style="margin:0 0 16px 0;padding:14px 16px;background:#F8FAFC;border:1px solid #E5E7EB;border-radius:10px;line-height:1.55;">
        <strong>One thing that stood out:</strong><br>{observation}
      </div>
      <div style="margin:0 0 16px 0;padding:16px;background:#DBEAFE;border:1px solid #93C5FD;border-radius:12px;">
        <div style="font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:#2563EB;margin-bottom:10px;">Inside the brief</div>
        <ul style="margin:0;padding-left:18px;line-height:1.6;">
          <li>3 concrete OpenClaw opportunities</li>
          <li><strong>Priority move:</strong> {top}</li>
          <li>a simple live-demo angle</li>
        </ul>
      </div>
      <p style="margin:0 0 18px 0;line-height:1.55;">Attached if you want to skim it. If useful, I can walk through it live in 10 minutes.</p>
      <p style="margin:0;line-height:1.6;">Best,<br>{html.escape(sender['senderName'])}<br><span style="color:#6B7280;">{html.escape(sender['senderBrandLine'])}</span><br><span style="color:#6B7280;">{html.escape(sender['senderAccount'])}</span></p>
    </div>
  </div>
</body>
</html>
'''
    Path(args.output).write_text(body, encoding='utf-8')


if __name__ == '__main__':
    main()


path: outreach-demo/scripts/render_outreach_preview.py
content: #!/usr/bin/env python3
import argparse
import json
from pathlib import Path


def main():
    ap = argparse.ArgumentParser(description='Render approval preview manifest for outreach-demo artifacts.')
    ap.add_argument('--input', required=True)
    ap.add_argument('--subject', required=True)
    ap.add_argument('--email-text', required=True)
    ap.add_argument('--email-html', required=True)
    ap.add_argument('--brief', required=True)
    ap.add_argument('--output', required=True)
    args = ap.parse_args()

    data = json.loads(Path(args.input).read_text(encoding='utf-8'))
    preview = {
        'recipient': data.get('contact_email', ''),
        'contact_name': data.get('contact_name', ''),
        'business_name': data.get('business_name', ''),
        'subject': args.subject,
        'plainTextEmailPath': args.email_text,
        'htmlEmailPath': args.email_html,
        'attachedBriefPath': args.brief,
        'attachedBriefFilename': Path(args.brief).name,
        'sendMode': 'html+brief',
    }
    Path(args.output).write_text(json.dumps(preview, indent=2) + '\n', encoding='utf-8')


if __name__ == '__main__':
    main()


path: outreach-demo/scripts/render_outreach_report.py
content: #!/usr/bin/env python3
import argparse
import json
from pathlib import Path


def main():
    ap = argparse.ArgumentParser(description='Render outreach demo report from structured JSON.')
    ap.add_argument('--input', required=True)
    ap.add_argument('--output', required=True)
    args = ap.parse_args()

    data = json.loads(Path(args.input).read_text(encoding='utf-8'))
    lines = []
    lines.append(f"# Outreach Demo Report — {data.get('business_name','Unknown')}\n")
    lines.append('## 1. Business snapshot')
    lines.append(f"- Business name: {data.get('business_name','')}")
    lines.append(f"- Website: {data.get('website_url','')}")
    lines.append(f"- What they appear to do: {data.get('business_summary','')}")
    lines.append(f"- Likely audience: {data.get('likely_audience','')}\n")

    lines.append('## 2. Website observations')
    for item in data.get('website_observations', []):
        lines.append(f"- {item}")
    lines.append('')

    lines.append('## 3. OpenClaw fit')
    for fit in data.get('openclaw_fits', []):
        lines.append(f"### {fit.get('use_case','Use case')}")
        lines.append(f"- Why it fits: {fit.get('why_it_fits','')}")
        lines.append(f"- Likely value: {fit.get('likely_value','')}")
        lines.append(f"- Lowest-friction start: {fit.get('starting_point','')}\n")

    lines.append('## 4. Suggested demo angle')
    lines.append(data.get('demo_angle',''))
    lines.append('')
    lines.append('## 5. Outreach summary')
    lines.append(data.get('outreach_summary',''))
    lines.append('')

    Path(args.output).write_text('\n'.join(lines).rstrip() + '\n', encoding='utf-8')


if __name__ == '__main__':
    main()


path: outreach-demo/scripts/render_outreach_report_html.py
content: #!/usr/bin/env python3
import argparse
import html
import json
from pathlib import Path
from load_sender_config import load_sender_config

CSS = """
body { font-family: Arial, Helvetica, sans-serif; color: #111827; margin: 0; padding: 32px; background: #F8FAFC; }
.wrap { max-width: 820px; margin: 0 auto; background: #FFFFFF; border: 1px solid #E5E7EB; border-radius: 14px; overflow: hidden; box-shadow: 0 12px 30px rgba(15,23,42,0.06); }
.hero { padding: 28px 32px; background: #0F172A; color: #FFFFFF; }
.hero h1 { margin: 0 0 8px 0; font-size: 28px; }
.hero p { margin: 0; color: #DBEAFE; font-size: 14px; }
.section { padding: 24px 32px; border-top: 1px solid #E5E7EB; }
.section h2 { margin: 0 0 14px 0; font-size: 18px; color: #111827; }
.card { background: #F8FAFC; border: 1px solid #E5E7EB; border-radius: 10px; padding: 16px; margin: 12px 0; }
.kv { margin: 6px 0; }
.label { font-weight: 700; }
ul { margin: 10px 0 0 18px; padding: 0; }
li { margin: 8px 0; }
.fit { border-left: 4px solid #2563EB; }
.topfit { display: inline-block; background: #DBEAFE; color: #2563EB; font-weight: 700; font-size: 12px; padding: 6px 10px; border-radius: 999px; margin-bottom: 10px; }
.footer { padding: 20px 32px 28px 32px; color: #6B7280; font-size: 13px; border-top: 1px solid #E5E7EB; }
"""


def esc(v):
    return html.escape(str(v or ''))


def main():
    ap = argparse.ArgumentParser(description='Render polished HTML outreach brief from structured JSON.')
    ap.add_argument('--input', required=True)
    ap.add_argument('--output', required=True)
    args = ap.parse_args()

    data = json.loads(Path(args.input).read_text(encoding='utf-8'))
    sender = load_sender_config()
    fits = data.get('openclaw_fits', [])
    fit_html = []
    priority_move = ''
    priority_why = ''
    priority_benefit = ''
    priority = data.get('priority_move') or {}
    if isinstance(priority, dict):
        priority_move = priority.get('move', '')
        priority_why = priority.get('why_first', '')
        priority_benefit = priority.get('expected_benefit', '')
    if not priority_move and fits:
        priority_move = fits[0].get('use_case', '')
        priority_why = fits[0].get('why_it_fits', '')
        priority_benefit = fits[0].get('likely_value', '')
    why_it_matters = data.get('why_it_matters', [])
    for i, fit in enumerate(fits):
        badge = '<div class="topfit">Top fit</div>' if i == 0 else ''
        fit_html.append(f'''<div class="card fit">{badge}
<h3>{esc(fit.get('use_case','Use case'))}</h3>
<div class="kv"><span class="label">Why it fits:</span> {esc(fit.get('why_it_fits',''))}</div>
<div class="kv"><span class="label">Likely value:</span> {esc(fit.get('likely_value',''))}</div>
<div class="kv"><span class="label">Lowest-friction start:</span> {esc(fit.get('starting_point',''))}</div>
</div>''')

    obs = ''.join(f'<li>{esc(item)}</li>' for item in data.get('website_observations', [])) or '<li>No public observations captured.</li>'
    matters = ''.join(f'<li>{esc(item)}</li>' for item in why_it_matters) or '<li>No business consequence notes captured.</li>'
    html_doc = f'''<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Outreach Brief — {esc(data.get('business_name','Unknown'))}</title>
<style>{CSS}</style>
</head>
<body>
<div class="wrap">
  <div class="hero">
    <h1>{esc(data.get('business_name','Unknown'))}</h1>
    <p>Short outreach brief prepared from public website signals and tailored OpenClaw fit analysis.</p>
  </div>
  <div class="section">
    <h2>Business snapshot</h2>
    <div class="card">
      <div class="kv"><span class="label">Website:</span> {esc(data.get('website_url',''))}</div>
      <div class="kv"><span class="label">What they appear to do:</span> {esc(data.get('business_summary',''))}</div>
      <div class="kv"><span class="label">Likely audience:</span> {esc(data.get('likely_audience',''))}</div>
    </div>
  </div>
  <div class="section">
    <h2>Website observations</h2>
    <div class="card"><ul>{obs}</ul></div>
  </div>
  <div class="section">
    <h2>OpenClaw fit</h2>
    {''.join(fit_html)}
  </div>
  <div class="section">
    <h2>Priority move</h2>
    <div class="card fit"><div class="topfit">Start here</div>
      <div class="kv"><span class="label">Move:</span> {esc(priority_move)}</div>
      <div class="kv"><span class="label">Why first:</span> {esc(priority_why)}</div>
      <div class="kv"><span class="label">Expected benefit:</span> {esc(priority_benefit)}</div>
    </div>
  </div>
  <div class="section">
    <h2>Why it matters</h2>
    <div class="card"><ul>{matters}</ul></div>
  </div>
  <div class="section">
    <h2>Suggested demo angle</h2>
    <div class="card">{esc(data.get('demo_angle',''))}</div>
  </div>
  <div class="section">
    <h2>Outreach summary</h2>
    <div class="card">{esc(data.get('outreach_summary',''))}</div>
  </div>
  <div class="footer">Prepared for outreach-demo · OpenClaw Opportunity Brief · {esc(sender['senderBrandLine'])}</div>
</div>
</body>
</html>
'''
    Path(args.output).write_text(html_doc, encoding='utf-8')


if __name__ == '__main__':
    main()


path: outreach-demo/scripts/render_outreach_report_pdf.sh
content: #!/usr/bin/env bash
set -euo pipefail

usage() {
  cat <<EOF
Usage: $0 --input <brief.html> --output <brief.pdf>
EOF
}

INPUT=""
OUTPUT=""
CHROME_BIN="${CHROME_BIN:-}"

while [[ $# -gt 0 ]]; do
  case "$1" in
    --input) INPUT="$2"; shift 2 ;;
    --output) OUTPUT="$2"; shift 2 ;;
    *) usage; exit 2 ;;
  esac
done

[[ -n "$INPUT" && -n "$OUTPUT" ]] || { usage; exit 2; }
[[ -f "$INPUT" ]] || { echo "missing input html" >&2; exit 2; }

if [[ -z "$CHROME_BIN" ]]; then
  if command -v chromium >/dev/null 2>&1; then CHROME_BIN="$(command -v chromium)";
  elif command -v chromium-browser >/dev/null 2>&1; then CHROME_BIN="$(command -v chromium-browser)";
  else echo "chromium not found" >&2; exit 2; fi
fi

INPUT_URL="file://$INPUT"
"$CHROME_BIN" \
  --headless=new \
  --disable-gpu \
  --no-sandbox \
  --print-to-pdf="$OUTPUT" \
  "$INPUT_URL" >/dev/null 2>&1

test -f "$OUTPUT" || { echo "pdf render failed" >&2; exit 1; }


path: outreach-demo/scripts/send_outreach_package.sh
content: #!/usr/bin/env bash
set -euo pipefail

usage() {
  cat <<EOF
Usage: $0 --to <email> --subject <subject> --text <body.txt> --html <body.html> --attach <brief.html> [--account <gmail>]
EOF
}

ACCOUNT="[REDACTED_SECRET]"
TO=""
SUBJECT=""
TEXT=""
HTML=""
ATTACH=""
DRY_RUN=0

while [[ $# -gt 0 ]]; do
  case "$1" in
    --account) ACCOUNT="$2"; shift 2 ;;
    --to) TO="$2"; shift 2 ;;
    --subject) SUBJECT="$2"; shift 2 ;;
    --text) TEXT="$2"; shift 2 ;;
    --html) HTML="$2"; shift 2 ;;
    --attach) ATTACH="$2"; shift 2 ;;
    --dry-run) DRY_RUN=1; shift ;;
    *) usage; exit 2 ;;
  esac
done

[[ -n "$TO" && -n "$SUBJECT" && -n "$TEXT" && -n "$HTML" && -n "$ATTACH" ]] || { usage; exit 2; }
[[ -f "$TEXT" && -f "$HTML" && -f "$ATTACH" ]] || { echo "missing input artifact" >&2; exit 2; }

HTML_BODY=$(cat "$HTML")
CMD=(gog gmail send
  --to "$TO"
  --subject "$SUBJECT"
  --body-file "$TEXT"
  --body-html "$HTML_BODY"
  --attach "$ATTACH")

if [[ "$DRY_RUN" -eq 1 ]]; then
  CMD+=(--dry-run)
fi

GOG_ACCOUNT="$ACCOUNT" "${CMD[@]}"


path: references/email-template.md
content: # Outreach Demo Email Template

Default to value-first outreach.

Lead with the finished mini-asset, not the meeting ask.

Subject options:
- I made this 1-page brief for {{business_name}}
- I put together 3 ideas for {{business_name}}
- Quick OpenClaw brief for {{business_name}}

Plain-text body pattern:

Hi {{contact_name}},

I pulled together a short **OpenClaw Opportunity Brief** for {{business_name}}.

One thing that stood out: {{specific_observation}}.

Inside:
- 3 concrete OpenClaw opportunities
- the priority move I would test first
- a simple live-demo angle

Attached if you want to skim it. If useful, I can walk through it live in 10 minutes.

Best,
{{sender_name}}
{{sender_brand_line}}
{{sender_email}}

Rules:
- Keep under ~170 words.
- Prefer short paragraphs and 3 bullets max.
- Make the value feel concrete: brief, teardown, mini-audit, or 3 ideas.
- No hype.
- No unverifiable claims.
- Ask for a light next step, not a heavy commitment.
- Draft first; send only after explicit user approval.
- If HTML is allowed, keep styling light and mobile-readable.


path: references/follow-up-sequence.md
content: # Follow-Up Sequence

Use only after explicit user approval.

## Follow-up 1
Send 2 to 4 business days later.

Pattern:
- mention one additional observed opportunity
- keep it under 90 words
- ask a soft question

Template:

Subject: Re: {{subject}}

Hi {{contact_name}},

One additional thing I noticed while looking at {{business_name}}: {{additional_observation}}.

If useful, I can send a revised brief or walk through the priority recommendation live.

Best,
{{sender_name}}
{{sender_email}}

## Follow-up 2
Send 4 to 7 business days after follow-up 1.

Pattern:
- short close-the-loop note
- no pressure

Template:

Subject: Re: {{subject}}

Hi {{contact_name}},

Closing the loop on the brief I sent for {{business_name}}.

If this is not a priority right now, no problem. If useful later, I can revisit it with updated observations.

Best,
{{sender_name}}
{{sender_email}}


path: references/report-format.md
content: # Outreach Demo Report Format

Use this structure for each prospect report.

## 1. Business snapshot
- Business name
- Website
- What they appear to do
- Likely audience

## 2. Website observations
- clear strengths
- unclear/missing signals
- operational/marketing/service observations grounded in visible evidence

## 3. OpenClaw fit
List up to 3 concrete ways OpenClaw could help.
For each:
- use case name
- why it fits this business
- likely business value
- lowest-friction starting point

## 4. Priority move
Recommend the single first move you would prioritize.
Include:
- what to do first
- why it should come first
- expected benefit

## 5. Why it matters
Frame the business consequence without inventing numbers.
Examples:
- saves owner time
- reduces slow follow-up
- catches regressions faster
- improves lead-response consistency

## 6. Suggested demo angle
Recommend one specific live demo angle that would resonate with this business.

## 7. Outreach summary
Two to four sentences suitable for email body reuse.

Rules:
- Do not claim internal business pain unless visibly supported.
- Phrase uncertain findings as likely or apparent.
- Prefer evidence from homepage/about/services/contact pages.
- Keep total report concise and decision-useful.


path: references/value-first-outreach.md
content: # Value-First Outreach Mode

Use the "free doughnut" framing:
- give a small concrete asset before asking for time
- make the value easy to picture
- reduce the ask to a light next step

## Core pattern

1. Lead with the finished asset
   - "I put together a short 1-page brief for {{business_name}}"
   - "I pulled together 3 ideas for {{business_name}}"

2. Show what is inside
   - quick business summary
   - 3 tailored opportunities
   - one lowest-friction starting point

3. Make the CTA soft
   - "If useful, I can send it over"
   - "If useful, I can walk through it in 10 minutes"

## Subject line pattern

Prefer curiosity + specificity:
- I put together 3 ideas for {{business_name}}
- Quick 1-page teardown for {{business_name}}
- A short automation brief for {{business_name}}

## Visual/scannability rules

- Keep the email under 170 words.
- Use short paragraphs.
- Prefer 3 bullets max.
- Make the asset feel concrete: brief, teardown, mini-audit, 3 ideas.
- Avoid hype words like revolutionary, game-changing, or guaranteed.

## Plain-text layout

Hi {{contact_name}},

I took a quick look at {{business_name}} and put together a short 1-page brief with a few concrete ways OpenClaw could help.

It includes:
- a quick summary of what the business appears to do
- 3 practical automation opportunities
- the lowest-friction starting point I would test first

If useful, I can send it over—or walk through it live in 10 minutes.

Best,
Alex
[REDACTED_SECRET]

## Optional HTML layout

If the workflow supports HTML email, use:
- one short intro paragraph
- a 3-bullet block
- one low-friction CTA
- no heavy styling; keep it clean and mobile-readable


path: references/visual-delivery.md
content: # Visual Delivery Mode

Use this mode when the outreach needs to feel like a mini-package rather than a plain note.

## Preferred delivery stack

1. Short HTML email body
2. Attached one-page brief as PDF
3. Approval preview before send

Fallback:
- HTML brief attachment when PDF rendering is unavailable

## What creates visual punch

- short intro paragraph
- 3-item bullet block
- highlighted top fit
- clean one-page brief with section cards
- enough whitespace to scan on mobile

## Avoid

- heavy branding
- image-heavy layouts
- salesy gradients/buttons
- long walls of copy

## Approval preview checklist

Before sending, show or summarize:
- subject line
- plain-text fallback body
- HTML body preview
- attached brief filename
- recipient

## Best default sequence

- generate structured JSON
- render markdown report
- render HTML brief from the same JSON
- render plain-text email
- render HTML email
- show preview
- wait for explicit approval


path: scripts/config.example.json
content: senderAccount: [REDACTED_SECRET]
senderName: Your Name
senderTitle: OpenClaw assistant
senderBrandLine: Built by Your Company · OpenClaw assistant
draftModeDefault: yes
approvalRequiredForSend: yes
defaultEmailMode: html+brief
defaultBriefFormat: pdf
reportOutputDir: /mnt/data/clawd/reports/outreach-demo
maxOpenClawFits: 3

path: scripts/load_sender_config.py
content: #!/usr/bin/env python3
import json
import os
from pathlib import Path


def load_sender_config():
    cfg_path = os.environ.get('OUTREACH_DEMO_CONFIG')
    cfg = {}
    if cfg_path and Path(cfg_path).exists():
        cfg = json.loads(Path(cfg_path).read_text(encoding='utf-8'))
    return {
        'senderAccount': os.environ.get('OUTREACH_SENDER_EMAIL') or cfg.get('senderAccount') or '[REDACTED_SECRET]',
        'senderName': os.environ.get('OUTREACH_SENDER_NAME') or cfg.get('senderName') or 'Your Name',
        'senderTitle': os.environ.get('OUTREACH_SENDER_TITLE') or cfg.get('senderTitle') or 'OpenClaw assistant',
        'senderBrandLine': os.environ.get('OUTREACH_SENDER_BRAND_LINE') or cfg.get('senderBrandLine') or 'Built by Your Company · OpenClaw assistant',
    }


path: scripts/render_outreach_email.py
content: #!/usr/bin/env python3
import argparse
import json
from pathlib import Path
from load_sender_config import load_sender_config


def main():
    ap = argparse.ArgumentParser(description='Render outreach email draft from structured JSON.')
    ap.add_argument('--input', required=True)
    ap.add_argument('--output', required=True)
    args = ap.parse_args()

    data = json.loads(Path(args.input).read_text(encoding='utf-8'))
    sender = load_sender_config()
    top = ''
    fits = data.get('openclaw_fits', [])
    if fits:
        top = fits[0].get('use_case', '')
    contact = data.get('contact_name') or 'there'
    business = data.get('business_name', '')
    business_line = business if str(business).rstrip().endswith(('.', '!', '?')) else f"{business}."
    observations = data.get('website_observations', [])
    observation = observations[0] if observations else 'I found a few concrete workflow opportunities worth testing.'

    body = (
        f"Hi {contact},\n\n"
        f"I pulled together a short OpenClaw Opportunity Brief for {business_line}\n\n"
        f"One thing that stood out: {observation}\n\n"
        "Inside:\n"
        "- 3 concrete OpenClaw opportunities\n"
        f"- the priority move I would test first: {top}\n"
        "- a simple live-demo angle\n\n"
        "Attached if you want to skim it. If useful, I can walk through it live in 10 minutes.\n\n"
        "Best,\n"
        f"{sender['senderName']}\n"
        f"{sender['senderBrandLine']}\n"
        f"{sender['senderAccount']}\n"
    )
    Path(args.output).write_text(body, encoding='utf-8')


if __name__ == '__main__':
    main()


path: scripts/render_outreach_email_html.py
content: #!/usr/bin/env python3
import argparse
import html
import json
from pathlib import Path
from load_sender_config import load_sender_config


def main():
    ap = argparse.ArgumentParser(description='Render lightweight HTML outreach email from structured JSON.')
    ap.add_argument('--input', required=True)
    ap.add_argument('--output', required=True)
    args = ap.parse_args()

    data = json.loads(Path(args.input).read_text(encoding='utf-8'))
    sender = load_sender_config()
    contact = html.escape(data.get('contact_name') or 'there')
    business = html.escape(data.get('business_name', ''))
    top = ''
    fits = data.get('openclaw_fits', [])
    if fits:
        top = html.escape(fits[0].get('use_case', ''))
    observations = data.get('website_observations', [])
    observation = html.escape(observations[0] if observations else 'I found a few concrete workflow opportunities worth testing.')
    body = f'''<!doctype html>
<html>
<body style="margin:0;padding:24px;background:#F8FAFC;font-family:Arial,Helvetica,sans-serif;color:#111827;">
  <div style="max-width:640px;margin:0 auto;background:#FFFFFF;border:1px solid #E5E7EB;border-radius:14px;overflow:hidden;box-shadow:0 8px 24px rgba(15,23,42,0.06);">
    <div style="padding:22px 24px;background:#0F172A;color:#FFFFFF;">
      <div style="font-size:13px;letter-spacing:.08em;text-transform:uppercase;color:#DBEAFE;font-weight:700;">OpenClaw Opportunity Brief</div>
      <div style="font-size:24px;font-weight:700;margin-top:6px;">{business}</div>
    </div>
    <div style="padding:24px;">
      <p style="margin:0 0 14px 0;">Hi {contact},</p>
      <p style="margin:0 0 14px 0;line-height:1.55;">I pulled together a short <strong>OpenClaw Opportunity Brief</strong> for <strong>{business}</strong>.</p>
      <div style="margin:0 0 16px 0;padding:14px 16px;background:#F8FAFC;border:1px solid #E5E7EB;border-radius:10px;line-height:1.55;">
        <strong>One thing that stood out:</strong><br>{observation}
      </div>
      <div style="margin:0 0 16px 0;padding:16px;background:#DBEAFE;border:1px solid #93C5FD;border-radius:12px;">
        <div style="font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:#2563EB;margin-bottom:10px;">Inside the brief</div>
        <ul style="margin:0;padding-left:18px;line-height:1.6;">
          <li>3 concrete OpenClaw opportunities</li>
          <li><strong>Priority move:</strong> {top}</li>
          <li>a simple live-demo angle</li>
        </ul>
      </div>
      <p style="margin:0 0 18px 0;line-height:1.55;">Attached if you want to skim it. If useful, I can walk through it live in 10 minutes.</p>
      <p style="margin:0;line-height:1.6;">Best,<br>{html.escape(sender['senderName'])}<br><span style="color:#6B7280;">{html.escape(sender['senderBrandLine'])}</span><br><span style="color:#6B7280;">{html.escape(sender['senderAccount'])}</span></p>
    </div>
  </div>
</body>
</html>
'''
    Path(args.output).write_text(body, encoding='utf-8')


if __name__ == '__main__':
    main()


path: scripts/render_outreach_preview.py
content: #!/usr/bin/env python3
import argparse
import json
from pathlib import Path


def main():
    ap = argparse.ArgumentParser(description='Render approval preview manifest for outreach-demo artifacts.')
    ap.add_argument('--input', required=True)
    ap.add_argument('--subject', required=True)
    ap.add_argument('--email-text', required=True)
    ap.add_argument('--email-html', required=True)
    ap.add_argument('--brief', required=True)
    ap.add_argument('--output', required=True)
    args = ap.parse_args()

    data = json.loads(Path(args.input).read_text(encoding='utf-8'))
    preview = {
        'recipient': data.get('contact_email', ''),
        'contact_name': data.get('contact_name', ''),
        'business_name': data.get('business_name', ''),
        'subject': args.subject,
        'plainTextEmailPath': args.email_text,
        'htmlEmailPath': args.email_html,
        'attachedBriefPath': args.brief,
        'attachedBriefFilename': Path(args.brief).name,
        'sendMode': 'html+brief',
    }
    Path(args.output).write_text(json.dumps(preview, indent=2) + '\n', encoding='utf-8')


if __name__ == '__main__':
    main()


path: scripts/render_outreach_report.py
content: #!/usr/bin/env python3
import argparse
import json
from pathlib import Path


def main():
    ap = argparse.ArgumentParser(description='Render outreach demo report from structured JSON.')
    ap.add_argument('--input', required=True)
    ap.add_argument('--output', required=True)
    args = ap.parse_args()

    data = json.loads(Path(args.input).read_text(encoding='utf-8'))
    lines = []
    lines.append(f"# Outreach Demo Report — {data.get('business_name','Unknown')}\n")
    lines.append('## 1. Business snapshot')
    lines.append(f"- Business name: {data.get('business_name','')}")
    lines.append(f"- Website: {data.get('website_url','')}")
    lines.append(f"- What they appear to do: {data.get('business_summary','')}")
    lines.append(f"- Likely audience: {data.get('likely_audience','')}\n")

    lines.append('## 2. Website observations')
    for item in data.get('website_observations', []):
        lines.append(f"- {item}")
    lines.append('')

    lines.append('## 3. OpenClaw fit')
    for fit in data.get('openclaw_fits', []):
        lines.append(f"### {fit.get('use_case','Use case')}")
        lines.append(f"- Why it fits: {fit.get('why_it_fits','')}")
        lines.append(f"- Likely value: {fit.get('likely_value','')}")
        lines.append(f"- Lowest-friction start: {fit.get('starting_point','')}\n")

    lines.append('## 4. Suggested demo angle')
    lines.append(data.get('demo_angle',''))
    lines.append('')
    lines.append('## 5. Outreach summary')
    lines.append(data.get('outreach_summary',''))
    lines.append('')

    Path(args.output).write_text('\n'.join(lines).rstrip() + '\n', encoding='utf-8')


if __name__ == '__main__':
    main()


path: scripts/render_outreach_report_html.py
content: #!/usr/bin/env python3
import argparse
import html
import json
from pathlib import Path
from load_sender_config import load_sender_config

CSS = """
body { font-family: Arial, Helvetica, sans-serif; color: #111827; margin: 0; padding: 32px; background: #F8FAFC; }
.wrap { max-width: 820px; margin: 0 auto; background: #FFFFFF; border: 1px solid #E5E7EB; border-radius: 14px; overflow: hidden; box-shadow: 0 12px 30px rgba(15,23,42,0.06); }
.hero { padding: 28px 32px; background: #0F172A; color: #FFFFFF; }
.hero h1 { margin: 0 0 8px 0; font-size: 28px; }
.hero p { margin: 0; color: #DBEAFE; font-size: 14px; }
.section { padding: 24px 32px; border-top: 1px solid #E5E7EB; }
.section h2 { margin: 0 0 14px 0; font-size: 18px; color: #111827; }
.card { background: #F8FAFC; border: 1px solid #E5E7EB; border-radius: 10px; padding: 16px; margin: 12px 0; }
.kv { margin: 6px 0; }
.label { font-weight: 700; }
ul { margin: 10px 0 0 18px; padding: 0; }
li { margin: 8px 0; }
.fit { border-left: 4px solid #2563EB; }
.topfit { display: inline-block; background: #DBEAFE; color: #2563EB; font-weight: 700; font-size: 12px; padding: 6px 10px; border-radius: 999px; margin-bottom: 10px; }
.footer { padding: 20px 32px 28px 32px; color: #6B7280; font-size: 13px; border-top: 1px solid #E5E7EB; }
"""


def esc(v):
    return html.escape(str(v or ''))


def main():
    ap = argparse.ArgumentParser(description='Render polished HTML outreach brief from structured JSON.')
    ap.add_argument('--input', required=True)
    ap.add_argument('--output', required=True)
    args = ap.parse_args()

    data = json.loads(Path(args.input).read_text(encoding='utf-8'))
    sender = load_sender_config()
    fits = data.get('openclaw_fits', [])
    fit_html = []
    priority_move = ''
    priority_why = ''
    priority_benefit = ''
    priority = data.get('priority_move') or {}
    if isinstance(priority, dict):
        priority_move = priority.get('move', '')
        priority_why = priority.get('why_first', '')
        priority_benefit = priority.get('expected_benefit', '')
    if not priority_move and fits:
        priority_move = fits[0].get('use_case', '')
        priority_why = fits[0].get('why_it_fits', '')
        priority_benefit = fits[0].get('likely_value', '')
    why_it_matters = data.get('why_it_matters', [])
    for i, fit in enumerate(fits):
        badge = '<div class="topfit">Top fit</div>' if i == 0 else ''
        fit_html.append(f'''<div class="card fit">{badge}
<h3>{esc(fit.get('use_case','Use case'))}</h3>
<div class="kv"><span class="label">Why it fits:</span> {esc(fit.get('why_it_fits',''))}</div>
<div class="kv"><span class="label">Likely value:</span> {esc(fit.get('likely_value',''))}</div>
<div class="kv"><span class="label">Lowest-friction start:</span> {esc(fit.get('starting_point',''))}</div>
</div>''')

    obs = ''.join(f'<li>{esc(item)}</li>' for item in data.get('website_observations', [])) or '<li>No public observations captured.</li>'
    matters = ''.join(f'<li>{esc(item)}</li>' for item in why_it_matters) or '<li>No business consequence notes captured.</li>'
    html_doc = f'''<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Outreach Brief — {esc(data.get('business_name','Unknown'))}</title>
<style>{CSS}</style>
</head>
<body>
<div class="wrap">
  <div class="hero">
    <h1>{esc(data.get('business_name','Unknown'))}</h1>
    <p>Short outreach brief prepared from public website signals and tailored OpenClaw fit analysis.</p>
  </div>
  <div class="section">
    <h2>Business snapshot</h2>
    <div class="card">
      <div class="kv"><span class="label">Website:</span> {esc(data.get('website_url',''))}</div>
      <div class="kv"><span class="label">What they appear to do:</span> {esc(data.get('business_summary',''))}</div>
      <div class="kv"><span class="label">Likely audience:</span> {esc(data.get('likely_audience',''))}</div>
    </div>
  </div>
  <div class="section">
    <h2>Website observations</h2>
    <div class="card"><ul>{obs}</ul></div>
  </div>
  <div class="section">
    <h2>OpenClaw fit</h2>
    {''.join(fit_html)}
  </div>
  <div class="section">
    <h2>Priority move</h2>
    <div class="card fit"><div class="topfit">Start here</div>
      <div class="kv"><span class="label">Move:</span> {esc(priority_move)}</div>
      <div class="kv"><span class="label">Why first:</span> {esc(priority_why)}</div>
      <div class="kv"><span class="label">Expected benefit:</span> {esc(priority_benefit)}</div>
    </div>
  </div>
  <div class="section">
    <h2>Why it matters</h2>
    <div class="card"><ul>{matters}</ul></div>
  </div>
  <div class="section">
    <h2>Suggested demo angle</h2>
    <div class="card">{esc(data.get('demo_angle',''))}</div>
  </div>
  <div class="section">
    <h2>Outreach summary</h2>
    <div class="card">{esc(data.get('outreach_summary',''))}</div>
  </div>
  <div class="footer">Prepared for outreach-demo · OpenClaw Opportunity Brief · {esc(sender['senderBrandLine'])}</div>
</div>
</body>
</html>
'''
    Path(args.output).write_text(html_doc, encoding='utf-8')


if __name__ == '__main__':
    main()


path: scripts/render_outreach_report_pdf.sh
content: #!/usr/bin/env bash
set -euo pipefail

usage() {
  cat <<EOF
Usage: $0 --input <brief.html> --output <brief.pdf>
EOF
}

INPUT=""
OUTPUT=""
CHROME_BIN="${CHROME_BIN:-}"

while [[ $# -gt 0 ]]; do
  case "$1" in
    --input) INPUT="$2"; shift 2 ;;
    --output) OUTPUT="$2"; shift 2 ;;
    *) usage; exit 2 ;;
  esac
done

[[ -n "$INPUT" && -n "$OUTPUT" ]] || { usage; exit 2; }
[[ -f "$INPUT" ]] || { echo "missing input html" >&2; exit 2; }

if [[ -z "$CHROME_BIN" ]]; then
  if command -v chromium >/dev/null 2>&1; then CHROME_BIN="$(command -v chromium)";
  elif command -v chromium-browser >/dev/null 2>&1; then CHROME_BIN="$(command -v chromium-browser)";
  else echo "chromium not found" >&2; exit 2; fi
fi

INPUT_URL="file://$INPUT"
"$CHROME_BIN" \
  --headless=new \
  --disable-gpu \
  --no-sandbox \
  --print-to-pdf="$OUTPUT" \
  "$INPUT_URL" >/dev/null 2>&1

test -f "$OUTPUT" || { echo "pdf render failed" >&2; exit 1; }


path: scripts/send_outreach_package.sh
content: #!/usr/bin/env bash
set -euo pipefail

usage() {
  cat <<EOF
Usage: $0 --to <email> --subject <subject> --text <body.txt> --html <body.html> --attach <brief.html> [--account <gmail>]
EOF
}

ACCOUNT="[REDACTED_SECRET]"
TO=""
SUBJECT=""
TEXT=""
HTML=""
ATTACH=""
DRY_RUN=0

while [[ $# -gt 0 ]]; do
  case "$1" in
    --account) ACCOUNT="$2"; shift 2 ;;
    --to) TO="$2"; shift 2 ;;
    --subject) SUBJECT="$2"; shift 2 ;;
    --text) TEXT="$2"; shift 2 ;;
    --html) HTML="$2"; shift 2 ;;
    --attach) ATTACH="$2"; shift 2 ;;
    --dry-run) DRY_RUN=1; shift ;;
    *) usage; exit 2 ;;
  esac
done

[[ -n "$TO" && -n "$SUBJECT" && -n "$TEXT" && -n "$HTML" && -n "$ATTACH" ]] || { usage; exit 2; }
[[ -f "$TEXT" && -f "$HTML" && -f "$ATTACH" ]] || { echo "missing input artifact" >&2; exit 2; }

HTML_BODY=$(cat "$HTML")
CMD=(gog gmail send
  --to "$TO"
  --subject "$SUBJECT"
  --body-file "$TEXT"
  --body-html "$HTML_BODY"
  --attach "$ATTACH")

if [[ "$DRY_RUN" -eq 1 ]]; then
  CMD+=(--dry-run)
fi

GOG_ACCOUNT="$ACCOUNT" "${CMD[@]}"


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)