Alexander ef5f5ba0e6
fix(cli): install all 7 skills via uipro init, not just the orchestrator (#362) (#387)
* fix(cli): install all 7 skills via uipro init, not just the orchestrator

`uipro init` rendered only the orchestrator (ui-ux-pro-max) and never
delivered the 6 sibling skills (banner-design, brand, design,
design-system, slides, ui-styling), so users got 1 of 7 skills (#362).

- sync-assets.mjs: bundle the 6 sub-skills into cli/assets/skills/ as
  static copies (source of truth: .claude/skills/), with sync + check
  coverage. Excludes ui-styling/canvas-fonts (~5.8MB of TTF) and
  __pycache__/.pyc cruft — a skill registers from its SKILL.md, not its
  fonts — so the bundle adds ~0.9MB, not ~6.6MB.
- template.ts: after rendering the orchestrator, install each bundled
  sub-skill as a sibling. The skills parent is derived from the
  platform's skillPath (skills/ for most, prompts/ for copilot,
  steering/ for kiro) rather than hardcoded.
- uninstall.ts: remove the sub-skills too.

Verified: check:assets in sync, tsc passes, and a per-platform install
harness delivers all 7 skills to the correct parent dir with no fonts.

Closes #362

* fix(cli): filter excluded files from target side of check:assets

check:assets filtered sourceFiles with isExcludedAssetFile but not
targetFiles, so a stray cli/assets/scripts/__pycache__/*.pyc (generated
by a local Python run) was reported as an "extra asset file" and failed
the gate. Apply the same predicate to targetFiles in both the
dirsToSync and sub-skill loops.

Verified: check:assets now passes with __pycache__/*.pyc present in the
target tree; typecheck passes.

* fix(cli): uninstall from each platform's real skills dir, not hardcoded skills/

removeSkillDir() hardcoded <folder>/skills/<name>, but the installer
places skills under each platform config's skillPath parent — copilot in
.github/prompts/, kiro in .kiro/steering/. So uninstall left those
platforms' skills (orchestrator + sub-skills) behind.

Derive the install parent from loadPlatformConfig(aiType).folderStructure
(same source the installer uses), and keep the legacy <folder>/skills/
cleanup (incl. .shared/) for older installs. Deduped via a Set.

Verified: typecheck passes; an install+uninstall harness removes all 7
skills with zero leftovers for claude (.claude/skills), copilot
(.github/prompts) and kiro (.kiro/steering).

* fix(cli): re-sync bundled sub-skills after #385 stripped ckm- names

#385 merged to main and removed the ckm- prefix from the six
.claude/skills/*/SKILL.md name fields. This branch's bundled copies
under cli/assets/skills/ still carried the old ckm- names, so after the
PR merges with main the source no longer matched the bundle and the
check-asset-sync CI gate failed (stale asset file: skills/*/SKILL.md).

Merge main and regenerate the bundle so cli/assets/skills matches the
current .claude/skills source of truth. check:assets and typecheck pass.
2026-06-25 17:33:23 +07:00

5.2 KiB

Token Architecture

Three-layer token system for scalable, themeable design systems.

Layer Overview

┌─────────────────────────────────────────┐
│  Component Tokens                       │  Per-component overrides
│  --button-bg, --card-padding            │
├─────────────────────────────────────────┤
│  Semantic Tokens                        │  Purpose-based aliases
│  --color-primary, --spacing-section     │
├─────────────────────────────────────────┤
│  Primitive Tokens                       │  Raw design values
│  --color-blue-600, --space-4            │
└─────────────────────────────────────────┘

Why Three Layers?

Layer Purpose When to Change
Primitive Base values (colors, sizes) Rarely - foundational
Semantic Meaning assignment Theme switching
Component Component customization Per-component needs

Layer 1: Primitive Tokens

Raw design values without semantic meaning.

:root {
  /* Colors */
  --color-gray-50: #F9FAFB;
  --color-gray-900: #111827;
  --color-blue-500: #3B82F6;
  --color-blue-600: #2563EB;

  /* Spacing (4px base) */
  --space-1: 0.25rem;  /* 4px */
  --space-2: 0.5rem;   /* 8px */
  --space-4: 1rem;     /* 16px */
  --space-6: 1.5rem;   /* 24px */

  /* Typography */
  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.125rem;

  /* Radius */
  --radius-sm: 0.25rem;
  --radius-default: 0.5rem;
  --radius-lg: 0.75rem;

  /* Shadows */
  --shadow-sm: 0 1px 2px rgb(0 0 0 / 0.05);
  --shadow-default: 0 1px 3px rgb(0 0 0 / 0.1);
}

Layer 2: Semantic Tokens

Purpose-based aliases that reference primitives.

:root {
  /* Background */
  --color-background: var(--color-gray-50);
  --color-foreground: var(--color-gray-900);

  /* Primary */
  --color-primary: var(--color-blue-600);
  --color-primary-hover: var(--color-blue-700);

  /* Secondary */
  --color-secondary: var(--color-gray-100);
  --color-secondary-foreground: var(--color-gray-900);

  /* Muted */
  --color-muted: var(--color-gray-100);
  --color-muted-foreground: var(--color-gray-500);

  /* Destructive */
  --color-destructive: var(--color-red-600);
  --color-destructive-foreground: white;

  /* Spacing */
  --spacing-component: var(--space-4);
  --spacing-section: var(--space-6);
}

Layer 3: Component Tokens

Component-specific tokens referencing semantic layer.

:root {
  /* Button */
  --button-bg: var(--color-primary);
  --button-fg: white;
  --button-hover-bg: var(--color-primary-hover);
  --button-padding-x: var(--space-4);
  --button-padding-y: var(--space-2);
  --button-radius: var(--radius-default);

  /* Input */
  --input-bg: var(--color-background);
  --input-border: var(--color-gray-300);
  --input-focus-ring: var(--color-primary);
  --input-padding: var(--space-2) var(--space-3);

  /* Card */
  --card-bg: var(--color-background);
  --card-border: var(--color-gray-200);
  --card-padding: var(--space-4);
  --card-radius: var(--radius-lg);
  --card-shadow: var(--shadow-default);
}

Dark Mode

Override semantic tokens for dark theme:

.dark {
  --color-background: var(--color-gray-900);
  --color-foreground: var(--color-gray-50);
  --color-muted: var(--color-gray-800);
  --color-muted-foreground: var(--color-gray-400);
  --color-secondary: var(--color-gray-800);
}

Naming Convention

--{category}-{item}-{variant}-{state}

Examples:
--color-primary           # category-item
--color-primary-hover     # category-item-state
--button-bg-hover         # component-property-state
--space-section-sm        # category-semantic-variant

Categories

Category Examples
color primary, secondary, muted, destructive
space 1, 2, 4, 8, section, component
font-size xs, sm, base, lg, xl
radius sm, default, lg, full
shadow sm, default, lg
duration fast, normal, slow

File Organization

tokens/
├── primitives.css     # Raw values
├── semantic.css       # Purpose aliases
├── components.css     # Component tokens
└── index.css          # Imports all

Or single file with layer comments:

/* === PRIMITIVES === */
:root { ... }

/* === SEMANTIC === */
:root { ... }

/* === COMPONENTS === */
:root { ... }

/* === DARK MODE === */
.dark { ... }

Migration from Flat Tokens

Before (flat):

--button-primary-bg: #2563EB;
--button-secondary-bg: #F3F4F6;

After (three-layer):

/* Primitive */
--color-blue-600: #2563EB;
--color-gray-100: #F3F4F6;

/* Semantic */
--color-primary: var(--color-blue-600);
--color-secondary: var(--color-gray-100);

/* Component */
--button-bg: var(--color-primary);
--button-secondary-bg: var(--color-secondary);

W3C DTCG Alignment

Token JSON format (W3C Design Tokens Community Group):

{
  "color": {
    "blue": {
      "600": {
        "$value": "#2563EB",
        "$type": "color"
      }
    }
  }
}