Copy logo

Copy

Community
dawitlabs
copy

Master UI copywriting skill that audits every user-facing string in the project — CTAs, error messages, empty states, placeholders, loading messages, headings, tooltips, confirmations — and rewrites them to be specific, benefit-first, and actionable. Works screen by screen in priority order with visual verification. Use when user wants to improve UI copy, fix button labels, write empty states, improve error messages, or invokes /copy.

Overview

Publisherdawitlabs
Repositoryui-skills
Skill namecopy
Stars
7
Forks
0
Bundled files
1
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 1 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by dawitlabs on GitHub. Read the source before you install it.

Installation

Install the Copy AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/dawitlabs/ui-skills/tree/master/skills/copy
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/dawitlabs/ui-skills.git /tmp/ui-skills
mkdir -p .claude/skills
cp -r /tmp/ui-skills/skills/copy .claude/skills/copy
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Copy in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Copy on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Copy is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

/copy — Master UI Copywriting

Audits every word the user sees and rewrites it to be clear, specific, and benefit-first. Works screen by screen, from highest to lowest priority, verifying every change visually before advancing.

Before starting: read ~/.claude/skills/ui-design-principles/SKILL.md — writing rules (expose benefit not action, strongest statement first, $1000 per deleted word) are in there and apply throughout.

Full copy rules by category: PRINCIPLES.md


Phase 0 — Environment Setup

0a. Check / start dev server

bash
curl -s --max-time 2 -o /dev/null -w "%{http_code}" http://localhost:3000

If not 200, find and start the dev command, then wait:

bash
until curl -s --max-time 2 http://localhost:3000 > /dev/null 2>&1; do sleep 1; done && echo "ready"

0b. Find source root

bash
find . -type d -name "src" \
  -not -path "*/node_modules/*" \
  -not -path "*/.next/*" \
  -not -path "*/dist/*" \
  -not -path "*/.turbo/*" \
  | head -1

If no src/ exists: find . -type d -name "app" -not -path "*/node_modules/*" -not -path "*/.next/*" | head -1

Use the result as <SRC> throughout.

0b-i. Detect i18n

bash
grep -r "i18next\|next-intl\|react-i18next\|@lingui\|formatjs\|next-translate" package.json packages/*/package.json apps/*/package.json 2>/dev/null | head -5

If a translation library is found:

  • Find the message files: find . -name "*.json" -path "*/locales/*" -not -path "*/node_modules/*" | head -10
  • All copy fixes go into the message files, not hardcoded in component files.
  • Note the library and message file paths — reference them throughout Phase 3.

0c. Write the screenshot script

Write to the project root (needs node_modules). Deleted in Phase 4.

_copy_screenshot.mjs:

js
import { chromium } from '@playwright/test';
import { mkdirSync } from 'fs';

const get = (f) => { const i = process.argv.indexOf(f); return i !== -1 ? process.argv[i+1] : null; };

const url    = get('--url');
const output = get('--output') ?? '/tmp/copy-screenshot.png';
const width  = parseInt(get('--width')  ?? '1280');
const height = parseInt(get('--height') ?? '900');
const auth   = get('--auth');

if (!url) { console.error('--url required'); process.exit(1); }

mkdirSync('/tmp/copy-screens', { recursive: true });

const browser = await chromium.launch();
const context = await browser.newContext({
  viewport: { width, height },
  ...(auth ? { storageState: auth } : {}),
});
const page = await context.newPage();
try {
  await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 });
} catch {
  await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 });
}
await page.screenshot({ path: output, fullPage: true });
await browser.close();
console.log('saved:', output);

Usage:

bash
node _copy_screenshot.mjs --url http://localhost:3000/dashboard --output /tmp/copy-dashboard.png
node _copy_screenshot.mjs --url http://localhost:3000/settings  --output /tmp/copy-settings-mobile.png --width 375 --height 812

After every screenshot: use the Read tool on the image file.

0d. Handle auth-protected screens

Check for an existing auth state from a previous skill run:

bash
ls /tmp/uiux-auth-state.json /tmp/animate-auth-state.json 2>/dev/null | head -1

If found, pass --auth <path> to all screenshot calls for protected routes.

If not found and protected screens exist, write _copy_auth.mjs to the project root:

