Skill: rijoyai/founder-story-brand-narrative (version 0.1.1)

--- name: founder-story-brand-narrative description: Creates and refines Founder Story and brand narrative content for DTC/independent stores selling founder-driven products (e.g. handcrafted leather, artisan sauces, small-batch goods). Use whenever the user mentions founder story, brand story, about page, origin story, artisan brand, handcrafted brand, "who we are" copy, brand voice, mission/vision for a product brand, landing page hero narrative, PDP brand block, or wants to differentiate through authenticity and founder-led storytelling—even if they do not say "founder story" explicitly. Output structured narrative frameworks, placement guidance, and ready-to-use copy aligned with DTC best practices. compatibility: required: [] --- # Founder Story & Brand Narrative (DTC) You are the brand and content lead for **founder-driven DTC brands**: independent stores where the founder is the face, the maker, or the central authority (e.g. handcrafted leather goods, homemade sauces, small-batch skincare, artisan food). Your job is to turn “we want to tell our story” into **structured founder-story and brand-narrative outputs** that support conversion, trust, and retention on the independent site and owned channels. ## Who this skill serves - **Founder-led / artisan / craft brands** selling on their own site (Shopify, WooCommerce, custom storefront). - **Use cases**: About page, brand page, PDP brand block, landing page hero, email/SMS brand intro, packaging or insert copy, social bio and pinned “our story” content. - **Goal**: Clear, consistent founder story and brand narrative that builds trust and emotional connection without sounding generic or salesy. ## Scope (when not to force-fit) - **Pure commodity or white-label** with no founder/maker angle: Prefer a value-proposition or feature-led skill. - **Enterprise or B2B** where the buyer is a procurement team: Focus on credentials and case studies; founder story is secondary. - **User only wants a single tagline or one-liner**: Deliver that plus a one-sentence note on where it fits in the full narrative—don’t force a full framework. If the scenario doesn’t fit, say why and what can still be reused (e.g. “origin story” paragraph for an About section). ## First 90 seconds: get the key facts Extract from the conversation when possible; otherwise ask. Keep to **6–8 questions**: 1. **Product and category**: What do you sell? (e.g. handcrafted leather bags, small-batch hot sauce, ceramic tableware.) Any sub-brands or product lines? 2. **Founder role**: Is the founder the maker, the face, the recipe-owner, or the curator? Solo founder or small team? 3. **Origin**: Why did this start? (personal need, craft passion, family recipe, side project turned full-time, etc.) 4. **Where the story will live**: About page, PDP, landing page hero, email welcome, packaging, social? Priority order if multiple. 5. **Current state**: Do you already have draft copy or a rough story? What feels missing or off (too long, too generic, not emotional enough)? 6. **Audience**: Who is the core customer? (e.g. gift buyers, daily users, collectors, food enthusiasts.) Any secondary segment? 7. **Tone and constraints**: Formal vs casual, short vs long, any “do not mention” (e.g. no health claims, no competitor names)? 8. **Tech / CMS**: Any limits on length, rich text, or modules (e.g. Shopify About page, custom landing builder)? ## Required output structure Whether the user asks for “a founder story” or “brand narrative for our About page,” output at least: - **Summary (for the team)** - **Placement map** (where each narrative block goes) - **Narrative framework** (core story elements + optional variants) - **Copy deliverables** (ready-to-drop or clearly templated) When they want a full system, use the structure below. ### 1) Summary (3–5 points) - **Current gap**: e.g. “About page is generic; PDP has no brand context; hero doesn’t say who you are.” - **Narrative spine**: One sentence that captures the founder story (origin + mission + for whom). - **Primary placements**: e.g. About page (full), PDP brand block (short), landing hero (one line + CTA). - **Tone and differentiators**: What makes this story credible and distinct (craft, ingredient, process, place, values). - **Next steps**: 1–3 concrete edits or additions (e.g. “Add 2-sentence founder line above fold on PDP”). ### 2) Placement map (where story lives on the site) Map narrative to standard DTC/independent-store touchpoints: | Touchpoint | Purpose | Suggested length | Notes | |------------|---------|------------------|--------| | **About page / Brand page** | Full origin + mission + values; trust and emotional connection | 300–800 words (scannable sections) | Primary home for founder story | | **Homepage hero** | Who you are + one clear value prop + primary CTA | 1–2 lines headline, 1–2 lines sub | Above-the-fold; avoid long paragraphs | | **PDP (Product Detail Page)** | Short “why we make this” or “from the founder” block | 2–4 sentences or a small module | Reinforce craft/quality without repeating full About | | **Landing page** (campaigns) | Story hook + offer; consistent with brand voice | Hero + 1 short paragraph max | Match campaign intent (e.g. first order, gift) | | **Email / SMS** (welcome, post-purchase) | Brief “who we are” + what to expect | 1–3 sentences | Link to About for full story | | **Packaging / insert** | Thank-you + one memorable line (origin or mission) | 1–2 sentences | Physical trust and recall | Output a **placement map** for this brand: which of the above they use, what goes where, and what to avoid (e.g. don’t put long story in hero). ### 3) Narrative framework (founder story building blocks) Use these elements; adapt order and depth by placement. - **Hook / opening**: Why should the reader care in one sentence? (Problem, desire, or “we started because…”.) - **Origin**: How and why the brand started (founder moment, craft journey, family recipe, frustration with existing options). - **Mission**: What you exist to do (e.g. “make heirloom-quality leather accessible,” “put real ingredients in every bottle”). - **Process / craft**: What you do that others don’t (materials, small-batch, made-to-order, recipe, technique)—brief and concrete. - **For whom**: Who this is for (without stereotyping): e.g. “people who value longevity over trends,” “home cooks who want clean flavor.” - **Values**: 2–4 principles that show up in product or behavior (e.g. transparency, sustainability, no shortcuts). - **Proof / credibility**: Years in business, awards, press, or customer line (e.g. “trusted by X since Y”)—one line unless About page. - **Close / CTA**: What you want them to do next (e.g. “Meet the collection,” “See how we make it,” “Join the table”). For each placement, specify which blocks to include and at what length (e.g. About: all; PDP: origin + process + for whom in 2–3 sentences). ### 4) Copy deliverables - **About page**: Full narrative in sections (e.g. Our Story, How We Make It, Our Values, Meet [Founder]). Use headings and short paragraphs; suggest word count per section. - **Short variants**: - **Hero headline + sub** (homepage or landing): 1–2 options. - **PDP “From the founder” / “Why we make this”**: 2–4 sentences, 2 options if useful. - **Email/SMS one-liner**: 1–2 options. - **Packaging/insert line**: 1–2 options. - **Tone note**: 2–3 sentences describing voice (e.g. warm, direct, no jargon, no hype) so future copy stays consistent. If the user provided existing copy, output **before/after** or “replace this block with…” for at least one section. ### 5) DTC terminology and conventions Use and reinforce standard independent-store language: - **Founder story / origin story**: The narrative of how and why the brand started; use “founder story” when the founder is the protagonist. - **About page / Brand page**: Dedicated site page for full narrative; “About” is the common label. - **PDP**: Product Detail Page; “brand block” or “founder note” is a short narrative module on PDP. - **Hero**: Top section of homepage or landing page (above-the-fold); headline + sub + CTA. - **Value proposition / UVP**: Clear benefit or promise (e.g. “Heirloom leather, made by hand in [place]”). - **Trust signals**: Elements that support credibility (origin, process, materials, guarantees, reviews). - **CTA (call-to-action)**: Primary button or link (e.g. “Shop the collection,” “Read our story”). - **Brand voice**: Consistent tone and style across copy (formal/casual, earnest/playful). - **Craft / artisan / small-batch**: Use when true; avoid empty claims. Prefer concrete detail (e.g. “each piece is cut and stitched in our studio”) over “artisan” alone. Avoid: vague “we’re passionate,” “quality is our priority,” or long blocks in hero/PDP. Prefer: specific origin, concrete process, and clear “for whom.” ### 6) Validation and iteration - **Readability**: About page should be scannable (headings, short paragraphs, optional pull quotes). - **Consistency**: Same origin and mission across placements; only length and emphasis change. - **Conversion support**: Hero and PDP narrative should point toward a clear next step (shop, sign up, learn more). - **Differentiation**: The story should be distinguishable from a generic “we love what we do” template; suggest one “signature line” they can own. Optional: Suggest one simple A/B test (e.g. hero with vs without founder line) and what to measure (e.g. scroll depth, time on site, add-to-cart from PDP). ## Output style - **Conclusion first**: Lead with summary and placement map, then narrative framework and copy. - **Actionable**: Every section should result in a concrete asset or edit (page, block, line). - **Concise**: No filler; if a block is “optional,” say so and why. - **Brand-safe**: No invented facts; if something is unknown, say “clarify with founder” or “placeholder until you confirm.” For very small asks (e.g. “one paragraph for our About page”), deliver the paragraph plus one sentence on where it sits in the full framework—don’t overload. ## References - For deeper narrative structure and example arcs, see [references/founder_story_framework.md](references/founder_story_framework.md). - For placement and word-count guidance by page type, see [references/placement_guidance.md](references/placement_guidance.md). ## Scripts (optional, for standardization) Use these when you want repeatable inputs/outputs and quick copy checks. ### 1) Generate a standardized Founder Story Brief - Script: `scripts/generate_founder_story_brief.py` - Purpose: Turn a structured JSON input into a scannable markdown brief you can reuse for About/PDP/hero variants. - Example input: `scripts/brief.example.json` Run: ```bash python3 scripts/generate_founder_story_brief.py --in brief.json --out brief.md ``` Then use `brief.md` as the single source of truth when writing the Founder Story and all short variants. ### 2) Lint copy for length and “generic filler” - Script: `scripts/copy_lint.py` - Purpose: Flag copy that is too long for common placements (hero, PDP block, packaging) and detect vague phrases (e.g. “we’re passionate”, “quality is our priority”). - Example format: `scripts/copy.example.md` (uses `## ` headings and `Headline:` / `Sub:` lines for hero) Run: ```bash python3 scripts/copy_lint.py --in copy.md ``` If the lint flags something, tighten the copy by replacing generic adjectives with concrete process/origin/proof.

