🎁 Get the FREE AI Skills Starter Guide β€” Subscribe β†’
BytesAgainBytesAgain
πŸ¦€ ClawHub

VVVLINK Site Builder

by @vladlat

Build and publish websites on vvvlink.com. Creates HTML sites, uploads to VVVLink API, publishes with unique subdomain URLs. Triggers on: "create a site", "b...

Versionv1.8.1
Downloads422
TERMINAL
clawhub install vvvlink-site-builder

πŸ“– About This Skill


name: vvvlink-site-builder description: > Build and publish websites on vvvlink.com. Creates HTML sites, uploads to VVVLink API, publishes with unique subdomain URLs. Triggers on: "create a site", "build a website", "landing page", "website", "portfolio", "publish site", "deploy site". config: path: ~/.vvvlink/config.json description: > Stores account UUID and API key. Created automatically on first use via POST /auth/create_new_user. API key is stored with chmod 600 permissions. persistence: - ~/.vvvlink/config.json (account credentials) - ~/.vvvlink/sites// (generated site files) api: base: https://publish.vvvlink.com auth: Bearer API key (stored in config.json)

VVVLink Sitebuilder

Use this skill when the user wants to create, redesign, or publish a website, landing page, portfolio, or any HTML page that should be accessible online.

Before ANY work, announce to the user: "Using vvvlink-site-builder to create and publish your site."