js
import { chromium } from '@playwright/test';
const { TEST_EMAIL, TEST_PASSWORD, LOGIN_URL, SUCCESS_URL } = process.env;
if (!TEST_EMAIL || !TEST_PASSWORD) { console.error('Set TEST_EMAIL and TEST_PASSWORD'); process.exit(1); }
const browser = await chromium.launch();
const page    = await browser.newPage();
await page.goto(LOGIN_URL ?? 'http://localhost:3000/sign-in');
await page.fill('input[type="email"]',    TEST_EMAIL);
await page.fill('input[type="password"]', TEST_PASSWORD);
await page.click('button[type="submit"]');
await page.waitForURL(SUCCESS_URL ?? '**/dashboard**', { timeout: 15000 });
await page.context().storageState({ path: '/tmp/copy-auth-state.json' });
await browser.close();
console.log('saved → /tmp/copy-auth-state.json');
bash
TEST_EMAIL=you@example.com TEST_PASSWORD=secret node _copy_auth.mjs

Phase 1 — Copy Audit

1a. Find all screens

Same discovery as /uiux — every page.tsx in App Router, every page in Pages Router, modals, auth flows, settings, onboarding. List each with its route.

1b. Read and extract copy for each screen

For each screen, read the file (and its key child components) and extract every user-facing string, grouped by category:

CategoryWhat to look for
CTAsButton labels, link text, submit text
Headingsh1, h2, h3, page titles, section titles
ErrorsError messages, validation text, failed states
Empty states"No results", "Nothing here", blank state text
PlaceholdersInput placeholder attributes
LoadingLoading text, skeleton labels, progress messages
Tooltipstitle attributes, tooltip content, aria-labels, aria-describedby, aria-placeholder
ConfirmationsDialog text, "Are you sure?", destructive action warnings
MicrocopyHelper text, hints, descriptions under fields
MarketingHero headlines, subheadlines, feature descriptions

Rate each string:

  • Bad — generic, vague, action-not-benefit, or missing entirely
  • Weak — technically correct but could be stronger
  • Good — specific, benefit-first, clear — skip

1c. Screenshot each screen

Take a desktop screenshot of every screen. Read every image. Note where copy looks too long, truncated, or misaligned after rewriting (copy changes affect layout).

Also scan for hidden copy in accessibility attributes:

bash
grep -rn "aria-label\|aria-description\|aria-placeholder\|title=" "$SRC" --include="*.tsx" --include="*.jsx" | grep -v "node_modules"

These strings are read by screen readers but invisible in the UI — they must follow the same copy rules.

Present the full audit grouped by screen before continuing.


Phase 2 — Priority Ranking

Score each screen:

FactorPoints
Entry point / first impression (landing, sign-up, onboarding)+3
Revenue / conversion critical (pricing, checkout, CTA)+3
Has Bad copy (generic, missing, or actively misleading)+3
High traffic / seen on every visit+2
Has Weak copy (improvable but not broken)+1
Admin / power-user only−1

Rank highest to lowest. Ask user to confirm before starting Phase 3.


Phase 3 — Recursive Copy Loop

Work through screens in ranked order. Maximum 3 iterations per screen.

3a. Screenshot before

Take desktop and mobile screenshots. Read both. Describe the copy problems you can see in context — not just in the code.

bash
node _copy_screenshot.mjs --url http://localhost:3000<ROUTE> --output /tmp/copy-before-<name>-desktop.png
node _copy_screenshot.mjs --url http://localhost:3000<ROUTE> --output /tmp/copy-before-<name>-mobile.png --width 375 --height 812

3b. Analyse

Read the screen's files. For each string, apply the rules from PRINCIPLES.md. Note every issue:

  • Critical — actively wrong (misleading, broken error message, inaccessible label)
  • Major — generic CTA, vague error, blank empty state, label-as-placeholder
  • Minor — could be shorter, stronger, or more specific

3c. Rewrite