path: evals/evals.json
content: {
  "skill_name": "founder-story-brand-narrative",
  "evals": [
    {
      "id": 1,
      "prompt": "We sell handcrafted leather bags and wallets on our Shopify store. The founder is the sole maker and has been doing this for 8 years. We have an About page but it reads generic. I want a proper founder story and brand narrative we can use on the About page, and a short block for our product pages so people know who’s behind the product. Everything in English and suitable for our independent DTC site.",
      "expected_output": "A full founder-story and brand-narrative output: summary (gaps, narrative spine, placements, tone), placement map (About + PDP), narrative framework (origin, mission, process, for whom, values), and copy deliverables including About page sections and a short PDP 'from the founder' block; all in English with DTC terminology (PDP, hero, CTA, trust signals, etc.).",
      "files": []
    },
    {
      "id": 2,
      "prompt": "I run a small DTC brand selling homemade hot sauce and small-batch condiments. The story is: family recipe, started in the home kitchen, now we have a small commercial kitchen. We need a founder story for our brand page and a one-liner for the homepage hero. Use professional e-commerce / independent store language.",
      "expected_output": "Structured output with summary, placement map (brand page + homepage hero), narrative framework tailored to food/craft origin, and copy deliverables: full brand-page narrative (sections, word-count guidance) and 1–2 hero headline + sub options; consistent DTC terms (About/Brand page, hero, CTA, value proposition).",
      "files": []
    },
    {
      "id": 3,
      "prompt": "We’re an artisan ceramic tableware brand, founder-led, selling on our own site. I need help with: 1) Where should the founder story appear on the site? 2) A short 'why we make this' block for product detail pages. 3) One line we could put on packaging or in the box. Keep it all in English and use proper terms for direct-to-consumer / independent store.",
      "expected_output": "Placement map for About, PDP, and packaging; narrative framework (origin, craft, mission); copy deliverables including PDP 'why we make this' block (2–4 sentences, 2 options) and 1–2 packaging/insert lines; summary with next steps; professional DTC/independent-store terminology throughout.",
      "files": []
    }
  ]
}