Quick Reference

  • API Base: https://publish.vvvlink.com
  • Site creation rules: See website-development-rules.md
  • Full API docs: See api-reference.md

    Direct Invocation

    When the user invokes this skill directly (without a specific task), show an intro and check for existing sites. Use Telegram formatting.

    1. Load API key from ~/.vvvlink/config.json and call GET /sites (if config exists) 2. Split sites by status: "live" and non-live ("uploading", etc.) 3. Show intro message based on result:

    If user has live sites:

    VVVLink Site Builder

    Your sites: 1. my-site.vvvlink.com 2. cool-page.vvvlink.com

    +2 drafts (uploading). Ask me to show or clean them up.

    I can update any of these or create a new one. What would you like to do?

    Only show the drafts line if there are non-live sites. If the user asks to see or manage drafts, list them with subdomain, status, and offer to delete individual ones or all at once via DELETE /sites/:siteId.

    If no sites or no API key:

    VVVLink Site Builder

    I build and publish websites on vvvlink.com in seconds.

    Describe what you need β€” a landing page, portfolio, business site β€” and I'll design, build, and publish it.

    Your site will be live with a unique URL right away.

    When user picks an existing site to update, go to Step 10 (Updates) in the flow below.

    Flow (strict order, no skipping)

    Step 1: Announce and show progress

    THIS IS THE MOST IMPORTANT RULE IN THIS SKILL.

    The user sees your text output as a stream. While you write text β€” they see it appearing live. When you make a tool call β€” they see nothing until the tool returns and you write more text.

    Therefore: WRITE TEXT BEFORE AND AFTER EVERY TOOL CALL.

    In practice, your response should look like this (the user sees each line appear as you stream it):

    ⚑ Building your site with VVVLink Site Builder...

    πŸ” Searching for photos of [topic]... [tool call: curl image search] πŸ“Έ Found 5 great shots!

    🎨 Designing layout with [palette] and [fonts]... πŸ—οΈ Building hero section... [tool call: write first part of HTML] ✨ Adding content sections... [tool call: write rest of HTML] πŸ“¦ HTML ready!

    πŸš€ Publishing to vvvlink.com... [tool call: create site] [tool call: upload file] [tool call: publish] [tool call: rename subdomain]

    βœ… Your site is live! 🌐 https://your-site.vvvlink.com

    KEY RULES:

  • Write at least one emoji + text line BEFORE each tool call
  • The user must always see what you're doing RIGHT NOW
  • NEVER make 2+ tool calls in a row without text between them
  • If a tool call takes time, the preceding text is what the
  • user will see while waiting β€” make it informative

    IMPORTANT: These progress rules apply ONLY when BUILDING a site (multi-step process: photos β†’ HTML β†’ upload β†’ publish). For simple operations like listing sites or checking status β€” just do it quickly, no need for multiple progress messages. One short message before + result is enough.

    Step 1.5: Ask for business details (if missing)

    If the site looks like a business page (cafe, shop, studio, agency, etc.) and the user did NOT provide key info, gently ask ONCE before building. Keep it short β€” one message:

    "πŸ“‹ Before I start β€” want to add any of these to make it real? πŸ“ž Phone / email / address πŸ• Opening hours πŸ”— Social media links

    Or I can build it now and you'll add details later."

    If user says "just build it" or similar β€” proceed immediately. Do NOT block the build. This is optional, not a gate. Skip this step entirely for non-business sites (portfolios, personal pages, experiments).

    Step 2: Find photos for the site

    Before writing any code, search for relevant photos:

    # Search by keywords matching the site's topic
    curl -s "https://publish.vvvlink.com/images/search?q=KEYWORD&count=5&orientation=landscape"
    

    Pick 3-6 keywords based on the site content (e.g., "office", "team", "technology"). Select the best photo for each section (hero, about, features, etc.). Use the url field from the response directly in tags.

    Add &w=1600&h=900&fit=crop to URLs for hero images, &w=600&h=400&fit=crop for cards.

    Step 3: Create the site

    Read and follow references/website-development-rules.md. Build a high-quality static site using the photos from Step 2. Output all files to ~/.vvvlink/sites//.

    Step 4: Get API key

    apiKey is SECRET. Never show it to the user, never log it, never include in messages. It is only for Authorization headers.

    UUID is public. It identifies the account. You may show it to the user if asked.

    Storage: ~/.vvvlink/config.json

    {
      "uuid": "account-uuid",
      "apiKey": "SECRET-key-never-share"
    }
    

    Site files: ~/.vvvlink/sites//

    Rules: 1. NEVER call /auth/create_new_user if ~/.vvvlink/config.json exists. The apiKey is issued ONCE and cannot be recovered. 2. Only create a new account if config.json does not exist. 3. Protect ~/.vvvlink/config.json with chmod 600.

    Procedure: 1. Read ~/.vvvlink/config.json β†’ use .apiKey 2. If file does not exist β†’ POST /auth/create_new_user {} β†’ save response to config.json

    # Load or create API credentials
    VVVLINK_DIR="$HOME/.vvvlink"
    VVVLINK_CONFIG="$VVVLINK_DIR/config.json"

    if [ -f "$VVVLINK_CONFIG" ]; then VVVLINK_API_KEY=$(jq -r '.apiKey' "$VVVLINK_CONFIG") else mkdir -p "$VVVLINK_DIR" RESP=$(curl -s -X POST https://publish.vvvlink.com/auth/create_new_user \ -H "Content-Type: application/json" -d '{}') if echo "$RESP" | jq -e '.apiKey' > /dev/null 2>&1; then VVVLINK_API_KEY=$(echo "$RESP" | jq -r '.apiKey') echo "$RESP" | jq '{uuid: .uuid, apiKey: .apiKey}' \ > "$VVVLINK_CONFIG" chmod 600 "$VVVLINK_CONFIG" else echo "Error: could not create account" >&2 exit 1 fi fi

    Step 5: Create site and upload

    # Create site
    curl -s -X POST https://publish.vvvlink.com/sites \
      -H "Authorization: Bearer $API_KEY"

    Upload each file (from ~/.vvvlink/sites//)

    curl -s -X PUT \ "https://publish.vvvlink.com/sites/$SITE_ID/files/index.html" \ -H "Authorization: Bearer $API_KEY" \ --data-binary @~/.vvvlink/sites/$SUBDOMAIN/index.html

    Upload ALL files: HTML, CSS, JS, images, fonts.

    If POST /sites returns HTTP 429 with "Site limit reached", the user has hit their plan limit. Handle this gracefully:

    1. Call the upgrade endpoint to get a checkout URL:

    curl -s -X POST https://publish.vvvlink.com/billing/upgrade \
      -H "Authorization: Bearer $API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"success_url": "https://vvvlink.com"}'
    

    2. Show the user an upgrade message with the payment link. In Telegram, use an inline link so the user can pay in the built-in web view:

    ⚠️ *Site limit reached*

    You've used all available sites on the Free plan\.

    πŸš€ Upgrade to Pro to unlock up to 100 sites\.

    _After upgrading, I'll publish your site right away\._

    3. After the user pays and confirms, retry POST /sites. The limit will be updated automatically.

    If the upgrade endpoint returns "Already on Pro plan", the user is already on Pro β€” this means they've genuinely used all 100 sites. Tell them to delete unused sites first.

    Step 6: Publish

    curl -s -X POST \
      "https://publish.vvvlink.com/sites/$SITE_ID/publish" \
      -H "Authorization: Bearer $API_KEY"
    

    Step 7: Choose subdomain BEFORE creating site

    Do NOT create a site with a random name. Pick a good one first:

    1. Analyze the site content (</code>, <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em"><h1></code>, topic) 2. Generate 10 slug-friendly name candidates 3. Check availability via POST /subdomains/check-bulk 4. Pick the BEST available name 5. Pass it when creating the site:</p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">curl -s -X POST https://publish.vvvlink.com/sites \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{"subdomain": "chosen-name"}' </code></pre></p><p style="margin:8px 0">The API will use your name if available, or fall back to a random one if taken. Check the response <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">subdomain</code> field to confirm which name was assigned.</p><p style="margin:8px 0">6. Show the user the live URL and other available options:</p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">πŸš€ Your site is live!</p><p style="margin:8px 0">🌐 https://chosen-name.vvvlink.com</p><p style="margin:8px 0">Want a different name? 1. alternative-one 2. alternative-two 3. alternative-three</p><p style="margin:8px 0">Reply with a number or type your own. </code></pre></p><p style="margin:8px 0">If user picks a different name, rename via: <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">curl -s -X PUT \ "https://publish.vvvlink.com/sites/$SITE_ID/subdomain" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "new-name"}' </code></pre></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Step 8: Show URL and STOP</h4></p><p style="margin:8px 0">When showing the final URL, add <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">?v=VERSION</code> to bust cache. Use the version number from the publish response. Use markdown link to hide the param:</p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em"><a href="https://your-site.vvvlink.com?v=2" target="_blank" rel="noopener" style="color:#6366f1">your-site.vvvlink.com</a> </code></pre></p><p style="margin:8px 0">The user sees clean URL but clicks through to the latest version.</p><p style="margin:8px 0">After showing the live URL and rename options β€” STOP. Do NOT automatically start improving, searching more photos, or making changes. Wait for the user to respond.</p><p style="margin:8px 0">You may add ONE short line like: "Want me to tweak anything β€” colors, content, add a section?"</p><p style="margin:8px 0">But do NOT take any action until the user asks.</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Step 9: Updates</h4></p><p style="margin:8px 0">To update an existing site:</p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">curl -s -X POST \ "https://publish.vvvlink.com/sites/$SITE_ID/new-version" \ -H "Authorization: Bearer $API_KEY" <h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Upload new files, then publish again</h2> </code></pre></p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Rules</h3></p><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0">Publish immediately without asking for confirmation</li> <li style="color:#94a3b8;margin:3px 0">For updates to existing sites, mention that the user can</li> roll back to a previous version or view version history <li style="color:#94a3b8;margin:3px 0">Always offer rename after first publish</li> <li style="color:#94a3b8;margin:3px 0">Store API key β€” don't re-init every time</li> <li style="color:#94a3b8;margin:3px 0">Upload ALL site files (HTML, CSS, JS, images, fonts)</li> <li style="color:#94a3b8;margin:3px 0">Max 10MB per site for free users</li></p><p style="margin:8px 0"><ERROR-HANDLING> NEVER let a script error silently kill the build.</p><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0">Do NOT use <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">set -e</code> in shell scripts</li> <li style="color:#94a3b8;margin:3px 0">ALWAYS check API response before parsing with jq:</li> <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em"> RESP=$(curl -s -X POST .../sites -H "Authorization: Bearer $KEY") SITE_ID=$(echo "$RESP" | jq -r '.siteId // empty') if [ -z "$SITE_ID" ]; then # Show error to user, don't silently fail echo "Error creating site: $RESP" # Try to recover or explain fi </code></pre> <li style="color:#94a3b8;margin:3px 0">If ANY step fails β€” tell the user what happened and what</li> to do next. NEVER end with a raw error or empty response. <li style="color:#94a3b8;margin:3px 0">Show the user a human-readable message, not jq errors or</li> raw JSON. <li style="color:#94a3b8;margin:3px 0">Use ONLY bash, curl, and jq. NEVER use python, python3,</li> node, ruby, or any other interpreter. There is NO python on the server. If you try to use python β€” IT WILL FAIL. Everything can be done with bash + curl + jq. <ABSOLUTE-BAN> NEVER EVER put HTML inside bash. This is the #1 cause of failures and MUST be followed without exception.</p><p style="margin:8px 0">BANNED β€” all of these WILL break: <li style="color:#94a3b8;margin:3px 0">heredoc: cat > file << EOF ... EOF</li> <li style="color:#94a3b8;margin:3px 0">echo: echo '<html>...' > file</li> <li style="color:#94a3b8;margin:3px 0">printf: printf '%s' '<html>...'</li> <li style="color:#94a3b8;margin:3px 0">inline: curl -d '<html>...'</li> <li style="color:#94a3b8;margin:3px 0">any bash command containing HTML</li></p><p style="margin:8px 0">REQUIRED β€” the ONLY correct way: 1. Use your Write/file tool to create the HTML file 2. Then upload it with curl (separate tool call): <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em"> curl -s -X PUT \ "https://publish.vvvlink.com/sites/$SITE_ID/files/index.html" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: text/html" \ --data-binary @~/.vvvlink/sites/my-site/index.html </code></pre></p><p style="margin:8px 0">Two separate tool calls. NEVER combine them. If you put HTML in bash β€” it WILL fail. Every time. </ABSOLUTE-BAN> <li style="color:#94a3b8;margin:3px 0">If you get HTTP 403 or 1010 β€” retry the request once.</li> Cloudflare may temporarily block requests. If retry fails, tell the user: "Temporary access issue, try again in a moment." </ERROR-HANDLING></p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Message Formatting</h3></p><p style="margin:8px 0">Messages to the user MUST be visually polished. Format for the platform you are running on (Telegram, CLI, etc). Do NOT hardcode escape characters β€” let your platform handle formatting.</p><p style="margin:8px 0"><strong style="color:#e5e7eb">Progress updates β€” use status indicators:</strong> <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">Searching for photos... Building your site... Uploading files (3/3)... Publishing... Done! </code></pre></p><p style="margin:8px 0"><strong style="color:#e5e7eb">Final result β€” clean, no technical details:</strong> <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">πŸš€ Your site is live!</p><p style="margin:8px 0">yoursite.vvvlink.com</p><p style="margin:8px 0">Want a different name? Here are some options: </code></pre></p><p style="margin:8px 0">Do NOT show file names, file counts, photo counts, version numbers, or any other technical details in the final message. The user only cares about the URL.</p><p style="margin:8px 0"><strong style="color:#e5e7eb">Alternative names β€” simple numbered list, no "available":</strong> <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">Want a different name? 1. cool-startup 2. my-project 3. awesome-landing</p><p style="margin:8px 0">Reply with a number or type your own. </code></pre></p><p style="margin:8px 0"><strong style="color:#e5e7eb">Key formatting rules:</strong> <li style="color:#94a3b8;margin:3px 0">Keep messages compact β€” no walls of text</li> <li style="color:#94a3b8;margin:3px 0">Clickable links for the published URL (always)</li> <li style="color:#94a3b8;margin:3px 0">Never dump raw JSON, curl output, or API responses</li></p><p style="margin:8px 0"><COMPLETION-CRITERIA> THE TASK IS NOT DONE UNTIL THE USER SEES A LIVE URL.</p><p style="margin:8px 0">If you showed progress messages ("Building...", "Searching...") but did NOT finish with a live URL β€” YOU FAILED. Go back and complete all remaining steps.</p><p style="margin:8px 0">Checklist β€” ALL must be true: 1. Site HTML was created and written to file 2. Site was uploaded via API (create + upload + publish) 3. User received a CLICKABLE live URL (https://*.vvvlink.com) 4. Rename was offered</p><p style="margin:8px 0">If ANY tool call fails or times out β€” retry or tell the user what happened. NEVER silently stop in the middle.</p><p style="margin:8px 0">NEVER end your response after a progress message. The last message the user sees MUST contain either: <li style="color:#94a3b8;margin:3px 0">A live URL (success), OR</li> <li style="color:#94a3b8;margin:3px 0">An error explanation with next steps (failure)</li> </COMPLETION-CRITERIA> </p></div></section><section class="skill-card" style="margin-bottom:20px"><h2 style="color:#f8fafc;font-size:1.2em;font-weight:800;margin:0 0 16px;display:flex;align-items:center;gap:8px">πŸ”’ Constraints</h2><div style="font-size:.92em;color:#94a3b8;line-height:1.75"><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0">Publish immediately without asking for confirmation</li> <li style="color:#94a3b8;margin:3px 0">For updates to existing sites, mention that the user can</li> roll back to a previous version or view version history <li style="color:#94a3b8;margin:3px 0">Always offer rename after first publish</li> <li style="color:#94a3b8;margin:3px 0">Store API key β€” don't re-init every time</li> <li style="color:#94a3b8;margin:3px 0">Upload ALL site files (HTML, CSS, JS, images, fonts)</li> <li style="color:#94a3b8;margin:3px 0">Max 10MB per site for free users</li></p><p style="margin:8px 0"><ERROR-HANDLING> NEVER let a script error silently kill the build.</p><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0">Do NOT use <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em" style="background:#0a0a1c;color:#fbbf24;padding:1px 5px;border-radius:3px;font-size:.85em">set -e</code> in shell scripts</li> <li style="color:#94a3b8;margin:3px 0">ALWAYS check API response before parsing with jq:</li> <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em" style="background:#0a0a1c;color:#fbbf24;padding:1px 5px;border-radius:3px;font-size:.85em"> RESP=$(curl -s -X POST .../sites -H "Authorization: Bearer $KEY") SITE_ID=$(echo "$RESP" | jq -r '.siteId // empty') if [ -z "$SITE_ID" ]; then # Show error to user, don't silently fail echo "Error creating site: $RESP" # Try to recover or explain fi </code></pre> <li style="color:#94a3b8;margin:3px 0">If ANY step fails β€” tell the user what happened and what</li> to do next. NEVER end with a raw error or empty response. <li style="color:#94a3b8;margin:3px 0">Show the user a human-readable message, not jq errors or</li> raw JSON. <li style="color:#94a3b8;margin:3px 0">Use ONLY bash, curl, and jq. NEVER use python, python3,</li> node, ruby, or any other interpreter. There is NO python on the server. If you try to use python β€” IT WILL FAIL. Everything can be done with bash + curl + jq. <ABSOLUTE-BAN> NEVER EVER put HTML inside bash. This is the #1 cause of failures and MUST be followed without exception.</p><p style="margin:8px 0">BANNED β€” all of these WILL break: <li style="color:#94a3b8;margin:3px 0">heredoc: cat > file << EOF ... EOF</li> <li style="color:#94a3b8;margin:3px 0">echo: echo '<html>...' > file</li> <li style="color:#94a3b8;margin:3px 0">printf: printf '%s' '<html>...'</li> <li style="color:#94a3b8;margin:3px 0">inline: curl -d '<html>...'</li> <li style="color:#94a3b8;margin:3px 0">any bash command containing HTML</li></p><p style="margin:8px 0">REQUIRED β€” the ONLY correct way: 1. Use your Write/file tool to create the HTML file 2. Then upload it with curl (separate tool call): <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em" style="background:#0a0a1c;color:#fbbf24;padding:1px 5px;border-radius:3px;font-size:.85em"> curl -s -X PUT \ "https://publish.vvvlink.com/sites/$SITE_ID/files/index.html" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: text/html" \ --data-binary @~/.vvvlink/sites/my-site/index.html </code></pre></p><p style="margin:8px 0">Two separate tool calls. NEVER combine them. If you put HTML in bash β€” it WILL fail. Every time. </ABSOLUTE-BAN> <li style="color:#94a3b8;margin:3px 0">If you get HTTP 403 or 1010 β€” retry the request once.</li> Cloudflare may temporarily block requests. If retry fails, tell the user: "Temporary access issue, try again in a moment." </ERROR-HANDLING></p></div></section></div><div class="two-col-side"></div></div></div><script> document.querySelectorAll('.copy-btn, .script-copy-btn').forEach(btn => { btn.addEventListener('click', () => { const cmd = btn.getAttribute('data-cmd'); if (!cmd) return; navigator.clipboard.writeText(cmd).then(() => { const orig = btn.textContent; btn.textContent = 'Copied!'; setTimeout(() => btn.textContent = orig, 1500); }).catch(() => {}); }); }); </script><!--$--><!--/$--></main><footer style="background:var(--bg-primary);border-top:1px solid var(--border-secondary);margin-top:60px"><div style="border-top:1px solid var(--border-light);max-width:1200px;margin:0 auto;padding:24px 20px"><div style="display:flex;justify-content:space-between;flex-wrap:wrap;gap:24px;margin-bottom:24px"><div><div style="font-weight:700;color:var(--text-muted);margin-bottom:8px">BytesAgain</div><div style="color:var(--text-muted3);font-size:.82em;max-width:200px">Discover the best AI agent skills for your workflow.</div></div><div><div style="color:var(--text-muted);font-size:.75em;text-transform:uppercase;letter-spacing:1px;margin-bottom:10px">Explore</div><div style="margin-bottom:6px"><a href="/skills" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Skills</a></div><div style="margin-bottom:6px"><a href="/articles" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Articles</a></div><div style="margin-bottom:6px"><a href="/use-case" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Cases</a></div></div><div><div style="color:var(--text-muted);font-size:.75em;text-transform:uppercase;letter-spacing:1px;margin-bottom:10px">Company</div><div style="margin-bottom:6px"><a href="/about" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">About</a></div><div style="margin-bottom:6px"><a href="/contact" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Contact</a></div><div style="margin-bottom:6px"><a href="/privacy-policy" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Privacy Policy</a></div><div style="margin-bottom:6px"><a href="/terms" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Terms</a></div><div style="margin-bottom:6px"><a href="/feedback" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Feedback</a></div></div></div><div style="border-top:1px solid var(--border-light);padding-top:16px"><div style="color:var(--text-muted4);font-size:.8em;margin-bottom:8px">Β© <!-- -->2026<!-- --> BytesAgain. All rights reserved.</div><div style="color:var(--text-muted5);font-size:.75em;line-height:1.6;max-width:720px">BytesAgain is an independent skill directory. We index and link to third-party content (ClawHub, GitHub, LobeHub, Dify, etc.) for informational purposes only. All trademarks, skill names, and content are the property of their respective owners. BytesAgain does not claim ownership of any indexed content.</div></div></div></footer><button style="position:fixed;bottom:28px;right:28px;z-index:1000;width:48px;height:48px;border-radius:50%;border:none;cursor:pointer;background:linear-gradient(135deg,#667eea,#00d4ff);color:#fff;font-size:1.3em;box-shadow:0 4px 20px #667eea66;display:flex;align-items:center;justify-content:center;transition:transform .2s">πŸ’¬</button><script src="/_next/static/chunks/0ze4gu236oq96.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[62894,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"LangProvider\"]\n3:I[89220,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"ThemeProvider\"]\n4:I[16988,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\ne:I[68027,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\",1]\n:HL[\"/_next/static/chunks/051nc0vy_6.rl.css?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"style\"]\n:HL[\"/_next/static/media/caa3a2e1cccd8315-s.p.09~u27dqhyhd6.woff2?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n5:Td5e,"])</script><script>self.__next_f.push([1,"[{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"BytesAgain\",\"url\":\"https://bytesagain.com\",\"description\":\"Search 60,000+ verified AI agent skills via MCP API or REST. Supports 7 languages. Free, no auth required.\",\"inLanguage\":[\"en\",\"zh\",\"es\",\"fr\",\"de\",\"ja\",\"ko\"],\"potentialAction\":{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https://bytesagain.com/skills?q={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}},{\"@context\":\"https://schema.org\",\"@type\":\"Organization\",\"name\":\"BytesAgain\",\"url\":\"https://bytesagain.com\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://bytesagain.com/og-image.png\"},\"description\":\"AI agent skill directory. Search 60,000+ skills, 1,000+ use cases, and community requests.\",\"foundingDate\":\"2026\",\"foundingLocation\":{\"@type\":\"Place\",\"name\":\"Global\"},\"sameAs\":[\"https://x.com/bytesagain\",\"https://github.com/bytesagain/ai-skills\",\"https://clawhub.ai/profile/bytesagain\"],\"contactPoint\":{\"@type\":\"ContactPoint\",\"email\":\"hello@bytesagain.com\",\"contactType\":\"customer support\"},\"numberOfEmployees\":{\"@type\":\"QuantitativeValue\",\"value\":1}},{\"@context\":\"https://schema.org\",\"@type\":\"WebApplication\",\"name\":\"BytesAgain AI Skills Search\",\"url\":\"https://bytesagain.com\",\"applicationCategory\":\"DeveloperApplication\",\"operatingSystem\":\"Web\",\"description\":\"Search engine and MCP API for 60,000+ AI agent skills. Semantic search, role recommendations, and use case packs.\",\"offers\":{\"@type\":\"Offer\",\"price\":\"0\",\"priceCurrency\":\"USD\"},\"featureList\":[\"Search 60,000+ AI agent skills\",\"Role-based recommendations for developers, creators, and traders\",\"1,000+ curated use case packs\",\"Free MCP API and REST API\",\"Multi-language search (EN, ZH, ES, FR, DE, JA, KO)\"],\"potentialAction\":{\"@type\":\"SearchAction\",\"target\":\"https://bytesagain.com/skills?q={search_term_string}\",\"query-input\":\"required name=search_term_string\"},\"dateModified\":\"2026-07-18\"},{\"@context\":\"https://schema.org\",\"@type\":\"FAQPage\",\"mainEntity\":[{\"@type\":\"Question\",\"name\":\"What is BytesAgain?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"BytesAgain is a curated directory of 60,000+ AI agent skills from ClawHub, GitHub, LobeHub, and Dify. Search skills by keyword in 7 languages, browse by role (developer, creator, trader, marketer) or by use case.\"}},{\"@type\":\"Question\",\"name\":\"How do I find AI skills on BytesAgain?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Use the search bar on BytesAgain.com to search by keyword in 7 languages. You can also browse by role (developer, creator, trader, marketer) or by use case. Each skill shows install instructions for Claude, Cursor, OpenClaw, Continue, and more.\"}},{\"@type\":\"Question\",\"name\":\"Is BytesAgain free?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes, BytesAgain is completely free. No registration required for searching skills. The MCP API is also free with rate limits.\"}},{\"@type\":\"Question\",\"name\":\"Does BytesAgain have an API for AI agents?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes! BytesAgain provides a free MCP SSE endpoint at /api/mcp/sse for AI agents, plus a REST API at /api/mcp?action=search\u0026q=\u003cquery\u003e. No authentication needed.\"}},{\"@type\":\"Question\",\"name\":\"Can I request a new AI skill on BytesAgain?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes! Visit the Requests page on BytesAgain.com to submit a skill request. Your request will be visible to the community and notified to the site admin.\"}}]}]"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"c\":[\"\",\"skill\",\"vvvlink-site-builder\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"skill\",{\"children\":[[\"slug\",\"vvvlink-site-builder\",\"d\",null],{\"children\":[\"__PAGE__\",{}]}]}]},\"$undefined\",\"$undefined\",16],[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/051nc0vy_6.rl.css?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-2\",{\"src\":\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[[\"$\",\"head\",null,{\"children\":[[\"$\",\"link\",null,{\"rel\":\"llms\",\"href\":\"/llms.txt\"}],[\"$\",\"link\",null,{\"rel\":\"llms-full\",\"href\":\"/llms-full.txt\"}],[\"$\",\"script\",null,{\"async\":true,\"src\":\"https://www.googletagmanager.com/gtag/js?id=G-3C1MM9FWYF\"}],[\"$\",\"script\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"\\n window.dataLayer = window.dataLayer || [];\\n function gtag(){dataLayer.push(arguments);}\\n gtag('js', new Date());\\n gtag('config', 'G-3C1MM9FWYF');\\n \"}}]]}],[\"$\",\"body\",null,{\"className\":\"geist_9e050971-module__05dp7a__className\",\"style\":{\"margin\":0},\"children\":[\"$\",\"$L2\",null,{\"children\":[\"$\",\"$L3\",null,{\"children\":[[\"$\",\"div\",null,{\"style\":{\"width\":\"100%\",\"background\":\"var(--bg-subscribe)\",\"borderBottom\":\"1px solid var(--border-primary)\",\"padding\":\"8px 20px\",\"textAlign\":\"center\",\"fontSize\":\".82em\",\"color\":\"#818cf8\"},\"children\":[\"🎁 \",[\"$\",\"strong\",null,{\"style\":{\"color\":\"var(--text-primary)\"},\"children\":\"Get the FREE AI Skills Starter Guide\"}],\" β€” \",[\"$\",\"a\",null,{\"href\":\"/register\",\"style\":{\"color\":\"#00d4ff\",\"textDecoration\":\"underline\"},\"children\":\"Subscribe β†’\"}]]}],[\"$\",\"$L4\",null,{}],[\"$\",\"script\",null,{\"type\":\"application/ld+json\",\"dangerouslySetInnerHTML\":{\"__html\":\"$5\"}}],\"$L6\",\"$L7\",\"$L8\"]}]}]}]]}]]}],{\"children\":[\"$L9\",{\"children\":[\"$La\",{\"children\":[\"$Lb\",{},null,false,null]},null,false,\"$@c\"]},null,false,\"$@c\"]},null,false,null],\"$Ld\",false]],\"m\":\"$undefined\",\"G\":[\"$e\",[\"$Lf\"]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\"}\n"])</script><script>self.__next_f.push([1,"10:I[39756,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n11:I[37457,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n12:I[22016,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0ka051yepewro.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"\"]\n13:I[90940,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n14:I[16397,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n16:I[97367,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"OutletBoundary\"]\n17:\"$Sreact.suspense\"\n1a:I[97367,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"ViewportBoundary\"]\n1c:I[97367,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"MetadataBoundary\"]\n"])</script><script>self.__next_f.push([1,"6:[\"$\",\"main\",null,{\"children\":[\"$\",\"$L10\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L11\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[\"$\",\"main\",null,{\"style\":{\"minHeight\":\"100vh\",\"display\":\"flex\",\"alignItems\":\"center\",\"justifyContent\":\"center\",\"background\":\"#050611\",\"color\":\"#e5e7eb\"},\"children\":[[\"$\",\"style\",null,{\"children\":\"\\n .nf-box { text-align: center; padding: 60px 32px; }\\n .nf-code { font-size: 6rem; font-weight: 900; color: #22d3ee; line-height: 1; margin: 0; }\\n .nf-title { font-size: 1.8rem; font-weight: 800; margin: 12px 0 8px; }\\n .nf-desc { color: var(--text-muted2); font-size: 1rem; margin-bottom: 32px; max-width: 440px; }\\n .nf-link { display: inline-block; padding: 12px 28px; background: linear-gradient(135deg,#34d399,#22d3ee); color: #000; font-weight: 900; border-radius: 12px; text-decoration: none; }\\n \"}],[\"$\",\"div\",null,{\"className\":\"nf-box\",\"children\":[[\"$\",\"p\",null,{\"className\":\"nf-code\",\"children\":\"404\"}],[\"$\",\"h1\",null,{\"className\":\"nf-title\",\"children\":\"Page Not Found\"}],[\"$\",\"p\",null,{\"className\":\"nf-desc\",\"children\":\"The skill or page you're looking for doesn't exist or has been moved.\"}],[\"$\",\"$L12\",null,{\"className\":\"nf-link\",\"href\":\"/\",\"children\":\"Back to BytesAgain\"}]]}]]}],[]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]\n"])</script><script>self.__next_f.push([1,"7:[\"$\",\"$L13\",null,{}]\n8:[\"$\",\"$L14\",null,{}]\n9:[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L10\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L11\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}]\na:[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L10\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L11\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}]\nb:[\"$\",\"$1\",\"c\",{\"children\":[\"$L15\",[[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/12w5ognupk9fb.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"$L16\",null,{\"children\":[\"$\",\"$17\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@18\"}]}]]}]\n19:[]\nc:\"$W19\"\nd:[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$L1a\",null,{\"children\":\"$L1b\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$L1c\",null,{\"children\":[\"$\",\"$17\",null,{\"name\":\"Next.Metadata\",\"children\":\"$L1d\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}]\nf:[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/051nc0vy_6.rl.css?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]\n"])</script><script>self.__next_f.push([1,"1b:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"1e:I[27201,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"IconMark\"]\n18:null\n"])</script><script>self.__next_f.push([1,"1d:[[\"$\",\"title\",\"0\",{\"children\":\"VVVLINK Site Builder β€” AI Agent Skill | BytesAgain | BytesAgain\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Build and publish websites on vvvlink.com. Creates HTML sites, uploads to VVVLink API, publishes with unique subdomain URLs. Triggers on: \\\"create a site\\\", \\\"b...\"}],[\"$\",\"meta\",\"2\",{\"name\":\"robots\",\"content\":\"index, follow\"}],[\"$\",\"meta\",\"3\",{\"name\":\"googlebot\",\"content\":\"index, follow, max-image-preview:large, max-snippet:-1\"}],[\"$\",\"meta\",\"4\",{\"name\":\"llms-txt\",\"content\":\"https://bytesagain.com/llms.txt\"}],[\"$\",\"meta\",\"5\",{\"name\":\"llms-full-txt\",\"content\":\"https://bytesagain.com/llms-full.txt\"}],[\"$\",\"link\",\"6\",{\"rel\":\"canonical\",\"href\":\"https://bytesagain.com/skill/vvvlink-site-builder\"}],[\"$\",\"meta\",\"7\",{\"name\":\"baidu-site-verification\",\"content\":\"codeva-0evUqX1TFs\"}],[\"$\",\"meta\",\"8\",{\"property\":\"og:title\",\"content\":\"VVVLINK Site Builder β€” AI Agent Skill | BytesAgain\"}],[\"$\",\"meta\",\"9\",{\"property\":\"og:description\",\"content\":\"Build and publish websites on vvvlink.com. Creates HTML sites, uploads to VVVLink API, publishes with unique subdomain URLs. Triggers on: \\\"create a site\\\", \\\"b...\"}],[\"$\",\"meta\",\"10\",{\"property\":\"og:url\",\"content\":\"https://bytesagain.com/skill/vvvlink-site-builder\"}],[\"$\",\"meta\",\"11\",{\"property\":\"og:site_name\",\"content\":\"BytesAgain\"}],[\"$\",\"meta\",\"12\",{\"property\":\"og:image\",\"content\":\"https://bytesagain.com/social-preview.png\"}],[\"$\",\"meta\",\"13\",{\"property\":\"og:image:width\",\"content\":\"1200\"}],[\"$\",\"meta\",\"14\",{\"property\":\"og:image:height\",\"content\":\"630\"}],[\"$\",\"meta\",\"15\",{\"property\":\"og:type\",\"content\":\"website\"}],[\"$\",\"meta\",\"16\",{\"name\":\"twitter:card\",\"content\":\"summary_large_image\"}],[\"$\",\"meta\",\"17\",{\"name\":\"twitter:title\",\"content\":\"VVVLINK Site Builder β€” AI Agent Skill | BytesAgain\"}],[\"$\",\"meta\",\"18\",{\"name\":\"twitter:description\",\"content\":\"Build and publish websites on vvvlink.com. Creates HTML sites, uploads to VVVLink API, publishes with unique subdomain URLs. Triggers on: \\\"create a site\\\", \\\"b...\"}],[\"$\",\"meta\",\"19\",{\"name\":\"twitter:image\",\"content\":\"https://bytesagain.com/social-preview.png\"}],[\"$\",\"meta\",\"20\",{\"name\":\"twitter:image:width\",\"content\":\"1200\"}],[\"$\",\"meta\",\"21\",{\"name\":\"twitter:image:height\",\"content\":\"630\"}],[\"$\",\"link\",\"22\",{\"rel\":\"icon\",\"href\":\"/favicon.ico?favicon.0x3dzn~oxb6tn.ico\",\"sizes\":\"256x256\",\"type\":\"image/x-icon\"}],[\"$\",\"$L1e\",\"23\",{}]]\n"])</script><script>self.__next_f.push([1,"1f:T1562,"])</script><script>self.__next_f.push([1,"\n .skill-page { max-width: 1100px; margin: 0 auto; padding: 32px 20px 80px; }\n .two-col { display: flex; gap: 32px; align-items: flex-start; }\n .two-col-main { flex: 1; min-width: 0; }\n .two-col-side { width: 300px; flex-shrink: 0; }\n @media (max-width: 860px) {\n .two-col { flex-direction: column; }\n .two-col-side { width: 100%; }\n }\n .breadcrumb { font-size: .82em; color: var(--text-muted2); margin-bottom: 28px; }\n .breadcrumb a { color: #818cf8; text-decoration: none; }\n .breadcrumb a:hover { text-decoration: underline; }\n .skill-card { background: var(--bg-card); border: 1px solid var(--border-card); border-radius: 20px; padding: 28px; margin-bottom: 24px; }\n .skill-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 20px; flex-wrap: wrap; }\n .skill-badges { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }\n .skill-top-actions { display: flex; align-items: center; gap: 10px; margin-left: auto; }\n .badge { display: inline-flex; align-items: center; gap: 5px; font-size: .75em; font-weight: 600; padding: 4px 12px; border-radius: 999px; border: 1px solid transparent; }\n .skill-title { font-size: 1.6em; font-weight: 800; color: var(--text-primary); margin: 0 0 4px; line-height: 1.2; }\n .skill-owner { font-size: .82em; color: var(--text-muted2); margin: 0 0 14px; }\n .skill-owner span { color: #818cf8; }\n .skill-desc { font-size: .92em; color: var(--text-secondary); line-height: 1.65; margin: 0 0 16px; }\n .skill-meta { display: flex; gap: 16px; flex-wrap: wrap; margin-bottom: 18px; padding-bottom: 16px; border-bottom: 1px solid var(--border-card); }\n .meta-item { display: flex; flex-direction: column; gap: 2px; }\n .meta-label { font-size: .7em; color: var(--text-muted5); text-transform: uppercase; letter-spacing: 1px; font-weight: 600; }\n .meta-value { font-size: .92em; color: var(--text-muted2); font-weight: 600; }\n .tags-row { display: flex; gap: 6px; flex-wrap: wrap; }\n .tag { font-size: .75em; color: #6366f1; background: #6366f115; border: 1px solid #6366f130; border-radius: 6px; padding: 3px 10px; text-decoration: none; }\n .tag:hover { background: #6366f125; }\n .install-box { background: var(--bg-deep); border: 1px solid var(--border-card); border-radius: 12px; overflow: hidden; margin-bottom: 24px; }\n .install-header { display: flex; align-items: center; justify-content: space-between; padding: 10px 16px; border-bottom: 1px solid var(--border-card); }\n .install-dots { display: flex; gap: 6px; }\n .dot { width: 10px; height: 10px; border-radius: 50%; }\n .install-label { font-size: .72em; color: var(--text-muted5); font-family: monospace; letter-spacing: 1px; }\n .install-body { padding: 16px 20px; display: flex; align-items: center; justify-content: space-between; gap: 12px; }\n .install-cmd { color: var(--text-code);\n font-family: 'Courier New', monospace; font-size: 1em; }\n .copy-btn { font-size: .75em; color: #6366f1; background: #6366f115; border: 1px solid #6366f130; border-radius: 6px; padding: 5px 12px; cursor: pointer; white-space: nowrap; transition: all .15s; }\n .copy-btn:hover { background: #6366f125; }\n .btn-secondary { display: inline-flex; align-items: center; gap: 8px; padding: 13px 24px; background: transparent; border: 1px solid var(--border-card); border-radius: 10px; color: #6b7280; text-decoration: none; font-weight: 600; font-size: .95em; transition: all .15s; }\n .btn-secondary:hover { border-color: #818cf8; color: #818cf8; }\n .ours-badge { display: inline-flex; align-items: center; gap: 6px; font-size: .72em; font-weight: 700; color: #22d3ee; background: #22d3ee10; border: 1px solid #22d3ee30; border-radius: 999px; padding: 4px 14px; }\n .section-card { background: var(--bg-card); border: 1px solid var(--border-card); border-radius: 16px; padding: 22px 24px; margin-bottom: 20px; }\n .section-title { color: var(--text-primary); font-size: 1.08em; font-weight: 800; margin: 0 0 12px; display: flex; align-items: center; gap: 8px; }\n /* Script box */\n .script-header { display: flex; align-items: center; justify-content: space-between; padding: 8px 14px; background: var(--bg-input); border-bottom: 1px solid var(--border-card); }\n .script-filename { font-size: .72em; color: var(--text-muted2); font-family: 'Courier New', monospace; }\n .script-copy-btn { font-size: .72em; color: #6366f1; background: none; border: 1px solid #6366f130; border-radius: 4px; padding: 2px 10px; cursor: pointer; }\n .script-copy-btn:hover { background: #6366f115; }\n .script-body { padding: 14px 16px; font-family: 'Courier New', monospace; font-size: .82em; line-height: 1.6; color: var(--text-code); overflow-x: auto; max-height: 420px; overflow-y: auto; white-space: pre; }\n /* Articles */\n .article-card { display: block; background: var(--bg-secondary); border: 1px solid var(--border-primary); border-radius: 10px; padding: 14px 16px; text-decoration: none; transition: border-color .15s; }\n .article-card:hover { border-color: #6366f1; }\n @media (max-width: 600px) {\n .skill-card { padding: 20px; }\n .skill-title { font-size: 1.5em; }\n }\n "])</script><script>self.__next_f.push([1,"15:[[\"$\",\"style\",null,{\"children\":\"$1f\"}],\"$L20\",\"$L21\"]\n"])</script><script>self.__next_f.push([1,"22:I[78297,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/12w5ognupk9fb.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n23:T6d1b,"])</script><script>self.__next_f.push([1,"\u003cp style=\"margin:8px 0\"\u003e\u003chr style=\"border:none;border-top:1px solid #1e1e3f;margin:12px 0\"\u003e\nname: vvvlink-site-builder\ndescription: \u003e\n Build and publish websites on vvvlink.com. Creates HTML sites,\n uploads to VVVLink API, publishes with unique subdomain URLs.\n Triggers on: \"create a site\", \"build a website\", \"landing page\",\n \"website\", \"portfolio\", \"publish site\", \"deploy site\".\nconfig:\n path: ~/.vvvlink/config.json\n description: \u003e\n Stores account UUID and API key. Created automatically on\n first use via POST /auth/create_new_user. API key is stored\n with chmod 600 permissions.\npersistence:\n - ~/.vvvlink/config.json (account credentials)\n - ~/.vvvlink/sites/\u003csubdomain\u003e/ (generated site files)\napi:\n base: https://publish.vvvlink.com\n auth: Bearer API key (stored in config.json)\n\u003chr style=\"border:none;border-top:1px solid #1e1e3f;margin:12px 0\"\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eVVVLink Sitebuilder\u003c/h2\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eUse this skill when the user wants to create, redesign, or\npublish a website, landing page, portfolio, or any HTML page\nthat should be accessible online.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cANNOUNCE\u003e\nBefore ANY work, announce to the user:\n\"Using vvvlink-site-builder to create and publish your site.\"\n\u003c/ANNOUNCE\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eQuick Reference\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eAPI Base\u003c/strong\u003e: \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ehttps://publish.vvvlink.com\u003c/code\u003e\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eSite creation rules\u003c/strong\u003e: See \u003ca href=\"\u003c/li\u003e\n references/website-development-rules.md\" target=\"_blank\" rel=\"noopener\" style=\"color:#6366f1\"\u003ewebsite-development-rules.md\u003c/a\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eFull API docs\u003c/strong\u003e: See \u003ca href=\"\u003c/li\u003e\n references/api-reference.md\" target=\"_blank\" rel=\"noopener\" style=\"color:#6366f1\"\u003eapi-reference.md\u003c/a\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eDirect Invocation\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eWhen the user invokes this skill directly (without a specific task),\nshow an intro and check for existing sites. Use Telegram formatting.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e1. Load API key from \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e~/.vvvlink/config.json\u003c/code\u003e and call\n \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eGET /sites\u003c/code\u003e (if config exists)\n2. Split sites by status: \"live\" and non-live (\"uploading\", etc.)\n3. Show intro message based on result:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eIf user has live sites:\u003c/strong\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eVVVLink Site Builder\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eYour sites:\n1. my-site.vvvlink.com\n2. cool-page.vvvlink.com\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e+2 drafts (uploading). Ask me to show or clean them up.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eI can update any of these or create a new one.\nWhat would you like to do?\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eOnly show the drafts line if there are non-live sites.\nIf the user asks to see or manage drafts, list them with\nsubdomain, status, and offer to delete individual ones or\nall at once via \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eDELETE /sites/:siteId\u003c/code\u003e.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eIf no sites or no API key:\u003c/strong\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eVVVLink Site Builder\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eI build and publish websites on vvvlink.com in seconds.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eDescribe what you need β€” a landing page, portfolio,\nbusiness site β€” and I'll design, build, and publish it.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eYour site will be live with a unique URL right away.\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eWhen user picks an existing site to update, go to Step 10\n(Updates) in the flow below.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eFlow (strict order, no skipping)\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eStep 1: Announce and show progress\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cPROGRESS-CRITICAL\u003e\nTHIS IS THE MOST IMPORTANT RULE IN THIS SKILL.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThe user sees your text output as a stream. While you write\ntext β€” they see it appearing live. When you make a tool call β€”\nthey see nothing until the tool returns and you write more text.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eTherefore: WRITE TEXT BEFORE AND AFTER EVERY TOOL CALL.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eIn practice, your response should look like this (the user\nsees each line appear as you stream it):\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e⚑ Building your site with VVVLink Site Builder...\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eπŸ” Searching for photos of [topic]...\n[tool call: curl image search]\nπŸ“Έ Found 5 great shots!\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e🎨 Designing layout with [palette] and [fonts]...\nπŸ—οΈ Building hero section...\n[tool call: write first part of HTML]\n✨ Adding content sections...\n[tool call: write rest of HTML]\nπŸ“¦ HTML ready!\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eπŸš€ Publishing to vvvlink.com...\n[tool call: create site]\n[tool call: upload file]\n[tool call: publish]\n[tool call: rename subdomain]\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eβœ… Your site is live!\n🌐 https://your-site.vvvlink.com\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eKEY RULES:\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eWrite at least one emoji + text line BEFORE each tool call\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eThe user must always see what you're doing RIGHT NOW\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eNEVER make 2+ tool calls in a row without text between them\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eIf a tool call takes time, the preceding text is what the\u003c/li\u003e\n user will see while waiting β€” make it informative\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eIMPORTANT: These progress rules apply ONLY when BUILDING a site\n(multi-step process: photos β†’ HTML β†’ upload β†’ publish).\nFor simple operations like listing sites or checking status β€”\njust do it quickly, no need for multiple progress messages.\nOne short message before + result is enough.\n\u003c/PROGRESS-CRITICAL\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eStep 1.5: Ask for business details (if missing)\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eIf the site looks like a business page (cafe, shop, studio,\nagency, etc.) and the user did NOT provide key info, gently\nask ONCE before building. Keep it short β€” one message:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\"πŸ“‹ Before I start β€” want to add any of these to make it real?\nπŸ“ž Phone / email / address\nπŸ• Opening hours\nπŸ”— Social media links\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eOr I can build it now and you'll add details later.\"\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eIf user says \"just build it\" or similar β€” proceed immediately.\nDo NOT block the build. This is optional, not a gate.\nSkip this step entirely for non-business sites (portfolios,\npersonal pages, experiments).\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eStep 2: Find photos for the site\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eBefore writing any code, search for relevant photos:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e# Search by keywords matching the site's topic\ncurl -s \"https://publish.vvvlink.com/images/search?q=KEYWORD\u0026count=5\u0026orientation=landscape\"\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003ePick 3-6 keywords based on the site content (e.g., \"office\",\n\"team\", \"technology\"). Select the best photo for each section\n(hero, about, features, etc.). Use the \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eurl\u003c/code\u003e field from\nthe response directly in \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\u003cimg src\u003e\u003c/code\u003e tags.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eAdd \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\u0026w=1600\u0026h=900\u0026fit=crop\u003c/code\u003e to URLs for hero images,\n\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\u0026w=600\u0026h=400\u0026fit=crop\u003c/code\u003e for cards.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eStep 3: Create the site\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eRead and follow \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ereferences/website-development-rules.md\u003c/code\u003e.\nBuild a high-quality static site using the photos from Step 2.\nOutput all files to \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e~/.vvvlink/sites/\u003csubdomain\u003e/\u003c/code\u003e.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eStep 4: Get API key\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cAPI-KEY-RULES\u003e\n\u003cstrong style=\"color:#e5e7eb\"\u003eapiKey is SECRET.\u003c/strong\u003e Never show it to the user, never log it,\nnever include in messages. It is only for Authorization headers.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eUUID is public.\u003c/strong\u003e It identifies the account. You may show it\nto the user if asked.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eStorage:\u003c/strong\u003e \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e~/.vvvlink/config.json\u003c/code\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e{\n \"uuid\": \"account-uuid\",\n \"apiKey\": \"SECRET-key-never-share\"\n}\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eSite files:\u003c/strong\u003e \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e~/.vvvlink/sites/\u003csubdomain\u003e/\u003c/code\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eRules:\u003c/strong\u003e\n1. NEVER call \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/auth/create_new_user\u003c/code\u003e if \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e~/.vvvlink/config.json\u003c/code\u003e\n exists. The apiKey is issued ONCE and cannot be recovered.\n2. Only create a new account if config.json does not exist.\n3. Protect \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e~/.vvvlink/config.json\u003c/code\u003e with \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003echmod 600\u003c/code\u003e.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eProcedure:\u003c/strong\u003e\n1. Read \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e~/.vvvlink/config.json\u003c/code\u003e β†’ use \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e.apiKey\u003c/code\u003e\n2. If file does not exist β†’ \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ePOST /auth/create_new_user {}\u003c/code\u003e\n β†’ save response to config.json\n\u003c/API-KEY-RULES\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e# Load or create API credentials\nVVVLINK_DIR=\"$HOME/.vvvlink\"\nVVVLINK_CONFIG=\"$VVVLINK_DIR/config.json\"\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eif [ -f \"$VVVLINK_CONFIG\" ]; then\n VVVLINK_API_KEY=$(jq -r '.apiKey' \"$VVVLINK_CONFIG\")\nelse\n mkdir -p \"$VVVLINK_DIR\"\n RESP=$(curl -s -X POST https://publish.vvvlink.com/auth/create_new_user \\\n -H \"Content-Type: application/json\" -d '{}')\n if echo \"$RESP\" | jq -e '.apiKey' \u003e /dev/null 2\u003e\u00261; then\n VVVLINK_API_KEY=$(echo \"$RESP\" | jq -r '.apiKey')\n echo \"$RESP\" | jq '{uuid: .uuid, apiKey: .apiKey}' \\\n \u003e \"$VVVLINK_CONFIG\"\n chmod 600 \"$VVVLINK_CONFIG\"\n else\n echo \"Error: could not create account\" \u003e\u00262\n exit 1\n fi\nfi\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eStep 5: Create site and upload\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e# Create site\ncurl -s -X POST https://publish.vvvlink.com/sites \\\n -H \"Authorization: Bearer $API_KEY\"\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eUpload each file (from ~/.vvvlink/sites/\u003csubdomain\u003e/)\u003c/h2\u003e\ncurl -s -X PUT \\\n \"https://publish.vvvlink.com/sites/$SITE_ID/files/index.html\" \\\n -H \"Authorization: Bearer $API_KEY\" \\\n --data-binary @~/.vvvlink/sites/$SUBDOMAIN/index.html\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eUpload ALL files: HTML, CSS, JS, images, fonts.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cSITE-LIMIT-HANDLING\u003e\nIf \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ePOST /sites\u003c/code\u003e returns HTTP 429 with \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\"Site limit reached\"\u003c/code\u003e,\nthe user has hit their plan limit. Handle this gracefully:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e1. Call the upgrade endpoint to get a checkout URL:\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003ecurl -s -X POST https://publish.vvvlink.com/billing/upgrade \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"success_url\": \"https://vvvlink.com\"}'\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e2. Show the user an upgrade message with the payment link.\n In Telegram, use an inline link so the user can pay in\n the built-in web view:\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e⚠️ *Site limit reached*\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eYou've used all available sites on the Free plan\\.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eπŸš€ \u003ca href=\"PAYMENT_URL\" target=\"_blank\" rel=\"noopener\" style=\"color:#6366f1\"\u003eUpgrade to Pro\u003c/a\u003e to unlock up to 100 sites\\.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e_After upgrading, I'll publish your site right away\\._\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e3. After the user pays and confirms, retry \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ePOST /sites\u003c/code\u003e.\n The limit will be updated automatically.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eIf the upgrade endpoint returns \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\"Already on Pro plan\"\u003c/code\u003e,\nthe user is already on Pro β€” this means they've genuinely\nused all 100 sites. Tell them to delete unused sites first.\n\u003c/SITE-LIMIT-HANDLING\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eStep 6: Publish\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003ecurl -s -X POST \\\n \"https://publish.vvvlink.com/sites/$SITE_ID/publish\" \\\n -H \"Authorization: Bearer $API_KEY\"\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eStep 7: Choose subdomain BEFORE creating site\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eDo NOT create a site with a random name. Pick a good one first:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e1. Analyze the site content (\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\u003ctitle\u003e\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\u003ch1\u003e\u003c/code\u003e, topic)\n2. Generate 10 slug-friendly name candidates\n3. Check availability via POST /subdomains/check-bulk\n4. Pick the BEST available name\n5. Pass it when creating the site:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003ecurl -s -X POST https://publish.vvvlink.com/sites \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"subdomain\": \"chosen-name\"}'\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThe API will use your name if available, or fall back to\na random one if taken. Check the response \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003esubdomain\u003c/code\u003e field\nto confirm which name was assigned.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e6. Show the user the live URL and other available options:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eπŸš€ Your site is live!\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e🌐 https://chosen-name.vvvlink.com\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eWant a different name?\n1. alternative-one\n2. alternative-two\n3. alternative-three\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eReply with a number or type your own.\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eIf user picks a different name, rename via:\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003ecurl -s -X PUT \\\n \"https://publish.vvvlink.com/sites/$SITE_ID/subdomain\" \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"name\": \"new-name\"}'\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eStep 8: Show URL and STOP\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eWhen showing the final URL, add \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e?v=VERSION\u003c/code\u003e to bust cache.\nUse the version number from the publish response. Use markdown\nlink to hide the param:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e\u003ca href=\"https://your-site.vvvlink.com?v=2\" target=\"_blank\" rel=\"noopener\" style=\"color:#6366f1\"\u003eyour-site.vvvlink.com\u003c/a\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThe user sees clean URL but clicks through to the latest version.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eAfter showing the live URL and rename options β€” STOP.\nDo NOT automatically start improving, searching more photos,\nor making changes. Wait for the user to respond.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eYou may add ONE short line like:\n\"Want me to tweak anything β€” colors, content, add a section?\"\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eBut do NOT take any action until the user asks.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eStep 9: Updates\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eTo update an existing site:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003ecurl -s -X POST \\\n \"https://publish.vvvlink.com/sites/$SITE_ID/new-version\" \\\n -H \"Authorization: Bearer $API_KEY\"\n\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eUpload new files, then publish again\u003c/h2\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eRules\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003ePublish immediately without asking for confirmation\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eFor updates to existing sites, mention that the user can\u003c/li\u003e\n roll back to a previous version or view version history\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eAlways offer rename after first publish\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eStore API key β€” don't re-init every time\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eUpload ALL site files (HTML, CSS, JS, images, fonts)\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eMax 10MB per site for free users\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cERROR-HANDLING\u003e\nNEVER let a script error silently kill the build.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eDo NOT use \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eset -e\u003c/code\u003e in shell scripts\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eALWAYS check API response before parsing with jq:\u003c/li\u003e\n \u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e RESP=$(curl -s -X POST .../sites -H \"Authorization: Bearer $KEY\")\n SITE_ID=$(echo \"$RESP\" | jq -r '.siteId // empty')\n if [ -z \"$SITE_ID\" ]; then\n # Show error to user, don't silently fail\n echo \"Error creating site: $RESP\"\n # Try to recover or explain\n fi\n \u003c/code\u003e\u003c/pre\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eIf ANY step fails β€” tell the user what happened and what\u003c/li\u003e\n to do next. NEVER end with a raw error or empty response.\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eShow the user a human-readable message, not jq errors or\u003c/li\u003e\n raw JSON.\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eUse ONLY bash, curl, and jq. NEVER use python, python3,\u003c/li\u003e\n node, ruby, or any other interpreter. There is NO python\n on the server. If you try to use python β€” IT WILL FAIL.\n Everything can be done with bash + curl + jq.\n\u003cABSOLUTE-BAN\u003e\nNEVER EVER put HTML inside bash. This is the #1 cause of\nfailures and MUST be followed without exception.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eBANNED β€” all of these WILL break:\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eheredoc: cat \u003e file \u003c\u003c EOF ... EOF\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eecho: echo '\u003chtml\u003e...' \u003e file\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eprintf: printf '%s' '\u003chtml\u003e...'\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003einline: curl -d '\u003chtml\u003e...'\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eany bash command containing HTML\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eREQUIRED β€” the ONLY correct way:\n1. Use your Write/file tool to create the HTML file\n2. Then upload it with curl (separate tool call):\n \u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e curl -s -X PUT \\\n \"https://publish.vvvlink.com/sites/$SITE_ID/files/index.html\" \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: text/html\" \\\n --data-binary @~/.vvvlink/sites/my-site/index.html\n \u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eTwo separate tool calls. NEVER combine them.\nIf you put HTML in bash β€” it WILL fail. Every time.\n\u003c/ABSOLUTE-BAN\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eIf you get HTTP 403 or 1010 β€” retry the request once.\u003c/li\u003e\n Cloudflare may temporarily block requests. If retry fails,\n tell the user: \"Temporary access issue, try again in a\n moment.\"\n\u003c/ERROR-HANDLING\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eMessage Formatting\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eMessages to the user MUST be visually polished. Format for the\nplatform you are running on (Telegram, CLI, etc). Do NOT\nhardcode escape characters β€” let your platform handle formatting.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eProgress updates β€” use status indicators:\u003c/strong\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eSearching for photos...\nBuilding your site...\nUploading files (3/3)...\nPublishing...\nDone!\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eFinal result β€” clean, no technical details:\u003c/strong\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eπŸš€ Your site is live!\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eyoursite.vvvlink.com\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eWant a different name? Here are some options:\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eDo NOT show file names, file counts, photo counts, version numbers,\nor any other technical details in the final message. The user only\ncares about the URL.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eAlternative names β€” simple numbered list, no \"available\":\u003c/strong\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eWant a different name?\n1. cool-startup\n2. my-project\n3. awesome-landing\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eReply with a number or type your own.\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eKey formatting rules:\u003c/strong\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eKeep messages compact β€” no walls of text\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eClickable links for the published URL (always)\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eNever dump raw JSON, curl output, or API responses\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cCOMPLETION-CRITERIA\u003e\nTHE TASK IS NOT DONE UNTIL THE USER SEES A LIVE URL.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eIf you showed progress messages (\"Building...\", \"Searching...\")\nbut did NOT finish with a live URL β€” YOU FAILED. Go back and\ncomplete all remaining steps.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eChecklist β€” ALL must be true:\n1. Site HTML was created and written to file\n2. Site was uploaded via API (create + upload + publish)\n3. User received a CLICKABLE live URL (https://*.vvvlink.com)\n4. Rename was offered\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eIf ANY tool call fails or times out β€” retry or tell the user\nwhat happened. NEVER silently stop in the middle.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eNEVER end your response after a progress message. The last\nmessage the user sees MUST contain either:\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eA live URL (success), OR\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eAn error explanation with next steps (failure)\u003c/li\u003e\n\u003c/COMPLETION-CRITERIA\u003e\n\u003c/p\u003e"])</script><script>self.__next_f.push([1,"20:[\"$\",\"div\",null,{\"className\":\"skill-page\",\"children\":[[\"$\",\"script\",null,{\"type\":\"application/ld+json\",\"dangerouslySetInnerHTML\":{\"__html\":\"{\\\"@context\\\":\\\"https://schema.org\\\",\\\"@type\\\":\\\"SoftwareApplication\\\",\\\"name\\\":\\\"VVVLINK Site Builder\\\",\\\"description\\\":\\\"Build and publish websites on vvvlink.com. Creates HTML sites, uploads to VVVLink API, publishes with unique subdomain URLs. Triggers on: \\\\\\\"create a site\\\\\\\", \\\\\\\"b...\\\",\\\"url\\\":\\\"https://bytesagain.com/skill/vvvlink-site-builder\\\",\\\"applicationCategory\\\":\\\"clawhub\\\",\\\"operatingSystem\\\":\\\"Any\\\",\\\"offers\\\":{\\\"@type\\\":\\\"Offer\\\",\\\"price\\\":\\\"0\\\",\\\"priceCurrency\\\":\\\"USD\\\"},\\\"publisher\\\":{\\\"@type\\\":\\\"Organization\\\",\\\"name\\\":\\\"BytesAgain\\\",\\\"url\\\":\\\"https://bytesagain.com\\\"}}\"}}],[\"$\",\"div\",null,{\"className\":\"breadcrumb\",\"children\":[[\"$\",\"a\",null,{\"href\":\"/\",\"children\":\"BytesAgain\"}],\" β€Ί \",[\"$\",\"a\",null,{\"href\":\"/skills\",\"children\":\"Skills\"}],\" β€Ί \",\"VVVLINK Site Builder\"]}],[\"$\",\"div\",null,{\"className\":\"two-col\",\"children\":[[\"$\",\"div\",null,{\"className\":\"two-col-main\",\"children\":[[\"$\",\"div\",null,{\"className\":\"skill-card\",\"children\":[[\"$\",\"div\",null,{\"className\":\"skill-header\",\"children\":[[\"$\",\"div\",null,{\"className\":\"skill-badges\",\"children\":[[\"$\",\"span\",null,{\"className\":\"badge\",\"style\":{\"color\":\"#818cf8\",\"background\":\"#818cf822\",\"borderColor\":\"#818cf844\"},\"children\":[\"πŸ¦€\",\" \",\"ClawHub\"]}],false]}],[\"$\",\"div\",null,{\"className\":\"skill-top-actions\",\"children\":[\"$\",\"$L22\",null,{\"slug\":\"vvvlink-site-builder\"}]}]]}],[\"$\",\"h1\",null,{\"className\":\"skill-title\",\"children\":\"VVVLINK Site Builder\"}],[\"$\",\"p\",null,{\"className\":\"skill-owner\",\"children\":[\"by \",[\"$\",\"span\",null,{\"children\":[\"@\",\"vladlat\"]}]]}],[\"$\",\"p\",null,{\"className\":\"skill-desc\",\"children\":\"Build and publish websites on vvvlink.com. Creates HTML sites, uploads to VVVLink API, publishes with unique subdomain URLs. Triggers on: \\\"create a site\\\", \\\"b...\"}],[\"$\",\"div\",null,{\"className\":\"skill-meta\",\"children\":[[\"$\",\"div\",null,{\"className\":\"meta-item\",\"children\":[[\"$\",\"span\",null,{\"className\":\"meta-label\",\"children\":\"Version\"}],[\"$\",\"span\",null,{\"className\":\"meta-value\",\"children\":[\"v\",\"1.8.1\"]}]]}],[\"$\",\"div\",null,{\"className\":\"meta-item\",\"children\":[[\"$\",\"span\",null,{\"className\":\"meta-label\",\"children\":\"Downloads\"}],[\"$\",\"span\",null,{\"className\":\"meta-value\",\"children\":\"422\"}]]}],false,false,false,[\"$\",\"div\",null,{\"className\":\"meta-item\",\"style\":{\"flexDirection\":\"row\",\"gap\":6,\"alignItems\":\"center\"},\"children\":[[\"$\",\"a\",\"legal\",{\"href\":\"/?q=legal\",\"className\":\"tag\",\"children\":[\"#\",\"legal\"]}],[\"$\",\"a\",\"automation\",{\"href\":\"/?q=automation\",\"className\":\"tag\",\"children\":[\"#\",\"automation\"]}]]}]]}],[\"$\",\"div\",null,{\"style\":{\"marginTop\":6},\"children\":[\"$\",\"a\",null,{\"href\":\"https://clawhub.ai/vladlat/vvvlink-site-builder\",\"target\":\"_blank\",\"rel\":\"noopener\",\"className\":\"btn-secondary\",\"style\":{\"padding\":\"6px 12px\",\"fontSize\":\".82em\",\"borderRadius\":8,\"background\":\"transparent\",\"border\":\"1px solid var(--border-card)\",\"color\":\"var(--text-muted2)\",\"textDecoration\":\"none\",\"whiteSpace\":\"nowrap\"},\"children\":[\"View on \",\"ClawHub\",\" β†’\"]}]}]]}],[\"$\",\"div\",null,{\"className\":\"install-box\",\"children\":[[\"$\",\"div\",null,{\"className\":\"install-header\",\"children\":[[\"$\",\"div\",null,{\"className\":\"install-dots\",\"children\":[[\"$\",\"div\",null,{\"className\":\"dot\",\"style\":{\"background\":\"#ef4444\"}}],[\"$\",\"div\",null,{\"className\":\"dot\",\"style\":{\"background\":\"#eab308\"}}],[\"$\",\"div\",null,{\"className\":\"dot\",\"style\":{\"background\":\"#22c55e\"}}]]}],[\"$\",\"span\",null,{\"className\":\"install-label\",\"children\":\"TERMINAL\"}]]}],[\"$\",\"div\",null,{\"className\":\"install-body\",\"style\":{\"flexWrap\":\"wrap\"},\"children\":[[\"$\",\"code\",null,{\"className\":\"install-cmd\",\"children\":\"clawhub install vvvlink-site-builder\"}],[\"$\",\"button\",null,{\"className\":\"copy-btn\",\"data-cmd\":\"clawhub install vvvlink-site-builder\",\"style\":{\"fontWeight\":700},\"children\":\"Copy\"}]]}]]}],[\"$\",\"section\",null,{\"className\":\"skill-card\",\"style\":{\"marginBottom\":20},\"children\":[[\"$\",\"h2\",null,{\"style\":{\"color\":\"#f8fafc\",\"fontSize\":\"1.2em\",\"fontWeight\":800,\"margin\":\"0 0 16px\",\"display\":\"flex\",\"alignItems\":\"center\",\"gap\":8},\"children\":\"πŸ“– About This Skill\"}],[\"$\",\"div\",null,{\"style\":{\"fontSize\":\".92em\",\"color\":\"#94a3b8\",\"lineHeight\":1.75},\"dangerouslySetInnerHTML\":{\"__html\":\"$23\"}}]]}],null,null,null,null,null,\"$L24\",null,false,false]}],\"$L25\"]}]]}]\n"])</script><script>self.__next_f.push([1,"21:[\"$\",\"script\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"\\n document.querySelectorAll('.copy-btn, .script-copy-btn').forEach(btn =\u003e {\\n btn.addEventListener('click', () =\u003e {\\n const cmd = btn.getAttribute('data-cmd');\\n if (!cmd) return;\\n navigator.clipboard.writeText(cmd).then(() =\u003e {\\n const orig = btn.textContent;\\n btn.textContent = 'Copied!';\\n setTimeout(() =\u003e btn.textContent = orig, 1500);\\n }).catch(() =\u003e {});\\n });\\n });\\n \"}}]\n"])</script><script>self.__next_f.push([1,"27:I[71521,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/12w5ognupk9fb.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n26:Tf3b,"])</script><script>self.__next_f.push([1,"\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003ePublish immediately without asking for confirmation\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eFor updates to existing sites, mention that the user can\u003c/li\u003e\n roll back to a previous version or view version history\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eAlways offer rename after first publish\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eStore API key β€” don't re-init every time\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eUpload ALL site files (HTML, CSS, JS, images, fonts)\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eMax 10MB per site for free users\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cERROR-HANDLING\u003e\nNEVER let a script error silently kill the build.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eDo NOT use \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\" style=\"background:#0a0a1c;color:#fbbf24;padding:1px 5px;border-radius:3px;font-size:.85em\"\u003eset -e\u003c/code\u003e in shell scripts\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eALWAYS check API response before parsing with jq:\u003c/li\u003e\n \u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\" style=\"background:#0a0a1c;color:#fbbf24;padding:1px 5px;border-radius:3px;font-size:.85em\"\u003e RESP=$(curl -s -X POST .../sites -H \"Authorization: Bearer $KEY\")\n SITE_ID=$(echo \"$RESP\" | jq -r '.siteId // empty')\n if [ -z \"$SITE_ID\" ]; then\n # Show error to user, don't silently fail\n echo \"Error creating site: $RESP\"\n # Try to recover or explain\n fi\n \u003c/code\u003e\u003c/pre\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eIf ANY step fails β€” tell the user what happened and what\u003c/li\u003e\n to do next. NEVER end with a raw error or empty response.\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eShow the user a human-readable message, not jq errors or\u003c/li\u003e\n raw JSON.\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eUse ONLY bash, curl, and jq. NEVER use python, python3,\u003c/li\u003e\n node, ruby, or any other interpreter. There is NO python\n on the server. If you try to use python β€” IT WILL FAIL.\n Everything can be done with bash + curl + jq.\n\u003cABSOLUTE-BAN\u003e\nNEVER EVER put HTML inside bash. This is the #1 cause of\nfailures and MUST be followed without exception.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eBANNED β€” all of these WILL break:\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eheredoc: cat \u003e file \u003c\u003c EOF ... EOF\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eecho: echo '\u003chtml\u003e...' \u003e file\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eprintf: printf '%s' '\u003chtml\u003e...'\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003einline: curl -d '\u003chtml\u003e...'\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eany bash command containing HTML\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eREQUIRED β€” the ONLY correct way:\n1. Use your Write/file tool to create the HTML file\n2. Then upload it with curl (separate tool call):\n \u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\" style=\"background:#0a0a1c;color:#fbbf24;padding:1px 5px;border-radius:3px;font-size:.85em\"\u003e curl -s -X PUT \\\n \"https://publish.vvvlink.com/sites/$SITE_ID/files/index.html\" \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: text/html\" \\\n --data-binary @~/.vvvlink/sites/my-site/index.html\n \u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eTwo separate tool calls. NEVER combine them.\nIf you put HTML in bash β€” it WILL fail. Every time.\n\u003c/ABSOLUTE-BAN\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eIf you get HTTP 403 or 1010 β€” retry the request once.\u003c/li\u003e\n Cloudflare may temporarily block requests. If retry fails,\n tell the user: \"Temporary access issue, try again in a\n moment.\"\n\u003c/ERROR-HANDLING\u003e\u003c/p\u003e"])</script><script>self.__next_f.push([1,"24:[\"$\",\"section\",null,{\"className\":\"skill-card\",\"style\":{\"marginBottom\":20},\"children\":[[\"$\",\"h2\",null,{\"style\":{\"color\":\"#f8fafc\",\"fontSize\":\"1.2em\",\"fontWeight\":800,\"margin\":\"0 0 16px\",\"display\":\"flex\",\"alignItems\":\"center\",\"gap\":8},\"children\":\"πŸ”’ Constraints\"}],[\"$\",\"div\",null,{\"style\":{\"fontSize\":\".92em\",\"color\":\"#94a3b8\",\"lineHeight\":1.75},\"dangerouslySetInnerHTML\":{\"__html\":\"$26\"}}]]}]\n25:[\"$\",\"div\",null,{\"className\":\"two-col-side\",\"children\":[\"$\",\"$L27\",null,{\"category\":\"clawhub\",\"currentSlug\":\"vvvlink-site-builder\",\"name\":\"VVVLINK Site Builder\",\"tags\":[\"legal\",\"automation\"]}]}]\n"])</script></body></html>