Apply all fixes, starting with critical. Rules from PRINCIPLES.md applied automatically — no permission needed:

  • CTAs: verb-first + benefit ("Start Free Trial" not "Submit")
  • Errors: what failed + what to do ("Email already in use — try signing in instead")
  • Empty states: what's possible + a CTA ("No projects yet — create your first one")
  • Placeholders: real example ("you@company.com" not "Email address")
  • Loading: what is happening ("Generating your report…" not "Loading…")
  • Confirmations: name the thing + consequence ("Delete 'Q4 Report'? This can't be undone.")
  • Headings: strongest claim first, cut every filler word
  • Microcopy: one sentence max, conversational, no jargon

Length discipline: after rewriting, check that no string is longer than necessary. Cut every word that isn't pulling weight. If the original was 8 words, the rewrite should be 5 or fewer.

Layout impact: if a rewrite is significantly longer than the original, note it — it may break the layout. Prefer shorter rewrites that fit the existing space.

3d. Screenshot after

bash
sleep 3  # hot reload
node _copy_screenshot.mjs --url http://localhost:3000<ROUTE> --output /tmp/copy-after-<name>-desktop.png
node _copy_screenshot.mjs --url http://localhost:3000<ROUTE> --output /tmp/copy-after-<name>-mobile.png --width 375 --height 812

Read both images. Check:

  1. Does the new copy fit without truncation or overflow — on both desktop AND mobile?
  2. Does it read naturally in context?
  3. Is the hierarchy still clear (heading → subtext → CTA)?
  4. Does the tone match the rest of the product?

If any answer is no: go back to 3c (increment iteration, max 3).

3e. Copy checklist

Before advancing to the next screen:

  • Every CTA is verb-first and exposes a benefit
  • No button says "Submit", "OK", "Click here", or "Continue" without context
  • Every error message says what failed AND what to do next
  • No error says "Something went wrong" or "An error occurred"
  • Every empty state explains what goes here AND has a CTA
  • No placeholder is just the field label repeated
  • Every placeholder shows a real example
  • Every loading message says what is happening specifically
  • Every confirmation names the thing being acted on and the consequence
  • Every heading is the strongest true statement — no filler words
  • No microcopy exceeds one sentence
  • All copy fits in its container without truncation (verified in desktop screenshot)
  • All copy fits at 375px mobile — no wrapping that breaks the layout (verified in mobile screenshot)
  • All aria-labels and title attributes follow the same copy rules
  • If i18n is detected: all changes are in message files, not hardcoded
  • Tone is consistent with the rest of the product on this screen

Any fail → go back to 3c. After iteration 3: flag and advance.


Phase 4 — Cleanup & Report

Delete temp scripts:

bash
rm -f _copy_screenshot.mjs _copy_auth.mjs

After each screen:

✓ Screen name (rank N, iteration K) — A critical + B major + C minor fixed

Final summary table:

RankScreenRouteStrings FixedIterationsState
1............✓ / ⚠

List all flagged strings at the bottom with the specific issue that couldn't be resolved.


Rules

  1. Read every screenshot. Copy that fits perfectly in code can truncate or overflow in the browser. The image wins.
  2. Shorter is always better. If two rewrites are equally clear, pick the shorter one.
  3. Benefit before action. Every CTA should answer "what do I get?" not "what will I do?"
  4. Specific beats generic. "Delete 'March Report'?" beats "Are you sure?". "Email already in use" beats "Invalid input".
  5. Match the tone. Don't make one screen sound like a startup and another like an enterprise tool.
  6. Layout first. If a rewrite breaks the layout, shorten it — don't ask the developer to change the layout for copy.
  7. Max 3 iterations per screen. Flag and move on.
  8. Never change logic or functionality. Only strings the user sees.

See PRINCIPLES.md for rules and good/bad examples by copy category.

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Copy AI skill do?

Master UI copywriting skill that audits every user-facing string in the project — CTAs, error messages, empty states, placeholders, loading messages, headings, tooltips, confirmations — and rewrites them to be specific, benefit-first, and actionable. Works screen by screen in priority order with visual verification. Use when user wants to improve UI copy, fix button labels, write empty states, improve error messages, or invokes /copy.

Why use Copy on TypingMind?

Because you install it once and use it with any model. Copy is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Copy in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/dawitlabs/ui-skills/tree/master/skills/copy. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Copy?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Copy?

As many as you like. As long as a model supports skills, you can use Copy with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Copy AI skill free?

Yes. It is published on GitHub by dawitlabs under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