path: references/founder_story_framework.md
content: # Founder Story Framework (DTC)

## Narrative arcs (choose one or blend)

1. **Origin / “Why I started”**  
   Personal need, frustration with existing options, or a turning point (e.g. “I couldn’t find X, so I made it”; “After Y years in the industry, I wanted to do it differently”). Best for: craft, food, wellness, custom goods.

2. **Craft / “How we make it”**  
   Process, materials, place, and standards. Use when the *how* is the differentiator (small-batch, hand-cut, family recipe, single-origin). Best for: artisan food, leather, ceramics, skincare.

3. **Mission / “What we stand for”**  
   Values and impact (sustainability, transparency, local, quality over scale). Best when the brand is values-led; keep concrete (e.g. “we use only full-grain leather from X”) not vague.

4. **For whom / “Who it’s for”**  
   The customer you serve (e.g. “for people who want heirloom pieces, not fast fashion”). Tighten with one clear descriptor; avoid long demographic lists.

## Story elements checklist

- [ ] **Hook**: One sentence that answers “why should I care?”
- [ ] **Origin**: When, where, and why the brand started
- [ ] **Founder role**: Maker, face, curator, or guardian of the recipe/process
- [ ] **Process**: 1–3 concrete differentiators (materials, technique, scale, place)
- [ ] **Mission**: What you exist to do (one clear sentence)
- [ ] **Values**: 2–4 principles; tie to product or behavior
- [ ] **Proof**: Years, awards, press, or “trusted by X” (one line unless About page)
- [ ] **CTA**: What you want them to do next

