diff --git a/apps/cli/ai/skills/creative-direction/SKILL.md b/apps/cli/ai/skills/creative-direction/SKILL.md new file mode 100644 index 0000000000..d61aa5e38e --- /dev/null +++ b/apps/cli/ai/skills/creative-direction/SKILL.md @@ -0,0 +1,147 @@ +--- +name: creative-direction +description: Expand a vague site brief into a rich content and structure plan. Infer site type, choose appropriate pages and sections, commit to a fitting design direction, and briefly tell the user what you're building — then build it without asking. +user-invokable: false +--- + +# Creative Direction + +When building a new WordPress site from a brief or vague prompt, use this skill to expand the brief into a rich content plan before writing any files. The goal: a user who types "a site for a bar" gets a site as complete and functional as one described with a detailed spec — because you make the right assumptions for them. + +## Step 0 — Gauge the Brief + +Before expanding, read what the user gave you and pick the right mode: + +| Signal | Mode | +|--------|------| +| Site type is clear, content is thin ("a site for a bar") | **Auto-expand** — proceed to Step 1 | +| Site type is ambiguous ("a site for my business") | **Ask one question** to resolve it, then proceed to Step 1 | +| User already described pages, sections, or copy | **Skip this skill** — build what they asked for | +| User said "minimal", "one page", or "just a placeholder" | **Skip this skill** | + +**The one question rule**: if you need to ask, ask a single, specific question that resolves the ambiguity (e.g. *"What kind of business is this for?"*). Do not ask multiple questions. Once you have the answer, proceed. + +## Step 1 — Detect the Site Type + +From the site name and prompt, infer what kind of site this is. Use your judgment freely — the following are common patterns, not an exhaustive list: + +- **Bar / Nightclub / Venue** — bar, club, lounge, pub, tavern, speakeasy, nightlife +- **Restaurant / Café / Bakery** — restaurant, café, bistro, diner, bakery, brunch, eatery +- **Coffee Shop** — coffee, roastery, espresso, specialty/third-wave café +- **Portfolio (Photo / Design / Art)** — photographer, designer, artist, illustrator, studio +- **Agency / Creative Studio** — agency, studio, consulting, branding, digital +- **SaaS / Tech Product** — app, software, platform, tool, saas, product +- **Fitness / Gym / Yoga** — gym, fitness, yoga, pilates, crossfit, wellness, studio +- **Salon / Spa / Beauty** — salon, spa, beauty, barbershop, nail, aesthetics +- **Non-profit / Cause** — foundation, charity, org, cause, community +- **Professional Services** — law, dental, medical, clinic, accounting, finance +- **Personal Blog / Magazine** — blog, journal, magazine, editorial + +For anything not on this list, reason by analogy: what kind of site does this business actually need? + +## Step 2 — Expand Into Pages and Sections + +Based on the site type, decide which pages and home-page sections to create. **Do not default to a minimal one-page placeholder** when the type clearly warrants more. The examples below are starting points — treat them as inspiration, not prescriptions. Adapt freely based on the site name and any details the user gave. + +### Bar / Nightclub / Venue +**Pages**: Home, Menu (drinks & food), Events, Gallery, Contact + Hours +**Home sections**: hero (full-bleed, mood-setting), featured events teaser, menu highlight, gallery strip, reservation CTA +**Forms**: reservation/book-a-table (Jetpack contact form), newsletter signup +**Design signal**: dark & atmospheric; bold typography; energy + +### Restaurant / Café / Bakery +**Pages**: Home, Menu, Reservations, Gallery, About, Contact +**Home sections**: hero with signature dish, menu teaser, about/story, gallery, reservation CTA +**Forms**: reservation form, contact form +**Design signal**: warm, inviting; food-photography-driven; approachable + +### Coffee Shop +**Pages**: Home, Menu, Our Story, Locations, Contact +**Home sections**: hero, signature drinks highlight, story/craft section, locations map teaser, newsletter +**Forms**: newsletter signup, contact +**Design signal**: artisanal; hand-crafted feel; warm neutrals or bold brand color + +### Portfolio (Photographer / Designer / Artist) +**Pages**: Home, Work/Portfolio (grid), About, Services, Contact +**Home sections**: full-bleed hero work, selected projects grid, brief about, services teaser, contact CTA +**Forms**: contact/inquiry form +**Design signal**: let the work breathe; minimal chrome; bold typography for name/headline + +### Agency / Creative Studio +**Pages**: Home, Services, Work/Case Studies, Team, About, Contact +**Home sections**: hero value prop, services overview, selected work, team teaser, client logos, contact CTA +**Forms**: project inquiry form, contact +**Design signal**: confident; editorial; distinctive brand identity + +### SaaS / Tech Product +**Pages**: Home (all-in-one landing), Pricing, About, Contact +**Home sections**: hero + one-line value prop, key features (3-up or 4-up), how-it-works, testimonials/social proof, pricing teaser, FAQ, final CTA +**Forms**: newsletter/waitlist signup, contact +**Design signal**: clean, modern; strong CTA hierarchy; trust signals prominent + +### Fitness / Gym / Yoga Studio +**Pages**: Home, Classes/Schedule, Memberships & Pricing, Trainers, About, Contact +**Home sections**: hero (energy/motion), classes preview, membership tiers, trainer spotlight, testimonials, CTA +**Forms**: class booking / free trial signup, contact +**Design signal**: energetic or calm (match the discipline); strong photography + +### Salon / Spa / Beauty +**Pages**: Home, Services & Pricing, Team, Gallery, Book Now, Contact +**Home sections**: hero, services overview, team highlights, gallery strip, booking CTA +**Forms**: booking/appointment form, contact +**Design signal**: luxe or friendly; clean; beauty imagery + +### Non-profit / Cause +**Pages**: Home, Mission/About, Programs, Team, Get Involved/Donate, Contact +**Home sections**: mission statement hero, impact stats, programs overview, team, call to donate/volunteer +**Forms**: donation CTA (link to external), volunteer/contact form +**Design signal**: hopeful, trustworthy, human-centered + +### Professional Services (Law / Medical / Dental / Accounting) +**Pages**: Home, Services, Team/Credentials, Testimonials, Contact +**Home sections**: hero (credibility-first), services grid, team highlight, testimonials, contact CTA +**Forms**: appointment / consultation request form +**Design signal**: authoritative, clean, trustworthy; conservative palette + +### Personal Blog / Magazine +**Pages**: Home (recent posts), About, Category archives, Contact +**Home sections**: featured post hero, recent posts grid, about blurb, newsletter +**Forms**: newsletter signup, contact +**Design signal**: editorial; typography-driven; readable + +## Step 3 — Pick a Design Direction + +Commit to an aesthetic that genuinely fits the site name and type. Use the name as a creative brief — the right answer is different every time. Some examples of how a name can point to a direction: + +- *"Boogie Bar"* → dark & moody; jazz/funk energy; amber + deep black; retro headlines +- *"Morning Light Bakery"* → warm, handcrafted; cream + terracotta; flowing serif display +- *"Apex Fitness"* → high-contrast; kinetic; strong sans-serif; black + electric accent +- *"Root Studio"* (yoga) → organic, grounded; earth tones; soft, breathing layout +- *"Hartley & Associates"* (law) → authoritative, minimal; navy + gold; refined serif + +These are illustrations, not a template. Read the name. + +## Step 4 — Brief the User in ≤4 Lines + +Before building, tell the user what you decided in 2–4 short lines: + +> *"Building a 5-page site for Boogie Bar: Home, Menu, Events, Gallery, and Contact. Dark & moody aesthetic with jazz-inspired typography and an amber/black palette. Includes a reservations form and a newsletter signup."* + +Then proceed — **do not ask for approval on the brief itself**. You may have already asked a clarifying question in Step 0; the brief is not a second round of questions. Build it. + +## What "Rich Content" Means + +When generating page content, go beyond placeholder copy: + +- **A bar**: write actual cocktail names and descriptions, real-sounding event nights ("Jazz Thursdays", "Trivia Night"), a gallery section +- **A bakery**: name actual pastries and breads; write a "baker's story" paragraph; include seasonal specials +- **A portfolio**: write a compelling headline and 3–5 project card stubs with realistic titles +- **A SaaS**: write feature names and one-liners, realistic pricing tiers, 2–3 testimonial quotes + +Use the site name as context. Make it feel like a real site for that specific business, not a generic template. + +## When to Skip This Skill + +- User already provided detailed content (specific pages, copy, sections described) +- User explicitly said "keep it minimal", "one page", or "just a placeholder" +- Redesigning or updating an existing site diff --git a/apps/cli/ai/system-prompt.ts b/apps/cli/ai/system-prompt.ts index ff3b099c06..96da9f7aab 100644 --- a/apps/cli/ai/system-prompt.ts +++ b/apps/cli/ai/system-prompt.ts @@ -116,7 +116,7 @@ For any request that involves a WordPress site, you MUST first determine which s Then continue with: 1. **Get site details**: Use site_info to get the site path, URL, and credentials. -2. **Plan the design**: Before writing any code, review the site spec (from the \`site-spec\` skill) and load the \`visual-design\` skill to plan the visual direction: layout, colors, typography, and spacing. +2. **Plan the design**: Before writing any code, review the site spec (from the \`site-spec\` skill). If the brief is vague — site type clear but pages, sections, and copy not yet specified — load the \`creative-direction\` skill to expand the brief and commit to a content/structure plan without asking for approval (skip it if the user gave a detailed spec or asked for minimal). Then load the \`visual-design\` skill to plan the visual direction: layout, colors, typography, and spacing. 3. **Write theme/plugin files**: For a brand new theme, call \`scaffold_theme\` first — it drops an unopinionated block-theme baseline (style.css with only the theme header, theme.json with appearanceTools only, functions.php with frontend + editor style enqueue, default templates and parts, empty assets/fonts and patterns dirs) and activates it by default. Then use Write and Edit to fill the scaffold (one part/template/file per turn). For plugins or for editing an existing theme, use Write and Edit directly under the site's wp-content/themes/ or wp-content/plugins/ directory. 4. **Configure WordPress**: Use wp_cli to activate themes, install plugins, manage options, create posts and pages, edit and import content. The site must be running. Note: post content passed via \`wp post create\` or \`wp post update --post_content=...\` need to be pre-validated for editability, follow the \`block-content\` skill, checked with validate_html_blocks, and validated/fixed with validate_and_fix_blocks. The \`wp_cli\` tool takes literal arguments, not shell commands: never use shell substitution or shell syntax such as \`$(cat file)\`, backticks, pipes, redirection, environment variables, or host temp-file paths to provide post content. Pass the literal content directly in \`--post_content=...\`, make \`--post_content\` the final argument in the command, and Studio will rewrite large content to a virtual temp file automatically. 5. **Check and fix block validity**: Run validate_html_blocks on block content first. If it reports invalid core/html blocks, rewrite only those blocks as editable core or plugin blocks and call validate_html_blocks again. Then call validate_and_fix_blocks with filePath whenever the content lives in a file. If validate_and_fix_blocks says an auto-fix was applied, the file already contains the fixed block content; do not manually replace markup or call validation again unless you intentionally change block markup afterward. Use the diff only to inspect class/nesting changes and update CSS selectors if needed. For inline content, use any returned fixed block content exactly as the replacement content. @@ -218,4 +218,6 @@ For any site creation, redesign, landing page, homepage, layout, style, CSS, typ For any page/post content, template or template-part content, block markup, block-theme layout, full-width section, or \`core/html\` use, load the \`block-content\` skill before writing markup or validating block content. -For forms, ecommerce, events, LMS, galleries/slideshows, embeds, SEO/performance plugin choices, or any feature that core WordPress blocks do not cleanly provide, load the \`plugin-recommendations\` skill before installing plugins or writing plugin-provided block markup.`; +For forms, ecommerce, events, LMS, galleries/slideshows, embeds, SEO/performance plugin choices, or any feature that core WordPress blocks do not cleanly provide, load the \`plugin-recommendations\` skill before installing plugins or writing plugin-provided block markup. + +For new sites built from a vague brief — site type clear (e.g. "a site for a bar") but pages, sections, or copy not yet specified — load the \`creative-direction\` skill before deciding pages, sections, or design direction. If the brief is already detailed, or the user asks for "minimal" or "one page", skip this skill.`; diff --git a/apps/cli/ai/tests/creative-direction-skill.test.ts b/apps/cli/ai/tests/creative-direction-skill.test.ts new file mode 100644 index 0000000000..8e74916598 --- /dev/null +++ b/apps/cli/ai/tests/creative-direction-skill.test.ts @@ -0,0 +1,108 @@ +import { describe, expect, it } from 'vitest'; +import { findSkill, loadSkills } from '../skills'; +import { buildSystemPrompt } from '../system-prompt'; + +describe( 'creative-direction skill', () => { + describe( 'discoverability', () => { + it( 'is listed among available skills', () => { + const names = loadSkills().map( ( s ) => s.name ); + expect( names ).toContain( 'creative-direction' ); + } ); + + it( 'has a description', () => { + const skill = findSkill( 'creative-direction' ); + expect( skill?.description ).toBeTruthy(); + expect( skill?.description.length ).toBeGreaterThan( 20 ); + } ); + + it( 'is not user-invokable (internal skill)', () => { + // Creative direction is loaded by the agent automatically, not typed by users. + // Verify the frontmatter doesn't mark it as user-invokable. + const skill = findSkill( 'creative-direction' ); + // The skill body is everything after the frontmatter — user-invokable: true + // would appear in the raw file but is stripped from body by parseSkillFile. + // We test intent via the description not advertising it as a slash command. + expect( skill?.description ).not.toMatch( /slash command|type \//i ); + } ); + } ); + + describe( 'skill body content', () => { + it( 'has a Step 0 that selects the expansion mode', () => { + const skill = findSkill( 'creative-direction' ); + // Step 0 should distinguish auto-expand, guided (one question), and skip paths + expect( skill?.body ).toMatch( /step 0/i ); + expect( skill?.body ).toMatch( /auto.?expand|auto.?expan/i ); + expect( skill?.body ).toMatch( /one question/i ); + expect( skill?.body ).toMatch( /skip this skill/i ); + } ); + + it( 'limits guided mode to a single clarifying question', () => { + const skill = findSkill( 'creative-direction' ); + // Must not fan out into a multi-question wizard + expect( skill?.body ).toMatch( /one question rule|single.*question|ask.*one/i ); + } ); + + it( 'covers site-type detection', () => { + const skill = findSkill( 'creative-direction' ); + expect( skill?.body ).toContain( 'Bar' ); + expect( skill?.body ).toContain( 'Restaurant' ); + expect( skill?.body ).toContain( 'SaaS' ); + expect( skill?.body ).toContain( 'Portfolio' ); + } ); + + it( 'includes per-type content plans with pages and forms', () => { + const skill = findSkill( 'creative-direction' ); + // Bar type should specify concrete pages and a reservations form + expect( skill?.body ).toContain( 'Menu' ); + expect( skill?.body ).toContain( 'Events' ); + expect( skill?.body ).toContain( 'Gallery' ); + expect( skill?.body ).toContain( 'reservations' ); + } ); + + it( 'instructs the agent to brief the user before building', () => { + const skill = findSkill( 'creative-direction' ); + expect( skill?.body ).toMatch( /brief the user|tell the user/i ); + expect( skill?.body ).toMatch( /do not ask for approval|proceed.*without asking/i ); + } ); + + it( 'includes a skip condition for detailed prompts', () => { + const skill = findSkill( 'creative-direction' ); + expect( skill?.body ).toMatch( /when to skip/i ); + expect( skill?.body ).toMatch( /detailed content|keep it minimal/i ); + } ); + + it( 'instructs the agent to use the site name as a design prompt', () => { + const skill = findSkill( 'creative-direction' ); + expect( skill?.body ).toMatch( /name.*creative|name.*brief|use.*name/i ); + } ); + } ); + + describe( 'system prompt integration', () => { + it( 'is referenced in the local site workflow', () => { + const prompt = buildSystemPrompt(); + expect( prompt ).toContain( 'creative-direction' ); + } ); + + it( 'is invoked during the design planning step', () => { + const prompt = buildSystemPrompt(); + // Verify the creative-direction skill reference appears in the workflow + // context (Plan the design step), not just anywhere in the prompt. + const planStep = prompt.match( /\*\*Plan the design\*\*[^\n]*/ )?.[ 0 ] ?? ''; + expect( planStep ).toContain( 'creative-direction' ); + } ); + + it( 'is NOT referenced in the remote site workflow', () => { + // Remote sites use the REST API workflow — creative direction is local only. + const prompt = buildSystemPrompt( { + remoteSite: { name: 'Test Site', url: 'https://test.wordpress.com', id: 123 }, + } ); + expect( prompt ).not.toContain( 'creative-direction' ); + } ); + + it( 'instructs the agent to proceed without asking for approval', () => { + const prompt = buildSystemPrompt(); + const planStep = prompt.match( /\*\*Plan the design\*\*[^\n]*/ )?.[ 0 ] ?? ''; + expect( planStep ).toContain( 'without asking for approval' ); + } ); + } ); +} );