---
name: smart-proposal
description: Use when someone asks to build a smart proposal, create a gated proposal for a client, generate a proposal with an access code, make a time-locked proposal, or build a standalone proposal without n8n or Supabase. Distributable to teammates.
disable-model-invocation: true
argument-hint: "[prospect name]"
---

# Smart Proposal

Generates a self-contained gated proposal HTML file: access code protected, time-locked (expires after N days), with mockup section and all content in one place. Pushes to a GitHub gist and delivers a hyperlinked htmlpreview URL. No backend required.

**Do NOT use this skill when:**
- You need the full Four D's chain (recon, diagnose, brief confirm, Console registration) → use `/build-proposal`
- You need a sealed bundle with Stripe payment + demo + deck → use `/proposal-contract`
- You need to diagnose before you have a scope → use `/scope-of-impact`

---

## Before Starting

1. Read the template from `.claude/skills/smart-proposal/template.html` — every `{{PLACEHOLDER}}` you will fill
2. Confirm `GITHUB_GIST_TOKEN` is set: `echo $GITHUB_GIST_TOKEN | cut -c1-8` — if empty, stop and ask the user to export it
3. Confirm `python3` is available: `python3 --version`

---

## Step 1: Intake

If the user invoked `/smart-proposal [prospect]`, that is `{{PROSPECT_NAME}}`. Skip asking for it.

Ask these questions (AskUserQuestion, all in one round):

1. **Prospect name** — company or person this proposal is for (if not in args)
2. **Scope summary** — one paragraph: what are you proposing to do? What problem does it solve?
3. **Mockup description** — describe what the product, UI, or system looks like. Include sections, layouts, or screens (e.g., "a dashboard with a left sidebar nav, main analytics area, and a quick-action panel"). If none, write "N/A" and skip the wireframe.
4. **Your name, company, and email** — shown on the proposal and in the reach-out message after expiry
5. **Access code** — suggest `PROP-[INITIALS][MONTH][YEAR]` (e.g., `PROP-TY0626`). User can override.
6. **Expiry days** — default 14. How many days until the proposal window closes?

---

## Step 2: Compute Dates

After intake, compute these values using Python:

```bash
python3 -c "
from datetime import datetime, timedelta
today = datetime.now()
expiry = today + timedelta(days={{EXPIRY_DAYS}})
print('GENERATED:', today.strftime('%B %d, %Y'))
print('EXPIRY_ISO:', expiry.strftime('%Y-%m-%dT23:59:59'))
print('EXPIRY_DISPLAY:', expiry.strftime('%B %d, %Y'))
"
```

---

## Step 3: Generate Proposal Content

Write all sections based on what the user provided. Follow these rules:

- **No hyphens, en dashes, or em dashes** anywhere in the text
- Pricing is always TBD. Never write a specific price. Use `{{INVESTMENT_DISPLAY}}` = `Contact for Pricing` and `{{INVESTMENT_NOTE}}` = the one-line reason (e.g., "Every engagement is scoped to fit your operation. Reach out to get exact numbers.")
- Keep paragraphs short. 2 to 3 sentences max each.
- Write from the perspective of the sender, not Ecrof unless sender IS Ecrof

**Content to write:**

`{{OVERVIEW_PARAGRAPHS}}` — 2 to 3 `<p>` tags. The "why now" and the core problem.

`{{SCOPE_PARAGRAPHS}}` — 2 to 3 `<p>` tags. What you are building and what it does.

`{{DESIGN_PARAGRAPHS}}` — 1 to 2 `<p>` tags. Describe the design direction, feel, and user experience before the wireframe.

`{{WIREFRAME_HTML}}` — Build CSS wireframe rows using `.wf-row` and `.wf-box` divs from the template. Use the mockup description to lay out sections as labeled boxes. If the user said "N/A", output a single centered box:
```html
<div class="wf-row" style="justify-content:center">
  <div class="wf-box" style="max-width:320px;justify-content:center">Design details shared in session</div>
</div>
```

`{{DELIVERABLES_HTML}}` — 4 to 6 `<li>` items, each with a `<span class="dot"></span>` and the deliverable text.

`{{TIMELINE_HTML}}` — 3 to 5 phases. Each is:
```html
<div class="tl-item">
  <div class="tl-label">Phase 1</div>
  <div class="tl-content">Discovery and setup (Week 1)</div>
</div>
```

`{{NEXT_STEPS_TEXT}}` — one short paragraph explaining what happens when the prospect reaches out.

`{{PROPOSAL_TITLE}}` — auto-generate: "A Plan for [Prospect Name]" or something natural.

---