## Tone guidelines

- **Warm but direct**: Human, not corporate; avoid “we’re passionate” without evidence.
- **Specific over generic**: “We stitch each bag by hand in our Austin studio” beats “we care about quality.”
- **No hype**: Let process and origin build trust; avoid superlatives unless backed by proof.
- **Consistent voice**: Same tone on About, PDP, email, and social “our story” content.

## Example spine (one sentence)

- “We started [Brand] because [origin]. Today we [mission] for [audience] by [key differentiator].”

Example: “We started [Brand] because we couldn’t find leather goods that lasted. Today we make heirloom-quality bags and wallets for people who value longevity, by hand in our Austin studio.”


path: references/placement_guidance.md
content: # Placement Guidance: Founder Story on DTC Sites

## Page types and word counts

| Page / Section       | Role of story                         | Suggested length     | Format                    |
|----------------------|----------------------------------------|----------------------|---------------------------|
| **About / Brand**    | Full narrative; trust and connection   | 300–800 words        | Sections with headings    |
| **Homepage hero**    | Who you are + UVP + primary CTA       | 1–2 lines + 1–2 sub  | Headline + sub + CTA      |
| **PDP brand block**  | “Why we make this” / “From the founder”| 2–4 sentences        | Short paragraph or module |
| **Landing hero**     | Campaign hook + offer                 | 1 line + 1 short ¶   | Match campaign intent     |
| **Email welcome**    | Brief “who we are” + next step        | 1–3 sentences        | Link to About             |
| **Packaging / insert**| Thank-you + one memorable line        | 1–2 sentences        | Single line or short note  |

## Above-the-fold rules

- **Hero**: Never long paragraphs. One clear headline (who you are or main promise), one supporting line, one primary CTA.
- **PDP**: Founder/craft block below fold or in a tab is fine; above fold keep product (image, title, price, key benefit, add-to-cart).

## Consistency

- Same **origin** and **mission** everywhere; only length and emphasis change.
- Same **brand voice** (formal/casual, earnest/playful) across site and owned channels.
- One **signature line** (e.g. “Made by hand, built to last”) can repeat in hero, PDP, and packaging for recall.

## What to avoid

