Install
openclaw skills install vvvlink-site-builderBuild 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".
openclaw skills install vvvlink-site-builderUse 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.
<ANNOUNCE> Before ANY work, announce to the user: "Using vvvlink-site-builder to create and publish your site." </ANNOUNCE>https://publish.vvvlink.comWhen the user invokes this skill directly (without a specific task), show an intro and check for existing sites. Use Telegram formatting.
~/.vvvlink/config.json and call
GET /sites (if config exists)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.
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:
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. </PROGRESS-CRITICAL>
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).
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 <img src> tags.
Add &w=1600&h=900&fit=crop to URLs for hero images,
&w=600&h=400&fit=crop for cards.
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/<subdomain>/.
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/<subdomain>/
Rules:
/auth/create_new_user if ~/.vvvlink/config.json
exists. The apiKey is issued ONCE and cannot be recovered.~/.vvvlink/config.json with chmod 600.Procedure:
~/.vvvlink/config.json → use .apiKeyPOST /auth/create_new_user {}
→ save response to config.json
</API-KEY-RULES>
# 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
# Create site
curl -s -X POST https://publish.vvvlink.com/sites \
-H "Authorization: Bearer $API_KEY"
# Upload each file (from ~/.vvvlink/sites/<subdomain>/)
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.
<SITE-LIMIT-HANDLING> If `POST /sites` returns HTTP 429 with `"Site limit reached"`, the user has hit their plan limit. Handle this gracefully: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"}'
⚠️ *Site limit reached*
You've used all available sites on the Free plan\.
🚀 [Upgrade to Pro](PAYMENT_URL) to unlock up to 100 sites\.
_After upgrading, I'll publish your site right away\._
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.
</SITE-LIMIT-HANDLING>
curl -s -X POST \
"https://publish.vvvlink.com/sites/$SITE_ID/publish" \
-H "Authorization: Bearer $API_KEY"
Do NOT create a site with a random name. Pick a good one first:
<title>, <h1>, topic)curl -s -X POST https://publish.vvvlink.com/sites \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"subdomain": "chosen-name"}'
The API will use your name if available, or fall back to
a random one if taken. Check the response subdomain field
to confirm which name was assigned.
🚀 Your site is live!
🌐 https://chosen-name.vvvlink.com
Want a different name?
1. alternative-one
2. alternative-two
3. alternative-three
Reply with a number or type your own.
If user picks a different name, rename via:
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"}'
When showing the final URL, add ?v=VERSION to bust cache.
Use the version number from the publish response. Use markdown
link to hide the param:
[your-site.vvvlink.com](https://your-site.vvvlink.com?v=2)
The user sees clean URL but clicks through to the latest version.
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.
You may add ONE short line like: "Want me to tweak anything — colors, content, add a section?"
But do NOT take any action until the user asks.
To update an existing site:
curl -s -X POST \
"https://publish.vvvlink.com/sites/$SITE_ID/new-version" \
-H "Authorization: Bearer $API_KEY"
# Upload new files, then publish again
set -e in shell scriptsRESP=$(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
NEVER EVER put HTML inside bash. This is the #1 cause of failures and MUST be followed without exception.
BANNED — all of these WILL break:
REQUIRED — the ONLY correct way:
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
Two separate tool calls. NEVER combine them. If you put HTML in bash — it WILL fail. Every time. </ABSOLUTE-BAN>
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.
Progress updates — use status indicators:
Searching for photos...
Building your site...
Uploading files (3/3)...
Publishing...
Done!
Final result — clean, no technical details:
🚀 Your site is live!
yoursite.vvvlink.com
Want a different name? Here are some options:
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.
Alternative names — simple numbered list, no "available":
Want a different name?
1. cool-startup
2. my-project
3. awesome-landing
Reply with a number or type your own.
Key formatting rules:
If you showed progress messages ("Building...", "Searching...") but did NOT finish with a live URL — YOU FAILED. Go back and complete all remaining steps.
Checklist — ALL must be true:
If ANY tool call fails or times out — retry or tell the user what happened. NEVER silently stop in the middle.
NEVER end your response after a progress message. The last message the user sees MUST contain either: