proposal-maker
SkillWeb & browsingTurn a raw commercial offer (client + line items + total) into an HTML proposal whose style copies a brand website. LLM-authored from a brand screenshot; --quick offline fallback. Output: proposal.html with clickable links + exact prices, prints to PDF. Use when: 'make a proposal', 'commercial offer', 'КП', 'коммерческое предложение'.
Available today. Use it from your connected AI after setup.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use the proposal-maker skill
What this skill tells your AI
The instructions your AI receives, as published by mikefluff/skills in skills/proposal-maker/SKILL.md and read by ahel’s review.
The default is LLM-authored, screenshot-driven. A deterministic template can't capture "dark, bold, dramatic brand with a green accent and uppercase display type" — it reads colours by frequency and gets the mood wrong. So instead: a Python step assembles a brand kit, and you (the orchestrator) look at the brand and write the markup.
Execute-layer modules under common/runners/:
proposal/parse.py— offer text → structuredskills.proposal.plan.v1(items, prices, recomputed subtotal, mismatch + outlier flags).proposal/brand.py— scrape brand tokens (accent / fonts / logo / name) + enrich each item with its real catalogue photo + description (og:tags).proposal/kit.py— screenshot the brand site (headless Chrome), download the logo, writeBRIEF.mdbundling tokens + the item table + authoring rules.proposal/render.py— the--quickdeterministic themed template + HTML→PDF helper.
Why a document and NOT an image deck: a proposal carries exact prices and clickable product links. AI image generation garbles both. You write real HTML text.
This skill does NOT: generate slides/images (use carousel-builder/flyer-maker); invent
prices or discounts; silently rewrite a suspicious number (it flags, you ask); log into a
CRM or send anything; compute tax/VAT.
ROLE
Build the brand kit → look at the brand screenshot → surface data-quality warnings to
the user → author a bespoke proposal.html that mirrors the brand → verify it by
screenshotting your own output → deliver. Fall back to --quick only when there's no LLM
loop or no network.
PIPELINE (default — LLM-authored)
-
Capture the offer. Save the user's pasted offer verbatim to a temp file (or pipe via stdin). Don't reformat — the parser handles emoji headers, RU/EN keys,
Name (url) qty — price CURlines, andTotal:. -
Build the brand kit:
python3 proposal-maker/scripts/run.py --offer /tmp/offer.txt # brand site auto-detected from the offer footer; or pass --brand-url <site>This writes to
./generated/proposal/<slug>/:site.png(the brand screenshot),logo.*,brand.json,offer.json(enriched with per-item photo URLs), andBRIEF.md. -
Look at the brand.
Readsite.png— actually view it. Note: dark vs light mood, type weight/case, where/how the logo sits, accent colour usage, imagery feel. Then readBRIEF.mdfor the tokens + the full item table (names, qty, prices, links, photo URLs). -
Surface warnings. If the run printed
⚠ stated total ≠ computedor⚠ '<item>' is N% of the total — possible typo, tell the user and ask before using that number. (The seed Double D offer hasЛогистика 5 000 000— 97% of the total — almost certainly5 000.) Never auto-fix; confirm, then correct the offer text and re-run if needed. -
Author
proposal.htmlinto the kit folder. House rules:- Mirror the screenshot. Same mood (dark canvas if the site is dark), same type
personality (the brand font from
BRIEF.mdvia its Google Fonts link), the accent as the one hot colour, the logo placed like the site places it. - Logo treatment. Brand logos are often monochrome/white SVGs. On a dark header use
as-is; on a light header tint to white via
filter:brightness(0) invert(1)or set it on a dark/accent plate. Always show the brand name beside it so identity survives. - Prominent key facts. Lead the hero with a big Date / Time / Location block — the fields the client checks first — above smaller secondary pills (guests, phone).
- Group by category. Split the items into 4–7 logical categories named for the client's domain (e.g. Звук и свет / Доп оборудование / Артисты / Декорации / Сервис), each with a large, scannable header (big type + accent marker), item count, and per-category subtotal. Never one long pile.
- Vary density. Showpiece / high-value items → big photo cards; utility / low-cost items (controllers, stands, staff, logistics) → compact 2-column rows (small thumb + name + price). Don't render everything large — it reads as sprawling.
- Real photos. Each item gets its
og:imagefromBRIEF.md(Tilda URLs hotlink fine). Items the kit could not photograph from a link get an auto-generated on-brand image (kit fills these; marked inBRIEF.md) — use it and tell the user they can swap a real one. Never a blank/placeholder if a photo can be sourced. - Exact data. Prices and links exactly as parsed; the computed subtotal is the total; quantity chips where given.
- Self-contained. One file: inline
<style>, the Google Fonts<link>, CSS custom properties for the tokens. - Print CSS (required — the PDF depends on it).
@page{size:A4;margin:0}to kill white margins (full-bleed). Running header and footer on every page via the table<thead>/<tfoot>pattern (display:table-header-group/-footer-groupin print) — aposition:fixedband can't reserve per-page space and looks broken on inner pages.break-inside:avoidon cards/total.BRIEF.mdships the exact recipe.
- Mirror the screenshot. Same mood (dark canvas if the site is dark), same type
personality (the brand font from
-
Verify your output. Screenshot the HTML you wrote and look at it:
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --headless --disable-gpu \ --hide-scrollbars --window-size=1100,3600 --screenshot=/tmp/check.png \ "file://$PWD/generated/proposal/<slug>/proposal.html"Read/tmp/check.png. Iterate until it genuinely reads as the brand. (proposal/kitexposesfind_browser()for the binary path across platforms.) -
PDF (optional). Render the authored HTML to PDF via the system browser (no extra deps — keeps clickable links + the dark background):
python3 proposal-maker/scripts/run.py --pdf-from generated/proposal/<slug>/proposal.html # → proposal.pdf next to itNo headless browser? Tell the user to open
proposal.htmland Cmd/Ctrl+P → Save as PDF. -
Deliver. Print the paths (
proposal.html+proposal.pdf).
--quick (deterministic, offline / no-LLM)
Renders one of three CSS themes (editorial / invoice / dark) from proposal/render.py —
no screenshot, no authoring. Use when offline, when there's no LLM loop, or for a fast
draft. Themes + auto-pick: see references/templates.md.
python3 proposal-maker/scripts/run.py --offer /tmp/offer.txt --quick --template dark --pdf
MODES
Required
--offer <path|->or--offer-text "<…>".
Brand (overrides stack on top)
--brand-url <site>(else auto-detected from the offer footer) ·--brand-file <brand.json>·--no-brand(defaults, no network) ·--accent <#hex>·--font "<Family>"·--logo <url|path>·--brand-name "<Name>".
Saved brand profiles (brands/)
- Reusable profiles live in
brands/_index.md— eachbrands/<slug>/holds a correctedbrand.json+ an authoredtemplate.htmlto clone + cached assets + reuse README. - When a profile exists for the client's brand, PREFER
--brand-file brands/<slug>/brand.jsonover a fresh scrape: profiles encode manual corrections a live scrape gets wrong (dark themes read as light on Tilda/Webflow, vanishing white SVG logos). - After any client-ready proposal for a NEW brand, offer to save it as a profile (see the checklist in
brands/_index.md).
Build / look
- (default) brand-kit mode ·
--quickdeterministic template ·--template auto|editorial|invoice|dark(quick only) ·--lang auto|ru|en·--no-thumbnails·--embed-images(quick) ·--currency <CODE>.
Output / inspection
--pdf(quick mode: render PDF via the system browser) ·--pdf-from <html>(render any HTML file to PDF and exit — the default-mode PDF step) ·--output <dir>·--parse-only(parsed JSON, no network — best first dry-run) ·--check(deps +--brand-urlreachability).
REFERENCES (load on demand)
| File | When |
|---|---|
BRIEF.md (generated per run) | The live authoring brief — tokens, item table, rules. Read it every run. |
| references/offer-format.md | Parse contract — header keys, item grammar, qty vs line-total, total/outlier policy. |
| references/brand-extraction.md | What's scraped + manual overrides + the screenshot/kit step. |
| references/templates.md | The 3 --quick themes + auto-pick. |
| references/troubleshoot.md | Fonts/photos/logo/PDF/encoding issues. |
EXAMPLES
See examples/before-after.md — the Double D Project event offer: the deterministic template (the wrong, light read) vs. the LLM-authored, brand-faithful dark proposal, plus the logistics-outlier handling.
CONSTRAINTS
- Look before you build. In default mode you MUST view
site.pngbefore authoring. Don't trustbrand.json.is_darkover your own eyes — the colour heuristic misreads dark Tilda/Webflow sites as light. - Copying the site's style is the whole point. Match its mood, type, accent, logo.
- Exact prices + clickable links. Never render them as generated images.
- Recompute, never silently rewrite. Show the computed subtotal; flag a disagreeing stated total and any line ≥60% of the total; ask the user.
- Verify by screenshot. Author → screenshot → look → iterate. Don't ship unseen.
- Enrichment is best-effort. A dead catalogue link leaves that item text-only.
- Output is
./generated/proposal/<slug>/; slug from client name + event + date. - No secrets. Pure public-web scraping; no API keys.
INVOCATION HINTS
Trigger when the user says: "make a proposal / commercial offer / quote", "turn this offer into a nice document", "КП", "коммерческое предложение", "оффер для клиента", "смету покрасивее", "proposal in the style of ", "копируй стиль с сайта"; or pastes a Telegram/WhatsApp offer with 📆/👤/🧾/💰 fields + a price list.
Default: build the kit, view the screenshot, author the HTML. Recommend --parse-only
first if the offer formatting looks unusual. Reach for --quick only when offline or when
no authoring loop is available.
Distinct from flyer-maker/carousel-builder/cover-maker (those generate IMAGES) and
landing-copy/cold-email (those write prose).
Signals
- GitHub stars
- 21
- Forks
- 1
- Last commit
- Sep 2026
ahel review
K6low
bundled executables the agent is told to runK1binfo
installs-packages (in references/troubleshoot.md)
Automated review, not a security audit. Ruleset v1+k2.
Advanced
- Item type
- skill
- Key
proposal-maker- Source
- github.com/mikefluff/skills