- Long founder story in hero or PDP above-the-fold (hurts conversion).
- Different “why we started” on About vs PDP (confuses trust).
- Empty adjectives (“passionate,” “quality-focused”) without concrete proof or process.


path: scripts/copy.example.md
content: ## Homepage hero

Headline: Heirloom leather goods, made by hand
Sub: Full-grain leather, solid brass hardware, and repairs you can count on.
CTA: Shop the collection

## PDP

We started Stone & Stitch because we couldn’t find leather pieces that aged beautifully and lasted for years. Today we make made-to-order wallets and bags, cut and stitched by hand in Austin, for people who value longevity over trends.

## Packaging

Thank you for supporting small-batch craft—made by hand, built to last.



path: scripts/copy_lint.py
content: #!/usr/bin/env python3

from __future__ import annotations

import argparse
import re
import sys
from dataclasses import dataclass
from pathlib import Path
from typing import Dict, Iterable, List, Tuple


@dataclass(frozen=True)
class LintIssue:
    section: str
    message: str
    evidence: str = ""


SECTION_TITLES = {
    "homepage hero": "Homepage hero",
    "hero": "Homepage hero",
    "pdp": "PDP brand block",
    "pdp brand block": "PDP brand block",
    "product detail page": "PDP brand block",
    "packaging": "Packaging / insert",
    "insert": "Packaging / insert",
}


GENERIC_PHRASES = [
    "we're passionate",
    "we are passionate",
    "quality is our priority",
    "highest quality",
    "best quality",
    "premium quality",
    "crafted with love",
    "made with love",
    "we care about quality",
    "our mission is to provide",
]


def normalize_title(title: str) -> str:
    t = title.strip().lower()
    t = re.sub(r"\s+", " ", t)
    return t


def split_sections(md: str) -> Dict[str, str]:
    """
    Split markdown into sections using '## ' headings.
    Returns {canonical_section_name: content}.
    """
    lines = md.splitlines()
    sections: Dict[str, List[str]] = {}
    current_key = "Document"
    sections[current_key] = []

    for line in lines:
        if line.startswith("## "):
            raw_title = line[3:].strip()
            key = SECTION_TITLES.get(normalize_title(raw_title), raw_title)
            current_key = key
            sections.setdefault(current_key, [])
            continue
        sections[current_key].append(line)

    return {k: "\n".join(v).strip() for k, v in sections.items() if "\n".join(v).strip()}


def word_count(text: str) -> int:
    words = re.findall(r"[A-Za-z0-9]+(?:'[A-Za-z0-9]+)?", text)
    return len(words)


def find_generic_phrases(text: str) -> List[str]:
    t = text.lower()
    hits = []
    for phrase in GENERIC_PHRASES:
        if phrase in t:
            hits.append(phrase)
    return hits


def lint_hero(section: str, text: str) -> List[LintIssue]:
    issues: List[LintIssue] = []

    headline = ""
    sub = ""
    for line in (l.strip() for l in text.splitlines() if l.strip()):
        if line.lower().startswith("headline:"):
            headline = line.split(":", 1)[1].strip()
        if line.lower().startswith(("sub:", "subhead:", "subheadline:")):
            sub = line.split(":", 1)[1].strip()

    if not headline:
        issues.append(
            LintIssue(
                section,
                "Missing 'Headline:' line. For best results, format as: Headline: ... / Sub: ... / CTA: ...",
            )
        )
    else:
        wc = word_count(headline)
        if wc > 14:
            issues.append(LintIssue(section, f"Headline is long ({wc} words). Aim for 6–14 words.", headline))

    if sub:
        wc = word_count(sub)
        if wc > 26:
            issues.append(LintIssue(section, f"Subhead is long ({wc} words). Aim for <= 26 words.", sub))

    return issues


def lint_pdp(section: str, text: str) -> List[LintIssue]:
    issues: List[LintIssue] = []
    wc = word_count(text)
    if wc > 120:
        issues.append(LintIssue(section, f"PDP block is long ({wc} words). Aim for ~40–120 words.", ""))
    if wc < 25:
        issues.append(LintIssue(section, f"PDP block is short ({wc} words). Aim for >= 25 words.", ""))
    return issues


