{
  "markdown": "![VueUse Skills banner](./.github/assets/banner.svg)\n\n# VueUse Skills\n\nAgent Skills for [VueUse](https://vueuse.org/) — a collection of essential Vue Composition Utilities.\n\n> [!IMPORTANT]\n> Experimental Project: Aims to help AI agents use libraries more accurately with fewer tokens. Feedback is welcome.\n\n- 🪜 Progressive disclosure: send VueUse function overviews first, then load detailed usage and type declarations on demand\n- 💰 Minimal token usage: provide only necessary information to reduce token consumption\n- 📵 Offline-first design: works without internet access or additional agent permissions\n- ⚙️ Customizable policies: users can override function invocation rules in prompts or `AGENTS.md`\n- 💉 Reduced hallucinations: precise usage references help prevent invented APIs\n\n## Installation\n\n```bash\nnpx skills add vueuse/skills\n```\n\n### Claude Code Marketplace\n\nAn alternative for Claude Code users:\n\n```bash\n# Add marketplace\n/plugin marketplace add vueuse/skills\n\n# Install individual skills\n/plugin install vueuse-functions@vueuse-skills\n```\n\n## Example Usage\n\nInstall VueUse in your Vue or Nuxt project, then instruct the agent. It will automatically leverage VueUse to assist development.\n\nExample prompt:\n\n```\ncreate a todo app with the following features:\n- save todos to local storage\n- show remains todo count on browser title\n- add a copy button for each todo items\n- infinite scrolling for this todo list\n- dark / light mode\n```\n\n<details>\n<summary>Output Snippet</summary>\n\n```vue\n<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport {\n  useClipboard,\n  useColorMode,\n  useInfiniteScroll,\n  useLocalStorage,\n  useTitle,\n} from '@vueuse/core'\n\ntype Todo = {\n  id: number\n  text: string\n  done: boolean\n}\n\nconst seedTexts = [\n  'Review project goals',\n  'Plan the next sprint',\n  'Reply to client email',\n]\n\nconst defaultTodos = Array.from({ length: 36 }, (_, index) => ({\n  id: index + 1,\n  text:\n    seedTexts[index % seedTexts.length] +\n    (index >= seedTexts.length ? ` #${index + 1}` : ''),\n  done: index % 7 === 0,\n}))\n\nconst todos = useLocalStorage<Todo[]>('focus-flow-todos', defaultTodos)\nconst nextId = ref(\n  todos.value.reduce((max, todo) => Math.max(max, todo.id), 0) + 1,\n)\nconst newTodo = ref('')\n\nconst totalCount = computed(() => todos.value.length)\nconst remainingCount = computed(() =>\n  todos.value.filter((todo) => !todo.done).length,\n)\nconst completedCount = computed(\n  () => totalCount.value - remainingCount.value,\n)\n\nuseTitle(computed(() => `Todos (${remainingCount.value})`))\n\nconst mode = useColorMode({\n  attribute: 'data-theme',\n  disableTransition: false,\n})\nconst isDark = computed(() => mode.value === 'dark')\n\nconst toggleMode = () => {\n  mode.value = isDark.value ? 'light' : 'dark'\n}\n\nconst { copy, copied, isSupported } = useClipboard()\nconst lastCopiedId = ref<number | null>(null)\n\nconst handleCopy = async (todo: Todo) => {\n  await copy(todo.text)\n  lastCopiedId.value = todo.id\n}\n\nconst pageSize = 8\nconst visibleCount = ref(Math.min(pageSize, todos.value.length))\nconst visibleTodos = computed(() => todos.value.slice(0, visibleCount.value))\n\nconst listRef = ref<HTMLElement | null>(null)\nconst { isLoading } = useInfiniteScroll(\n  listRef,\n  () => {\n    if (visibleCount.value < todos.value.length) {\n      visibleCount.value = Math.min(\n        visibleCount.value + pageSize,\n        todos.value.length,\n      )\n    }\n  },\n  {\n    distance: 120,\n    canLoadMore: () => visibleCount.value < todos.value.length,\n  },\n)\n\nconst syncVisibleCount = () => {\n  if (todos.value.length <= pageSize) {\n    visibleCount.value = todos.value.length\n    return\n  }\n\n  if (visibleCount.value === 0) {\n    visibleCount.value = pageSize\n    return\n  }\n\n  if (visibleCount.value > todos.value.length) {\n    visibleCount.value = todos.value.length\n  }\n}\n\nconst addTodo = () => {\n  const value = newTodo.value.trim()\n  if (!value)\n    return\n\n  todos.value.unshift({\n    id: nextId.value++,\n    text: value,\n    done: false,\n  })\n  newTodo.value = ''\n  syncVisibleCount()\n}\n\nconst removeTodo = (id: number) => {\n  todos.value = todos.value.filter((todo) => todo.id !== id)\n  syncVisibleCount()\n}\n</script>\n\n<template>\n  <div class=\"page\">\n    <div class=\"shell\">\n      <header class=\"header\">\n        <div>\n          <p class=\"eyebrow\">Minimal todo tracker</p>\n          <h1>Focus Flow</h1>\n          <p class=\"subtitle\">\n            Keep a lightweight list, copy tasks with a click, and scroll as the\n            list grows.\n          </p>\n        </div>\n        <button class=\"btn ghost mode-toggle\" type=\"button\" @click=\"toggleMode\">\n          <span class=\"mode-dot\" :class=\"{ dark: isDark }\" />\n          <span>{{ isDark ? 'Dark' : 'Light' }} mode</span>\n        </button>\n      </header>\n\n      <form class=\"composer\" @submit.prevent=\"addTodo\">\n        <div class=\"input-wrap\">\n          <input\n            v-model=\"newTodo\"\n            type=\"text\"\n            maxlength=\"120\"\n            placeholder=\"Add a new task\"\n            aria-label=\"Add a new task\"\n          />\n          <button class=\"btn primary\" type=\"submit\" :disabled=\"!newTodo.trim()\">\n            Add task\n          </button>\n        </div>\n        <div class=\"stats\">\n          <div class=\"stat\">\n            <span>Total</span>\n            <strong>{{ totalCount }}</strong>\n          </div>\n          <div class=\"stat\">\n            <span>Remaining</span>\n            <strong>{{ remainingCount }}</strong>\n          </div>\n          <div v-if=\"completedCount\" class=\"stat\">\n            <span>Done</span>\n            <strong>{{ completedCount }}</strong>\n          </div>\n        </div>\n      </form>\n\n      <section class=\"list-card\">\n        <div class=\"list-head\">\n          <h2>Todo list</h2>\n          <span class=\"list-hint\">\n            {{ visibleTodos.length }} / {{ totalCount }} shown\n          </span>\n        </div>\n        <div ref=\"listRef\" class=\"todo-list\" aria-live=\"polite\">\n          <article\n            v-for=\"(todo, index) in visibleTodos\"\n            :key=\"todo.id\"\n            class=\"todo-item\"\n            :class=\"{ done: todo.done }\"\n            :style=\"{ animationDelay: `${index * 0.03}s` }\"\n          >\n            <label class=\"todo-check\">\n              <input v-model=\"todo.done\" type=\"checkbox\" />\n              <span class=\"checkmark\" />\n            </label>\n            <p class=\"todo-text\">{{ todo.text }}</p>\n            <div class=\"todo-actions\">\n              <button\n                class=\"btn ghost\"\n                type=\"button\"\n                :disabled=\"!isSupported\"\n                :title=\"isSupported ? 'Copy task text' : 'Clipboard not supported'\"\n                @click=\"handleCopy(todo)\"\n              >\n                {{ copied && lastCopiedId === todo.id ? 'Copied' : 'Copy' }}\n              </button>\n              <button class=\"btn danger\" type=\"button\" @click=\"removeTodo(todo.id)\">\n                Remove\n              </button>\n            </div>\n          </article>\n\n          <p v-if=\"!visibleTodos.length\" class=\"empty\">\n            No tasks yet. Add your first todo above.\n          </p>\n\n          <div v-if=\"visibleTodos.length\" class=\"list-footer\">\n            <span class=\"footer-status\">\n              <span v-if=\"isLoading\">Loading more...</span>\n              <span v-else-if=\"visibleTodos.length < totalCount\">\n                Scroll to load more\n              </span>\n              <span v-else>All caught up</span>\n            </span>\n          </div>\n        </div>\n      </section>\n    </div>\n  </div>\n</template>\n\n<style>\n@import url('https://fonts.googleapis.com/css2?family=Sora:wght@400;500;600;700&family=Space+Mono:wght@400;700&display=swap');\n\n:root {\n  color-scheme: light;\n  --bg: #f6f7fb;\n  --bg-spot: rgba(59, 130, 246, 0.18);\n  --bg-spot-2: rgba(34, 197, 94, 0.18);\n  --card: rgba(255, 255, 255, 0.92);\n  --surface: rgba(255, 255, 255, 0.84);\n  --border: rgba(148, 163, 184, 0.35);\n  --text: #0f172a;\n  --muted: #64748b;\n  --accent: #2563eb;\n  --accent-strong: #1d4ed8;\n  --accent-soft: rgba(37, 99, 235, 0.18);\n  --danger: #ef4444;\n  --danger-soft: rgba(239, 68, 68, 0.15);\n  --shadow: 0 24px 60px rgba(15, 23, 42, 0.12);\n  --radius: 22px;\n}\n\n:root[data-theme='dark'] {\n  color-scheme: dark;\n  --bg: #0b1222;\n  --bg-spot: rgba(56, 189, 248, 0.18);\n  --bg-spot-2: rgba(16, 185, 129, 0.16);\n  --card: rgba(15, 23, 42, 0.86);\n  --surface: rgba(15, 23, 42, 0.7);\n  --border: rgba(148, 163, 184, 0.25);\n  --text: #f8fafc;\n  --muted: #94a3b8;\n  --accent: #38bdf8;\n  --accent-strong: #0ea5e9;\n  --accent-soft: rgba(56, 189, 248, 0.2);\n  --danger: #f87171;\n  --danger-soft: rgba(248, 113, 113, 0.2);\n  --shadow: 0 26px 70px rgba(2, 6, 23, 0.55);\n}\n\n* {\n  box-sizing: border-box;\n}\n\nbody {\n  margin: 0;\n  min-height: 100vh;\n  font-family: 'Sora', system-ui, -apple-system, sans-serif;\n  color: var(--text);\n  background:\n    radial-gradient(900px circle at top left, var(--bg-spot), transparent 55%),\n    radial-gradient(700px circle at bottom right, var(--bg-spot-2), transparent 50%),\n    var(--bg);\n  transition: background 0.4s ease, color 0.4s ease;\n}\n\n#app {\n  min-height: 100vh;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  padding: clamp(20px, 4vw, 48px);\n}\n\n.page {\n  width: min(980px, 100%);\n}\n\n.shell {\n  display: grid;\n  gap: clamp(20px, 3vw, 28px);\n  padding: clamp(20px, 4vw, 36px);\n  border-radius: var(--radius);\n  background: var(--card);\n  border: 1px solid var(--border);\n  box-shadow: var(--shadow);\n  backdrop-filter: blur(18px);\n}\n\n.header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 24px;\n}\n\n.eyebrow {\n  text-transform: uppercase;\n  letter-spacing: 0.2em;\n  font-size: 0.72rem;\n  color: var(--muted);\n  margin: 0 0 10px;\n}\n\nh1 {\n  margin: 0;\n  font-size: clamp(2rem, 3vw, 2.6rem);\n}\n\n.subtitle {\n  margin: 10px 0 0;\n  color: var(--muted);\n  max-width: 520px;\n}\n\n.composer {\n  display: grid;\n  gap: 16px;\n}\n\n.input-wrap {\n  display: grid;\n  grid-template-columns: 1fr auto;\n  gap: 12px;\n}\n\ninput[type='text'] {\n  padding: 12px 14px;\n  border-radius: 14px;\n  border: 1px solid var(--border);\n  background: var(--surface);\n  color: var(--text);\n  font-size: 1rem;\n  transition: border 0.2s ease, box-shadow 0.2s ease;\n}\n\ninput[type='text']:focus {\n  outline: none;\n  border-color: var(--accent);\n  box-shadow: 0 0 0 3px var(--accent-soft);\n}\n\n.stats {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 12px;\n}\n\n.stat {\n  display: inline-flex;\n  align-items: center;\n  gap: 8px;\n  padding: 8px 12px;\n  border-radius: 999px;\n  background: var(--surface);\n  border: 1px solid var(--border);\n  font-size: 0.9rem;\n  color: var(--muted);\n}\n\n.stat strong {\n  font-family: 'Space Mono', ui-monospace, SFMono-Regular, monospace;\n  color: var(--text);\n  font-size: 0.95rem;\n}\n\n.list-card {\n  display: grid;\n  gap: 16px;\n}\n\n.list-head {\n  display: flex;\n  align-items: baseline;\n  justify-content: space-between;\n  gap: 12px;\n}\n\n.list-head h2 {\n  margin: 0;\n  font-size: 1.2rem;\n}\n\n.list-hint {\n  font-size: 0.85rem;\n  color: var(--muted);\n  font-family: 'Space Mono', ui-monospace, SFMono-Regular, monospace;\n}\n\n.todo-list {\n  max-height: clamp(320px, 55vh, 520px);\n  overflow-y: auto;\n  display: grid;\n  gap: 12px;\n  padding: 6px;\n  margin: -6px;\n}\n\n.todo-item {\n  display: grid;\n  grid-template-columns: auto 1fr auto;\n  gap: 12px;\n  align-items: center;\n  padding: 14px 16px;\n  border-radius: 16px;\n  background: var(--surface);\n  border: 1px solid var(--border);\n  animation: fadeUp 0.4s ease both;\n}\n\n.todo-item.done {\n  opacity: 0.7;\n}\n\n.todo-text {\n  margin: 0;\n  font-size: 0.98rem;\n}\n\n.todo-item.done .todo-text {\n  text-decoration: line-through;\n  color: var(--muted);\n}\n\n.todo-check {\n  display: inline-flex;\n  align-items: center;\n}\n\n.todo-check input {\n  width: 18px;\n  height: 18px;\n  accent-color: var(--accent);\n}\n\n.checkmark {\n  display: none;\n}\n\n.todo-actions {\n  display: inline-flex;\n  gap: 8px;\n  flex-wrap: wrap;\n}\n\n.btn {\n  border: 1px solid var(--border);\n  background: transparent;\n  color: var(--text);\n  padding: 8px 14px;\n  border-radius: 999px;\n  font-size: 0.88rem;\n  cursor: pointer;\n  display: inline-flex;\n  align-items: center;\n  gap: 8px;\n  transition: all 0.2s ease;\n}\n\n.btn:disabled {\n  cursor: not-allowed;\n  opacity: 0.6;\n}\n\n.btn.primary {\n  background: var(--accent);\n  border-color: var(--accent);\n  color: #fff;\n  font-weight: 600;\n}\n\n.btn.primary:hover:not(:disabled) {\n  background: var(--accent-strong);\n  border-color: var(--accent-strong);\n}\n\n.btn.ghost:hover:not(:disabled) {\n  border-color: var(--accent);\n  color: var(--accent);\n}\n\n.btn.danger {\n  border-color: transparent;\n  color: var(--danger);\n  background: var(--danger-soft);\n}\n\n.mode-toggle {\n  white-space: nowrap;\n}\n\n.mode-dot {\n  width: 10px;\n  height: 10px;\n  border-radius: 50%;\n  background: #facc15;\n  box-shadow: 0 0 0 3px rgba(250, 204, 21, 0.2);\n}\n\n.mode-dot.dark {\n  background: #38bdf8;\n  box-shadow: 0 0 0 3px rgba(56, 189, 248, 0.2);\n}\n\n.empty {\n  text-align: center;\n  padding: 32px 12px;\n  color: var(--muted);\n  border-radius: 16px;\n  border: 1px dashed var(--border);\n}\n\n.list-footer {\n  text-align: center;\n  font-size: 0.85rem;\n  color: var(--muted);\n  padding: 8px 0 12px;\n}\n\n@keyframes fadeUp {\n  from {\n    opacity: 0;\n    transform: translateY(8px);\n  }\n  to {\n    opacity: 1;\n    transform: translateY(0);\n  }\n}\n\n@media (max-width: 820px) {\n  .header {\n    flex-direction: column;\n    align-items: flex-start;\n  }\n\n  .input-wrap {\n    grid-template-columns: 1fr;\n  }\n\n  .todo-item {\n    grid-template-columns: auto 1fr;\n  }\n\n  .todo-actions {\n    grid-column: 1 / -1;\n    justify-content: flex-end;\n  }\n}\n</style>\n```\n</details>\n\n## License\n\nMIT\n",
  "bytes": 13662,
  "sha": "9f4ba8df2ed9ab0c8ddd2086900df9fcb2e8c125f619bc6c8c8e4ff47b64192f",
  "repo_slug": "vueuse/skills",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/skl_vueuse_skills_vueuse_functions_ece5faf0/readme"
}