| name | annotask-init |
| description | Initialize Annotask for this project by scanning the codebase and generating .annotask/design-spec.json. Use when the user asks to initialize, set up, configure, or scan their project for Annotask, or runs /annotask-init. |
annotask-init
Initialize Annotask for this project by scanning the codebase and generating .annotask/design-spec.json.
For editor agents only. Annotask's built-in agent runs init through the shell UI — open http://localhost:5173/__annotask/ and the wizard walks the user through provider setup, project scan, and style guide. This skill exists for users who prefer their own editor agent (Claude Code, Cursor, Windsurf) to do the deeper token extraction the in-shell baseline doesn't cover.
When to use
Use this skill when the user says any of:
- "initialize Annotask" / "init Annotask"
- "set up Annotask" / "configure Annotask"
- "scan my project for Annotask"
/annotask-init
What it does
Scans the project to detect the framework, design tokens (colors, typography, spacing, borders), icon library, and component library. Writes .annotask/design-spec.json which Annotask reads to populate the Theme page with editable design tokens.
Output schema
Every token carries resolved values for every detected theme variant via a
values map keyed by theme id. Tokens that don't change across variants repeat
the same value under each id — this keeps every token one uniform shape.
{
"role": "primary",
"values": { "light": "#3b82f6", "dark": "#60a5fa" },
"cssVar": "--color-primary",
"source": "var(--color-primary)",
"sourceFile": "src/assets/main.css",
"sourceLine": 12
}
role — semantic name (see vocabularies below)
values — resolved value per theme variant, keyed by theme id (light, dark, or a named theme). Single-theme apps use { "default": "…" }.
cssVar — the CSS custom property name, if backed by one. Omit if not a CSS variable.
source — human-readable provenance: var(--x), tailwind.config:colors.primary, @theme:--color-primary
sourceFile — relative path to the file where the value is defined
sourceLine — line number in that file (the :root variant's definition is fine when multiple exist)
Theme variants themselves are listed at the top of the spec under themes,
with a selector describing how each variant is activated in the DOM so the
Annotask shell can match it against the iframe's current state:
"themes": [
{ "id": "light", "name": "Light", "scheme": "light", "selector": { "kind": "default" } },
{ "id": "dark", "name": "Dark", "scheme": "dark", "selector": { "kind": "class", "host": "html", "name": "dark" } }
],
"defaultTheme": "light"
selector.kind is one of attribute (with name, value, optional host), class (with name, optional host), media (with media: '(prefers-color-scheme: dark)'), or default (no selector — the base :root values). Apps with only one theme should emit a single { "id": "default", "name": "Default", "selector": { "kind": "default" } } variant.
Steps
1. Detect framework
Check package.json dependencies:
vue → name "vue", read version
react / react-dom → name "react"
svelte → name "svelte"
solid-js → name "solid"
Check for styling:
tailwindcss in devDependencies or dependencies → add "tailwind" to styling array
- Look for
<style scoped> in .vue files → add "scoped-css"
- Look for CSS module imports (
.module.css) → add "css-modules"
2. Detect theme variants
Before extracting any token values, determine which theme variants the project
supports so you know which scopes to resolve values under. Check these signals
in order and stop at the first match:
-
Tailwind .dark class — If CSS files contain .dark { … } / :root.dark { … } / html.dark { … } blocks, or Tailwind v3 config has darkMode: 'class', or Tailwind v4 CSS contains @custom-variant dark (&:where(.dark, .dark *)). Emit two variants:
{ "id": "light", "name": "Light", "scheme": "light", "selector": { "kind": "default" } }
{ "id": "dark", "name": "Dark", "scheme": "dark", "selector": { "kind": "class", "host": "html", "name": "dark" } }
-
Data-attribute theming — If CSS contains [data-theme="…"], [data-bs-theme="…"], [data-mui-color-scheme="…"], [data-mantine-color-scheme="…"], or [data-color-scheme="…"] selectors. Emit one variant per distinct attribute value:
{ "id": "<value>", "name": "<Value>", "scheme": "light|dark (if obvious)", "selector": { "kind": "attribute", "host": "html", "name": "<attr-name>", "value": "<value>" } }
-
@media (prefers-color-scheme: dark) blocks — If the project only switches via media queries. Emit:
{ "id": "light", "name": "Light", "scheme": "light", "selector": { "kind": "default" } }
{ "id": "dark", "name": "Dark", "scheme": "dark", "selector": { "kind": "media", "media": "(prefers-color-scheme: dark)" } }
-
Single theme — If none of the above are present:
[ { "id": "default", "name": "Default", "selector": { "kind": "default" } } ]
Set defaultTheme to the variant id used when no selector matches — typically "light" or "default".
3. Scan colors
Find color sources:
-
CSS variables: Search for :root, :root.dark, html.dark, [data-theme="…"], and @media (prefers-color-scheme: dark) :root declarations across CSS files and Vue <style> blocks. Extract --variable-name: value pairs, remembering which variant scope each came from.
-
Tailwind v4 (version >= 4): Search for @theme blocks in CSS files. Extract custom properties defined within them. @theme blocks without a variant selector apply to every variant.
-
Tailwind v3 (version < 4): Read tailwind.config.js, tailwind.config.ts, or tailwind.config.mjs. Extract theme.extend.colors. Convert to flat key-value pairs.
Resolve each color under every detected variant. Produce a values map
keyed by theme id. When a variant doesn't override the token, copy the base
(:root/light/default) value so every token has a value for every variant.
When a variant-specific block defines a new CSS variable that doesn't appear
in the base scope, leave the missing variants blank (the server normalizer
will fill them when the spec is read).
Classify into semantic roles using this fixed vocabulary:
| Role | Match heuristics (in variable/key name) |
|---|
primary | primary, brand |
secondary | secondary |
accent | accent |
background | bg, background |
surface | surface, card |
text | text, foreground, fg (but not text-muted) |
text-muted | text-muted, muted, subtle |
border | border, divider, separator |
danger | danger, error, destructive, red |
warning | warning, amber, yellow (semantic) |
success | success, green (semantic) |
info | info, blue (semantic) |
If a variable doesn't match any heuristic, use a descriptive role name derived from the variable name (e.g., --sidebar-bg → role sidebar-bg).
Limits: Maximum 30 color tokens. Prioritize semantic roles first, then most-used custom colors.
4. Scan typography
Apply the same per-variant values map strategy used for colors: resolve each
token under every detected variant, copying the base value for variants that
don't override it.
Font families: Search for:
- CSS variables containing
font, family in :root or @theme
fontFamily in Tailwind config
- Direct
font-family declarations on body, html, h1-h6 elements
Classify as:
heading — used on h1-h6 or named heading/display
body — used on body/html or named sans/body/base
mono — monospace, code, named mono
Font scale: Search for:
- CSS variables containing
font-size, text- size names
fontSize in Tailwind config
@theme size definitions
Use roles: xs, sm, base, lg, xl, 2xl, 3xl, 4xl (match by name or by ascending size order).
Weights: Scan component files for font-weight values and Tailwind weight classes (font-bold, font-semibold, etc.). List unique weights as strings: ["400", "500", "600", "700"].
5. Scan spacing
Search for:
- CSS variables containing
space, gap, margin, padding, size
spacing in Tailwind config
@theme spacing definitions
Use roles: xs, sm, md, lg, xl, 2xl, 3xl, 4xl (match by name or ascending size).
Limits: Maximum 12 spacing tokens.
6. Scan border radius
Search for:
- CSS variables containing
radius, rounded
borderRadius in Tailwind config
@theme radius definitions
Use roles: sm, md, lg, xl, full (match by name or ascending size).
7. Scan breakpoints
Detect responsive breakpoints from whatever styling system the project uses. Output as a flat object mapping name → min-width value.
Sources (check in order):
-
Tailwind v4 (>= 4): Look in CSS files for @theme blocks defining --breakpoint-* custom properties, or @custom-media rules.
-
Tailwind v3 (< 4): Read tailwind.config.{js,ts,mjs}. Extract theme.screens or theme.extend.screens. Default Tailwind screens: { "sm": "640px", "md": "768px", "lg": "1024px", "xl": "1280px", "2xl": "1536px" } — use these as fallback if Tailwind is detected but no custom screens are defined.
-
Bootstrap: Check for bootstrap in package.json. Default Bootstrap breakpoints: { "sm": "576px", "md": "768px", "lg": "992px", "xl": "1200px", "xxl": "1400px" }.
-
CSS custom properties: Search :root for variables containing breakpoint, screen, bp in the name (e.g., --bp-mobile: 480px).
-
CSS @media patterns: Scan .css, .scss, .vue files for @media queries with min-width or max-width. Extract the 3–6 most common breakpoint values and assign roles by ascending size: sm, md, lg, xl, 2xl.
Output example:
"breakpoints": {
"sm": "640px",
"md": "768px",
"lg": "1024px",
"xl": "1280px",
"2xl": "1536px"
}
If no breakpoints are detected, omit the breakpoints field (don't include an empty object).
8. Detect icon library
Check package.json dependencies for:
| Package | Library name |
|---|
lucide-vue-next or lucide-react | lucide |
@heroicons/vue or @heroicons/react | heroicons |
@fortawesome/vue-fontawesome or @fortawesome/react-fontawesome | fontawesome |
@phosphor-icons/vue or @phosphor-icons/react | phosphor |
@tabler/icons-vue or @tabler/icons-react | tabler |
Read the version from package.json.
9. Detect component library
Check package.json dependencies for:
| Package | Library name |
|---|
primevue | PrimeVue |
vuetify | Vuetify |
element-plus | Element Plus |
@headlessui/vue | Headless UI |
radix-vue | Radix Vue |
naive-ui | Naive UI |
@shadcn/ui or shadcn-vue | shadcn/ui |
If found, scan src/ for import statements to find used components:
grep -rh "from '${package}" --include='*.vue' --include='*.ts' --include='*.js' src/ | sort -u
Extract component names from the imports. Read the version from package.json.
10. Write design spec
Create .annotask/design-spec.json:
{
"version": "1.0",
"framework": {
"name": "vue",
"version": "3.5.0",
"styling": ["tailwind", "scoped-css"]
},
"themes": [
{ "id": "light", "name": "Light", "scheme": "light", "selector": { "kind": "default" } },
{ "id": "dark", "name": "Dark", "scheme": "dark", "selector": { "kind": "class", "host": "html", "name": "dark" } }
],
"defaultTheme": "light",
"colors": [
{ "role": "primary", "values": { "light": "#3b82f6", "dark": "#60a5fa" }, "cssVar": "--color-primary", "source": "var(--color-primary)", "sourceFile": "src/assets/main.css", "sourceLine": 5 },
{ "role": "background", "values": { "light": "#ffffff", "dark": "#0b1120" }, "cssVar": "--bg", "source": "var(--bg)", "sourceFile": "src/assets/main.css", "sourceLine": 3 }
],
"typography": {
"families": [
{ "role": "body", "values": { "light": "Inter, sans-serif", "dark": "Inter, sans-serif" }, "cssVar": "--font-sans", "source": "var(--font-sans)", "sourceFile": "src/assets/main.css", "sourceLine": 10 }
],
"scale": [
{ "role": "base", "values": { "light": "1rem", "dark": "1rem" }, "cssVar": "--text-base", "source": "var(--text-base)", "sourceFile": "src/assets/main.css", "sourceLine": 15 }
],
"weights": ["400", "500", "600", "700"]
},
"spacing": [
{ "role": "sm", "values": { "light": "8px", "dark": "8px" }, "cssVar": "--space-sm", "source": "var(--space-sm)", "sourceFile": "src/assets/main.css", "sourceLine": 20 }
],
"borders": {
"radius": [
{ "role": "md", "values": { "light": "8px", "dark": "8px" }, "cssVar": "--radius-md", "source": "var(--radius-md)", "sourceFile": "src/assets/main.css", "sourceLine": 25 }
]
},
"breakpoints": {
"sm": "640px",
"md": "768px",
"lg": "1024px",
"xl": "1280px",
"2xl": "1536px"
},
"icons": {
"library": "lucide",
"version": "0.300.0"
},
"components": {
"library": "PrimeVue",
"version": "4.5.4",
"used": ["DataTable", "Column", "InputText", "Tag", "Button"]
}
}
11. Clean up old config
If .annotask/config.json exists, delete it — it's been replaced by design-spec.json.
12. Update .gitignore
Check if .gitignore contains .annotask/. If not, append:
# Annotask (generated)
.annotask/
13. Report to user
Tell the user:
- What was detected (framework, number of color/typography/spacing tokens, libraries)
- That they can open the Theme page in Annotask to view and edit tokens
- That they can re-run
/annotask-init to rescan
Notes
- This skill is idempotent — re-running overwrites the design spec with fresh data
- The design spec is gitignored because it's generated and may vary per developer
- If detection fails for any section, use empty defaults (empty array, null) rather than omitting the field
- All top-level fields are required — use empty values for missing data
- When a token has a CSS variable backing, always populate the
cssVar field — this enables live preview in the Theme page