def lint_packaging(section: str, text: str) -> List[LintIssue]:
    issues: List[LintIssue] = []
    wc = word_count(text)
    if wc > 30:
        issues.append(LintIssue(section, f"Packaging/insert copy is long ({wc} words). Aim for <= 30 words.", ""))
    return issues


def lint_generic(section: str, text: str) -> List[LintIssue]:
    issues: List[LintIssue] = []
    hits = find_generic_phrases(text)
    for phrase in hits:
        issues.append(
            LintIssue(
                section,
                f"Generic filler detected: '{phrase}'. Replace with concrete origin/process/proof.",
                phrase,
            )
        )
    return issues


def lint_sections(sections: Dict[str, str]) -> List[LintIssue]:
    issues: List[LintIssue] = []
    for section, text in sections.items():
        issues.extend(lint_generic(section, text))
        key = normalize_title(section)
        if key in ("homepage hero", "hero"):
            issues.extend(lint_hero(section, text))
        if key in ("pdp brand block", "pdp"):
            issues.extend(lint_pdp(section, text))
        if key in ("packaging / insert", "packaging", "insert"):
            issues.extend(lint_packaging(section, text))
    return issues


def format_issues(issues: Iterable[LintIssue]) -> str:
    out: List[str] = []
    for i, issue in enumerate(issues, start=1):
        out.append(f"{i}. [{issue.section}] {issue.message}")
        if issue.evidence:
            out.append(f"   Evidence: {issue.evidence}")
    return "\n".join(out)


def build_parser() -> argparse.ArgumentParser:
    p = argparse.ArgumentParser(
        description="Lint founder-story related copy for length and generic filler. Input should use '## ' headings (e.g. '## Homepage hero', '## PDP', '## Packaging')."
    )
    p.add_argument("--in", dest="in_path", required=True, help="Path to markdown file (copy.md).")
    return p


def main() -> None:
    args = build_parser().parse_args()
    in_path = Path(args.in_path).expanduser()
    text = in_path.read_text(encoding="utf-8")
    sections = split_sections(text)
    issues = lint_sections(sections)

    if issues:
        print("COPY LINT: issues found\n")
        print(format_issues(issues))
        sys.exit(1)

    print("COPY LINT: OK (no issues found)")


if __name__ == "__main__":
    main()



path: scripts/brief.example.json
content: {
  "brand_name": "Stone & Stitch",
  "category": "Handcrafted leather goods",
  "products": ["Wallets", "Bags", "Belts"],
  "founder_name": "Alex",
  "founder_role": "sole maker",
  "location": "Austin, TX",
  "origin": "I couldn't find leather pieces that aged beautifully and lasted for years, so I began making my own after hours in a small studio.",
  "mission": "make heirloom-quality leather goods that get better with time",
  "audience": "people who value longevity over trends",
  "differentiators": [
    "cut, skive, and stitch each piece by hand in our studio",
    "use full-grain leather and solid brass hardware",
    "made-to-order to reduce waste and improve consistency"
  ],
  "values": ["no shortcuts", "transparent materials", "repair over replace"],
  "proof": ["8 years making leather goods", "1,200+ orders fulfilled"],
  "tone": "warm, direct, craft-forward, no hype",
  "constraints": ["avoid health claims", "avoid competitor mentions"],
  "primary_cta": "Shop the collection"
}



path: scripts/generate_founder_story_brief.py
content: #!/usr/bin/env python3

from __future__ import annotations

import argparse
import json
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Dict, List, Optional


@dataclass(frozen=True)
class FounderStoryBrief:
    brand_name: str = ""
    category: str = ""
    products: List[str] = None  # type: ignore[assignment]
    founder_name: str = ""
    founder_role: str = ""
    location: str = ""
    origin: str = ""
    mission: str = ""
    audience: str = ""
    differentiators: List[str] = None  # type: ignore[assignment]
    values: List[str] = None  # type: ignore[assignment]
    proof: List[str] = None  # type: ignore[assignment]
    tone: str = ""
    constraints: List[str] = None  # type: ignore[assignment]
    primary_cta: str = ""

    def __post_init__(self) -> None:
        object.__setattr__(self, "products", self.products or [])
        object.__setattr__(self, "differentiators", self.differentiators or [])
        object.__setattr__(self, "values", self.values or [])
        object.__setattr__(self, "proof", self.proof or [])
        object.__setattr__(self, "constraints", self.constraints or [])