## Step 4: Fill the Template

Read `.claude/skills/smart-proposal/template.html`. Replace every `{{PLACEHOLDER}}` with the generated values:

| Placeholder | Source |
|---|---|
| `{{PROSPECT_NAME}}` | Intake |
| `{{PROPOSAL_TITLE}}` | Generated |
| `{{SENDER_NAME}}` | Intake |
| `{{SENDER_COMPANY}}` | Intake |
| `{{SENDER_EMAIL}}` | Intake |
| `{{GENERATED_DATE}}` | Step 2 |
| `{{EXPIRY_ISO}}` | Step 2 |
| `{{EXPIRY_DISPLAY}}` | Step 2 |
| `{{ACCESS_CODE}}` | Intake |
| `{{OVERVIEW_PARAGRAPHS}}` | Step 3 |
| `{{SCOPE_PARAGRAPHS}}` | Step 3 |
| `{{DESIGN_PARAGRAPHS}}` | Step 3 |
| `{{WIREFRAME_HTML}}` | Step 3 |
| `{{DELIVERABLES_HTML}}` | Step 3 |
| `{{TIMELINE_HTML}}` | Step 3 |
| `{{INVESTMENT_DISPLAY}}` | Always "Contact for Pricing" |
| `{{INVESTMENT_NOTE}}` | Step 3 |
| `{{NEXT_STEPS_TEXT}}` | Step 3 |

Write the filled HTML to `/tmp/smart-proposal-[slug].html` where `[slug]` is the prospect name lowercased, spaces replaced with dashes.

---

## Step 5: Push to GitHub Gist

Build the JSON payload using Python (avoids shell escaping issues with HTML):

```bash
python3 - <<'PYEOF'
import json, sys
slug = "{{SLUG}}"
prospect = "{{PROSPECT_NAME}}"
with open(f'/tmp/smart-proposal-{slug}.html', 'r') as f:
    html = f.read()
payload = {
    "description": f"Proposal: {prospect}",
    "public": True,
    "files": {"proposal.html": {"content": html}}
}
with open(f'/tmp/smart-proposal-{slug}-payload.json', 'w') as f:
    json.dump(payload, f)
print("Payload written.")
PYEOF
```

Create the gist:

```bash
RESPONSE=$(curl -s -X POST \
  -H "Authorization: token $GITHUB_GIST_TOKEN" \
  -H "Accept: application/vnd.github.v3+json" \
  -H "Content-Type: application/json" \
  -d @/tmp/smart-proposal-{{SLUG}}-payload.json \
  https://api.github.com/gists)

echo "$RESPONSE" | python3 -c "
import json,sys
r = json.load(sys.stdin)
gist_id = r['id']
html_url = r['html_url']
raw_base = html_url.replace('gist.github.com','gist.githubusercontent.com') + '/raw'
portal = 'https://htmlpreview.github.io/?' + raw_base + '/proposal.html'
print('GIST_ID:', gist_id)
print('PORTAL_URL:', portal)
"
```

If the curl response contains `\"message\"` with an error (e.g., "Bad credentials"), stop and tell the user their `GITHUB_GIST_TOKEN` is invalid or missing the `gist` scope. Classic PAT (not fine-grained) is required for gist creation.

---

## Step 6: Deliver

Output to the user:

```
Proposal ready.

Prospect: [PROSPECT_NAME]
Access code: [ACCESS_CODE]
Expires: [EXPIRY_DISPLAY]

Portal: [hyperlinked URL — use markdown [Proposal for PROSPECT_NAME](URL)]

Send the prospect the portal link and their access code separately.
```

Clean up temp files:
```bash
rm -f /tmp/smart-proposal-{{SLUG}}.html /tmp/smart-proposal-{{SLUG}}-payload.json
```

---

## Notes

- The `GITHUB_GIST_TOKEN` must be a **classic PAT** with `gist` scope. Fine-grained PATs do not have gist permission.
- Investment is always TBD. Never suggest a price. The sender sets it in follow-up.
- The expiry check is client-side JavaScript. It uses the viewer's local clock. It is a soft lock, not cryptographic.
- Hyperlink all URLs. Never display a raw URL in the output.
- If the user is on Ecrof infrastructure, they can also run `/build-proposal` for the full automated chain including Console registration and Telegram gate.

---

## Dependencies

- `.claude/skills/smart-proposal/template.html` — the base HTML template with all placeholders
- `core/identity.md` — voice rules (no hyphens, no "artifact", etc.)
- `GITHUB_GIST_TOKEN` env var (classic PAT, gist scope)
- `python3` available in shell
- `curl` available in shell
