_create-contact-page
Skill50 starsChanged 21 days agoApache-2.0View on GitHub
Internal helper - creates a functional /contact page with a secured form (honeypot anti-spam, rate limiting delegated to the tRPC procedure, HTML injection protection). Detects i18n, email provider (Resend vs Brevo), and shadcn/ui + react-hook-form deps. Invoked by /add-email or any future skill that needs to add a contact UI. Not meant to be called directly by users.
- Reads credentials
- Installs packages
1 other project uses a version of this.
Adopted by
Most recently taken up by
- flavien-ia/hypervibe-craftword for word, first seen Oct 25, 2026
Other versions1 project uses a version of this.
Word for word · 1
- flavien-ia/hypervibe-craft★ 50100% same
Edited · 0
None found.
Wrote their own version · 0
None found.
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository89 other files it gives its agents.
- accessibilitySkill
- add-2faSkill
- add-agent-dashboardSkill
- add-aiSkill
- add-analyticsSkill
- add-authSkill
- add-automationSkill
- add-backup-dbSkill
- add-collabSkill
- add-cronSkill
- add-dark-modeSkill
- add-dbSkill
- add-domainSkill
- add-emailSkill
- add-github-authSkill
- add-google-authSkill
- add-i18nSkill
- _add-keyringSkill
- add-mapSkill
- add-notification-centerSkill
- add-push-notificationSkill
- add-pwaSkill
- add-roleSkill
- add-routineSkill
- add-storageSkill
- add-stripeSkill
- add-testSkill
- bootstrapSkill
- _check-depsSkill
- cleanSkill
- _collect-secretSkill
- _convert-to-turborepoSkill
- _create-agentSkill
- _create-cloudflare-workerSkill
- _create-render-workerSkill
- _create-routineSkill
- _create-workflowSkill
- delete-projectSkill
- _detect-project-rootSkill
- _dns-brevoSkill
- _dns-cloudflareSkill
- _dns-gandiSkill
- _dns-godaddy-manualSkill
- _dns-hostingerSkill
- _dns-infomaniak-manualSkill
- _dns-ionos-manualSkill
- _dns-lwsSkill
- _dns-namecheapSkill
- _dns-ovhSkill
- _dns-porkbunSkill
- _dns-resendSkill
- _dns-squarespace-manualSkill
- eco-auditSkill
- _ensure-aiSkill
- _ensure-vaultSkill
- _generate-secretSkill
- geoSkill
- _get-secretSkill
- gscSkill
- _hash-passwordSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
8 rules. About 0 are common, 5 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 13
Use plain, non-technical business language.
- Distinctive: line 14
When generating user-facing content for the scaffolded project (UI labels, emails, copy), write it in the user's language too.
- Very specific: line 82
Resend: no guessed default - ask for the address, optionally proposing $CURRENT_SENDER if it is defined and is not onboarding@resend.dev.
- Very specific: line 85
If the current sender is onboarding@resend.dev, warn the user that receiving will not work until they provide a real address.
- Distinctive: line 87
Ask the user (plain language, no jargon):
- Distinctive: line 128
If any of the three is missing, install:
- Very specific: line 152
If MISSING is non-empty, install:
- Distinctive: line 198
Do not display a clean summary (the calling skill produces its own final summary).
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Create Contact Page - Internal helper
Communication
- Detect the user's language from the conversation (the user's own messages, anywhere in the session - not just this invocation: a bare slash command like
/bootstrapcarries no language signal by itself). If nothing in the conversation gives a signal, fall back to the OS locale (node -e "console.log(Intl.DateTimeFormat().resolvedOptions().locale)") before defaulting to English. ALWAYS reply in that language for every user-facing message: questions, progress, confirmations, summaries, errors - including any example text quoted in this skill, which is illustrative and must be translated, never sent verbatim. - Use plain, non-technical business language. Never expose internal script names (*.mjs) or jargon; describe actions in human terms.
- When generating user-facing content for the scaffolded project (UI labels, emails, copy), write it in the user's language too.
- Show progress as a short natural-language checklist (in-progress and done states).
Creates a functional /contact page with a secured form (invisible honeypot anti-spam, server-side rate limiting, HTML/Brevo escaping on every user field). Invoked by /add-email after the user has accepted the offer to create it.
Expected prerequisites (normally guaranteed by the calling skill):
- The tRPC procedure
contact.sendexists (created byadd-email, or put in place at Step 1 below when the email was set up without it) - The
mail.tsutility exportssendMail+ the escape function (escapeHtmlfor Resend,escapeForBrevofor Brevo) - shadcn/ui is installed (via
/bootstrap)
Step 1 - Detect the project context
Invoke _detect-project-root to retrieve PROJECT_NAME, WEB_DIR, IS_MONOREPO, IS_NEXTJS.
Invoke _check-deps email i18n:
result=$(node "${CLAUDE_SKILL_DIR}/../../scripts/check-deps.mjs" email i18n)
email_provider=$(echo "$result" | node -e "console.log(JSON.parse(require('fs').readFileSync(0,'utf8')).email.provider || 'none')")
i18n_ok=$(echo "$result" | node -e "console.log(JSON.parse(require('fs').readFileSync(0,'utf8')).i18n.ok)")Consequences of the detected provider
If email_provider === "none" -> abort: the contact page depends on a functional email backend. Inform the calling skill and create nothing.
Otherwise (resend or brevo) -> continue. The React client page is identical in both cases (the HTML/Brevo escaping happens server-side in contact.send, not in the page). No need to distinguish.
The contact router may be missing: /add-email does not write it when the email was wanted for something else, nor when the project had no rate limiting yet. When <WEB_DIR>/src/server/api/routers/contact.ts is not there, put it in place first:
node "${CLAUDE_SKILL_DIR}/../../scripts/setup-email.mjs" --provider "$email_provider" --name "<PROJECT_NAME>" --web-dir "<WEB_DIR>" --contact-router-onlyIt writes the router and registers it in root.ts, nothing else. If it refuses because rateLimitedProcedure is missing from the project's tRPC, stop here and say so plainly: the form needs the rate limiting that /bootstrap's security setup adds, and a page without its back-end would break the build.
Also retrieve the current sender email from the local env (useful for Step 2):
SENDER_EMAIL_VAR=$(case "$email_provider" in resend) echo "RESEND_FROM_EMAIL";; brevo) echo "BREVO_SENDER_EMAIL";; esac)
CURRENT_SENDER=$(grep -E "^${SENDER_EMAIL_VAR}=" .env 2>/dev/null | head -1 | sed -E "s/^${SENDER_EMAIL_VAR}=//" | tr -d '"' | tr -d "'")Determine a functional default for receiving
The goal: a default that actually works for receiving the emails. The sender is not always a good choice (e.g. onboarding@resend.dev is NOT an inbox - emails sent there disappear silently).
The Resend API does not cleanly expose the account email -> we ask the user directly for the receiving address (better UX: explicit rather than guessed). For Brevo, the CURRENT_SENDER (verified address) remains a reasonable default to propose.
Step 2 - Ask for the receiving email address
First, does the sender's domain relay its addresses? When a domain's mail goes through Cloudflare's Email Routing (its mail servers are route1.mx.cloudflare.net and siblings), its addresses are not mailboxes: Cloudflare forwards what they receive to another inbox. A notification sent to an address of the sending domain then goes out and comes back through that relay, which can break its DKIM signature, and the message ends up in spam. Read it for the sender's domain (CURRENT_SENDER after its @):
node "${CLAUDE_SKILL_DIR}/../../scripts/domain-mx.mjs" <sender's domain>It reads the domain's mail servers in public DNS: no key, no vault.
relayed: true-> no default on that domain, the sender included. Ask for the final inbox (the one the relay forwards to, often a Gmail or Outlook address), and say why in one sentence, for example: "Your addresses at<domain>are forwarded to another inbox: sending the form's messages there through that relay can land them in spam, so tell me the inbox they finally arrive in." If the user still gives an address of that domain, say it once more, then respect their choice.relayed: false-> the defaults below apply.- Exit
1(the DNS could not be read) -> ask for the receiving address with no default.
Proposed default:
- Resend: no guessed default - ask for the address, optionally proposing
$CURRENT_SENDERif it is defined and is notonboarding@resend.dev. - Brevo: propose
$CURRENT_SENDER(verified address, real inbox).
If the current sender is onboarding@resend.dev, warn the user that receiving will not work until they provide a real address.
Ask the user (plain language, no jargon):
📬 Which address do you want to receive the form messages at?
When someone fills out your contact form, their message is sent to you by email. Tell me where you want to receive them:
- By default: at
<DEFAULT_RECIPIENT>(<explanation: "your Resend email" if owner, otherwise "the sending address you just configured">)- Another address: give me another address (e.g. your personal Gmail
me@gmail.com)Tell me "leave the default" or type the address you want.
Depending on the answer
Whatever the choice, always push CONTACT_RECIPIENT_EMAIL via _push-env-vars (we do not rely on the tRPC procedure fallback for the nominal case - the fallback remains only a safety belt):
If "leave the default" -> RECIPIENT = $DEFAULT_RECIPIENT.
If the user gives a different address -> RECIPIENT = <provided address>. Quickly validate that it looks like an email (contains @ and at least one . after it). If invalid, ask again.
Then in both cases:
node "${CLAUDE_SKILL_DIR}/../../scripts/push-env-vars.mjs" "CONTACT_RECIPIENT_EMAIL=$RECIPIENT"The helper updates the local .env AND Vercel (production/preview/development) in a single idempotent operation. The tRPC procedure contact.send reads process.env.CONTACT_RECIPIENT_EMAIL.
⚠️ Brevo note: if the provider is Brevo and the user gives a custom address, remind them that Brevo does not require formal validation of the recipient (unlike the sender, which must be verified). No blocking, but if the address is wrong, the email will disappear silently (Brevo 201-then-async-fail).
Step 3 - Install the missing deps
3.a - react-hook-form + zod
Check in <WEB_DIR>/package.json:
grep -qE '"(react-hook-form|@hookform/resolvers|zod)"' "<WEB_DIR>/package.json"If any of the three is missing, install:
# Majors pinned: this skill writes the code that calls them (zodResolver, zod 3 schemas).
cd <WEB_DIR> && pnpm add react-hook-form "@hookform/resolvers@^7" "zod@^3"Why
zod@^3and not a barezod. The create-t3-app scaffold shipszod@^3.24.2together with@t3-oss/env-nextjs@^0.12.x, whose peer range iszod: ^3.24.0- zod 4 is not supported there. A barepnpm add zodresolves thelatesttag (zod 4.x), breaks that peer and the typing ofsrc/env.js. Lift this pin once the scaffold ships@t3-oss/env-nextjs>= 0.13, which accepts^3.24.0 || ^4.0.0.
3.b - Required shadcn/ui components
The page needs: card, input, textarea, button, label, alert. Check that each is present in <WEB_DIR>/src/components/ui/:
for c in card input textarea button label alert; do
[ -f "<WEB_DIR>/src/components/ui/$c.tsx" ] || MISSING="$MISSING $c"
doneIf MISSING is non-empty, install:
cd <WEB_DIR> && pnpm dlx shadcn@latest add$MISSINGStep 4 - Copy the template (standard i18n-aware pattern)
The contact page follows the plugin's standard i18n-aware feature pattern. All the files (plain.tsx, i18n.tsx, messages-fr.json, messages-en.json) are in templates/contact-page/ at the plugin root.
4.a - Choose the variant and the path
PLUGIN_ROOT="${CLAUDE_SKILL_DIR}/../.."
if [ "$i18n_ok" = "true" ]; then
VARIANT="i18n"
DEST="<WEB_DIR>/src/app/[locale]/contact/page.tsx"
else
VARIANT="plain"
DEST="<WEB_DIR>/src/app/contact/page.tsx"
fi4.b - Copy the right template
mkdir -p "$(dirname "$DEST")"
cp "$PLUGIN_ROOT/templates/contact-page/$VARIANT.tsx" "$DEST"4.c - If i18n is active, merge the messages
if [ "$i18n_ok" = "true" ]; then
node "$PLUGIN_ROOT/scripts/_i18n-merge-messages.mjs" --web-dir "<WEB_DIR>" --feature contact-page
fiThe helper merges the contact.* keys into every messages/<locale>.json in the project. EN fallback for the locales the template does not ship (the helper warns about those locales). If other languages are present (e.g. es.json, de.json), warn the user that the translations must be completed manually.
Step 5 - Return to the calling skill
Do not display a clean summary (the calling skill produces its own final summary). Just confirm the path of the created page:
CONTACT_PAGE_CREATED: <DEST>The calling skill will integrate this info into its user-facing summary.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