def _as_list(value: Any) -> List[str]:
    if value is None:
        return []
    if isinstance(value, list):
        return [str(v).strip() for v in value if str(v).strip()]
    if isinstance(value, str):
        v = value.strip()
        return [v] if v else []
    return [str(value).strip()]


def parse_brief(data: Dict[str, Any]) -> FounderStoryBrief:
    return FounderStoryBrief(
        brand_name=str(data.get("brand_name", "")).strip(),
        category=str(data.get("category", "")).strip(),
        products=_as_list(data.get("products")),
        founder_name=str(data.get("founder_name", "")).strip(),
        founder_role=str(data.get("founder_role", "")).strip(),
        location=str(data.get("location", "")).strip(),
        origin=str(data.get("origin", "")).strip(),
        mission=str(data.get("mission", "")).strip(),
        audience=str(data.get("audience", "")).strip(),
        differentiators=_as_list(data.get("differentiators")),
        values=_as_list(data.get("values")),
        proof=_as_list(data.get("proof")),
        tone=str(data.get("tone", "")).strip(),
        constraints=_as_list(data.get("constraints")),
        primary_cta=str(data.get("primary_cta", "")).strip(),
    )


def narrative_spine(brief: FounderStoryBrief) -> str:
    brand = brief.brand_name or "[Brand]"
    origin = brief.origin or "[origin]"
    mission = brief.mission or "[mission]"
    audience = brief.audience or "[audience]"
    diff = brief.differentiators[0] if brief.differentiators else "[key differentiator]"
    return f"We started {brand} because {origin}. Today we {mission} for {audience} by {diff}."


def to_markdown(brief: FounderStoryBrief) -> str:
    brand = brief.brand_name or "[Brand]"
    founder = brief.founder_name or "[Founder]"
    founder_role = brief.founder_role or "[founder role]"

    def bullets(items: List[str], empty_placeholder: str) -> str:
        if not items:
            return f"- {empty_placeholder}\n"
        return "".join(f"- {x}\n" for x in items)

    products_line = ", ".join(brief.products) if brief.products else "[list key products]"
    proof_block = bullets(brief.proof, "[proof / credibility to add]")
    constraints_block = bullets(brief.constraints, "[constraints / compliance notes]")

    location = f" ({brief.location})" if brief.location else ""
    tone = brief.tone or "[voice: warm, direct, no hype]"
    cta = brief.primary_cta or "[primary CTA: Shop the collection]"

    md = []
    md.append(f"# Founder Story Brief: {brand}\n")
    md.append("## Snapshot\n")
    md.append(f"- **Category**: {brief.category or '[category]'}\n")
    md.append(f"- **Products**: {products_line}\n")
    md.append(f"- **Founder**: {founder}{location} — {founder_role}\n")
    md.append(f"- **Audience**: {brief.audience or '[who this is for]'}\n")
    md.append(f"- **Brand voice**: {tone}\n")
    md.append(f"- **Primary CTA**: {cta}\n\n")

    md.append("## Narrative spine (one sentence)\n\n")
    md.append(f"> {narrative_spine(brief)}\n\n")

    md.append("## Origin (why it started)\n\n")
    md.append(f"{brief.origin or '[Write 2–4 sentences: the moment/problem/turning point that started the brand.]'}\n\n")

    md.append("## Mission (what you exist to do)\n\n")
    md.append(f"{brief.mission or '[One sentence: what you do for the customer, not what you like doing.]'}\n\n")

    md.append("## Differentiators (concrete, verifiable)\n\n")
    md.append(bullets(brief.differentiators, "[Add 1–3 concrete process/material/ingredient differentiators]"))
    md.append("\n")

    md.append("## Values (2–4 principles)\n\n")
    md.append(bullets(brief.values, "[Add values that show up in product or behavior]"))
    md.append("\n")

    md.append("## Proof / credibility (if any)\n\n")
    md.append(proof_block)
    md.append("\n")

    md.append("## Constraints (what not to claim or mention)\n\n")
    md.append(constraints_block)
    md.append("\n")

    md.append("## Placement plan\n\n")
    md.append("- **About / Brand page**: Full origin + mission + values + proof. (300–800 words, scannable sections)\n")
    md.append("- **Homepage hero**: One headline + one sub + one CTA. No long paragraphs.\n")
    md.append("- **PDP brand block**: 2–4 sentences: origin + process + for-whom. Avoid repeating the full About.\n")
    md.append("- **Packaging / insert**: 1–2 sentences with one memorable line.\n\n")

    md.append("## Notes for copywriting\n\n")
    md.append("- Replace generic claims with concrete process/origin/proof.\n")
    md.append("- Keep hero copy short; let product and CTA do the work.\n")
    md.append("- Keep origin and mission consistent across placements; only length changes.\n")

    return "".join(md)


def read_json(path: Path) -> Dict[str, Any]:
    try:
        return json.loads(path.read_text(encoding="utf-8"))
    except json.JSONDecodeError as e:
        raise SystemExit(f"Invalid JSON in {path}: {e}") from e


def write_text(path: Path, text: str) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(text, encoding="utf-8")


def build_parser() -> argparse.ArgumentParser:
    p = argparse.ArgumentParser(
        description="Generate a standardized Founder Story Brief markdown from a JSON input."
    )
    p.add_argument("--in", dest="in_path", required=True, help="Path to input JSON (brief.json).")
    p.add_argument("--out", dest="out_path", required=True, help="Path to output markdown (brief.md).")
    return p


def main() -> None:
    args = build_parser().parse_args()
    in_path = Path(args.in_path).expanduser()
    out_path = Path(args.out_path).expanduser()
    data = read_json(in_path)
    brief = parse_brief(data)
    md = to_markdown(brief)
    write_text(out_path, md)


if __name__ == "__main__":
    main()



path: scripts/brief.example.md
content: # Founder Story Brief: Stone & Stitch
## Snapshot
- **Category**: Handcrafted leather goods
- **Products**: Wallets, Bags, Belts
- **Founder**: Alex (Austin, TX) — sole maker
- **Audience**: people who value longevity over trends
- **Brand voice**: warm, direct, craft-forward, no hype
- **Primary CTA**: Shop the collection

## Narrative spine (one sentence)

> We started Stone & Stitch because I couldn't find leather pieces that aged beautifully and lasted for years, so I began making my own after hours in a small studio.. Today we make heirloom-quality leather goods that get better with time for people who value longevity over trends by cut, skive, and stitch each piece by hand in our studio.

## Origin (why it started)

I couldn't find leather pieces that aged beautifully and lasted for years, so I began making my own after hours in a small studio.

## Mission (what you exist to do)

make heirloom-quality leather goods that get better with time

## Differentiators (concrete, verifiable)

- cut, skive, and stitch each piece by hand in our studio
- use full-grain leather and solid brass hardware
- made-to-order to reduce waste and improve consistency

## Values (2–4 principles)

- no shortcuts
- transparent materials
- repair over replace

## Proof / credibility (if any)

- 8 years making leather goods
- 1,200+ orders fulfilled

## Constraints (what not to claim or mention)

- avoid health claims
- avoid competitor mentions

## Placement plan

- **About / Brand page**: Full origin + mission + values + proof. (300–800 words, scannable sections)
- **Homepage hero**: One headline + one sub + one CTA. No long paragraphs.
- **PDP brand block**: 2–4 sentences: origin + process + for-whom. Avoid repeating the full About.
- **Packaging / insert**: 1–2 sentences with one memorable line.

## Notes for copywriting

- Replace generic claims with concrete process/origin/proof.
- Keep hero copy short; let product and CTA do the work.
- Keep origin and mission consistent across placements; only length changes.


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