---
name: design-system-discovery
description: |
  Design system auto-detection — scans repository for installed component libraries,
  token systems, and variant frameworks to build a structured design-system-profile.yaml.
  Provides the discoverDesignSystem() algorithm used by devise, strive, and arc workflows.
  Provides the discoverFrontendStack() algorithm for Layer 1 frontend stack detection with
  version extraction, used by design-prototype Phase 0.
  Trigger keywords: shadcn, shadcn/ui, untitled ui, radix ui, design system, design tokens,
  tailwind, css variables, cva, class-variance-authority, styled-components, tailwind v4,
  style-dictionary, token system, component library, design compliance, frontend stack,
  figma tokens, dark mode, ui library, component library detection, design system discovery.
user-invocable: false
disable-model-invocation: false
---

# Design System Discovery

Auto-detects the project's component library, token system, and variant framework.
Called by devise Phase 0.5 (pre-brainstorm), strive worker injection (Phase 1.5), and arc Phase 2.8 (semantic verification).

## Output

Writes `tmp/plans/{timestamp}/design-system-profile.yaml` (session-scoped, ephemeral).
Never writes to `.claude/` — that path is reserved for Claude Code platform files only.

## discoverDesignSystem(repoRoot, sessionCacheDir)

**Input**: `repoRoot` — repository root path; `sessionCacheDir` — caller-provided session-scoped temp directory (e.g. `tmp/plans/{timestamp}`)
**Output**: `design-system-profile.yaml` (written to `{sessionCacheDir}/`), profile object returned to caller

### Phase 0: Pre-flight

```
// sessionCacheDir is passed by caller (e.g. tmp/plans/{timestamp})
IF {sessionCacheDir}/design-system-profile.yaml EXISTS:
  RETURN cached profile  // Re-use within same session
ELSE:
  BEGIN tiered scan
```

### Phase 1: Tiered Scanning

Scanning proceeds in 3 tiers (Tier 0: root manifests, Tier 1: shallow scan, Tier 2: deep content scan). Each tier is only entered if the previous tier did not yield a conclusive match (confidence >= 0.90). Early exit saves time in well-configured projects.

See [tiered-scanning.md](references/tiered-scanning.md) for the full signal definitions, file lists, and weight assignments per tier.

### Phase 2–3: Signal Aggregation & Confidence

After all tiers, signals are grouped by category (library, token, variant) and confidence is computed using a weighted formula: `confidence = maxWeight * (matchedCount / totalSignals) ^ 0.3`, with a conclusive single-signal shortcut (weight == 1.0 → confidence = 1.0).

See [signal-aggregation.md](references/signal-aggregation.md) for the aggregation categories, confidence formula, worked examples, and threshold table.

**Winner selection**: The library with the highest confidence is `primary_library`.
If two libraries both exceed 0.50, include both (migration edge case — see §Edge Cases).

### Phase 4–5: Token & Variant System Resolution

Resolves the project's design token format (Tailwind v4 theme, v3 config, Style Dictionary, CSS variables) and variant system (CVA, styled-components, CSS modules). Extracts semantic colors, spacing, border radius, and shadow scales from detected source files.

See [signal-aggregation.md](references/signal-aggregation.md) for the full Phase 4 (Token System Resolution) and Phase 5 (Variant System Resolution) pseudocode.

### Phase 5.5: Domain Inference

Infers the project's business domain (e-commerce, saas, fintech, healthcare, creative, education, content, or general) from 4 signal sources: file/directory keywords (0.4), route patterns (0.3), package dependencies (0.2), and README content (0.1).

Uses a proportional confidence formula: `confidence = sum(matched_weights) / sum(all_weights)`. At least 3 signal sources must agree for confidence >= 0.70. Below that threshold, domain falls back to "general".

Gated by `talisman.yml` → `devise.design_system_discovery.domain_inference.enabled` (default: true). Manual override via `domain_inference.override` bypasses inference entirely.

See [domain-inference.md](references/domain-inference.md) for the full algorithm pseudocode, domain registry (8 domains), signal maps (DOMAIN_KEYWORD_MAP, DOMAIN_ROUTE_MAP, DOMAIN_DEP_MAP), worked examples, and edge cases.

### Phase 6: Component Inventory

```
Glob: {src/,}components/ui/*.tsx  (limit 50)
Glob: {src/,}components/**/*.tsx  (limit 100, exclude ui/ subdirectory)

components.path         = primary component directory
components.naming       = detect: PascalCase | kebab-case | snake_case
components.existing     = deduplicated list of component names (strip .tsx)
components.count        = len(components.existing)
```

### Phase 7: Ancillary Fields

```
accessibility.layer     = "radix" IF @radix-ui/* deps >= 3 ELSE "unknown"
class_merge.utility     = "cn" IF cn() from utils detected ELSE "clsx" IF clsx used ELSE "none"
class_merge.import      = resolved import path of utility
dark_mode.strategy      = "class" IF .dark { ... } in CSS ELSE "media" IF @media (prefers-color-scheme) ELSE "unknown"
dark_mode.class_name    = "dark" (default shadcn/ui) — read from components.json "darkMode" if present
tailwind.version        = "4" | "3" | null
tailwind.config_type    = "js" | "ts" | "css" (v4 inline) | null
tailwind.plugins        = list of tailwind plugins from config
path_alias              = "@" IF tsconfig.json contains "paths": {"@/*": ["./src/*"]} ELSE null
evidence_files          = list of all files that contributed a signal (deduplicated)
```

## Output Schema: design-system-profile.yaml

```yaml
# Written to: tmp/plans/{timestamp}/design-system-profile.yaml
# Generated by: discoverDesignSystem() — do not edit manually

library: shadcn_ui              # Primary library: shadcn_ui | untitled_ui | custom_design_system | unknown
library_version: "0.8.0"       # From components.json "style" or package.json version, null if unknown
confidence: 0.95               # 0.0–1.0 — see confidence formula above

tokens:
  format: tailwind-v4-theme    # tailwind-v4-theme | tailwind-v3-config | style-dictionary | css-variables | unknown
  source_file: app/globals.css # Relative path to token source
  semantic_colors:
    background: "oklch(1 0 0)"
    foreground: "oklch(0.145 0 0)"
    primary: "oklch(0.205 0 0)"
    # ... additional token values
  spacing_scale: []            # List of spacing values, empty if not detected
  border_radius_scale:
    default: "0.625rem"        # --radius from components.json or CSS
  shadow_scale: []             # List of shadow values, empty if not detected

variants:
  system: cva                  # cva | css-in-js | css-modules | unknown
  pattern: class-variance-authority

components:
  path: src/components/ui      # Primary component directory (relative)
  naming: PascalCase           # PascalCase | kebab-case | snake_case
  existing:                    # Discovered component names
    - Button
    - Card
    - Dialog
    - Input
  count: 4

accessibility:
  layer: radix                 # radix | unknown

class_merge:
  utility: cn                  # cn | clsx | none
  import: "@/lib/utils"

dark_mode:
  strategy: class              # class | media | unknown
  class_name: dark

tailwind:
  version: "4"                 # "4" | "3" | null
  config_type: css             # js | ts | css | null
  plugins: []                  # e.g., ["@tailwindcss/typography", "tailwindcss-animate"]

domain:
  inferred: "e-commerce"         # Business domain: e-commerce | saas | fintech | healthcare | creative | education | content | general
  confidence: 0.90               # 0.0–1.0 — proportional weighted formula (NOT library detection formula)
  source: "inferred"             # "inferred" | "talisman_override" | "disabled" | "low_confidence"

path_alias: "@"                # "@" | null
evidence_files:
  - package.json
  - components.json
  - app/globals.css
```

## Edge Cases

8 edge cases are handled: Monorepo (EC-1), Migration (EC-2), Custom-on-Radix (EC-3), Bare Tailwind (EC-4), Figma Code Connect (EC-5), Storybook-Only (EC-6), CSS Modules (EC-7), RSC (EC-8).

See [edge-cases.md](references/edge-cases.md) for detection criteria and handling rules for each case.

## discoverUIBuilder(sessionCacheDir, repoRoot, detectedLibrary, figmaFramework)

**Input**: `sessionCacheDir` — session-scoped temp directory; `repoRoot` — repository root path; `detectedLibrary` — (optional) library from `discoverDesignSystem()`, null for independent operation; `figmaFramework` — (optional) framework from `discoverFigmaFramework()`, null when Figma data unavailable
**Output**: `builder-profile.yaml` (written to `{sessionCacheDir}/`), builder config object returned to caller or null

Discovers which UI builder MCP is available. Operates independently of `discoverDesignSystem()` — when called without `detectedLibrary`, skips library-dependent steps (2-4) and runs heuristic-only MCP detection. When called with `detectedLibrary`, runs the full 5-step priority cascade.

Called after `discoverDesignSystem()` during devise Phase 0.5 and strive Phase 1.5. Called independently during design-prototype Phase 0 (Layer 3).

5-step priority cascade: session cache → talisman binding (0.95) → project skill frontmatter (0.90) → plugin skill frontmatter (0.90) → known MCP registry + heuristic (0.50). First match wins. Steps 2-4 require `detectedLibrary`; Step 5 runs unconditionally.

See [ui-builder-discovery.md](references/ui-builder-discovery.md) for the full algorithm, `parseBuilderFrontmatter()`, known MCP registry, heuristic detection, and `builder-profile.yaml` output schema.

## discoverFrontendStack(repoRoot)

**Input**: `repoRoot` — repository root path
**Output**: frontend stack profile object with framework, version, build tool, CSS framework, and confidence

Extends the existing `detectTypeScriptStack()` with version extraction for the design-prototype pipeline (Layer 1 of 3-layer detection). Extracts major version from `package.json` semver strings, detects build tool (vite/next/webpack), and identifies CSS framework with version (Tailwind v3 vs v4).

Fallback: `{ framework: null, css_framework: "tailwind", css_version: 4, confidence: 0.0 }` — ensures the pipeline always has a valid CSS target.

See [tiered-scanning.md](references/tiered-scanning.md) for the full algorithm, `extractMajor()` helper, output schema, and relationship to `detectTypeScriptStack()`.

## discoverFigmaFramework(figmaApiResponse, nodeId)

**Input**: `figmaApiResponse` — full Figma API response object; `nodeId` — target node ID from Figma URL
**Output**: detection result with framework identifier, numeric score (0.0-1.0), match details, and alternatives — or null-safe fallback

Analyzes Figma API component metadata (component names, variant props, icon names, typography styles, effect styles, remote flags) to identify which UI library a design references. Layer 2 of the 3-layer detection pipeline. Called during `design-prototype` Phase 0 after Figma data is fetched. Not called in text-only (`--describe`) mode.

Uses weighted signal matching (component 0.35, variant 0.25, icon 0.15, typography 0.10, effect 0.05, remote 0.10) against the framework signature registry. Returns `framework: null` when score < 0.40 or margin between top two candidates < 0.15.

See [figma-framework-detection.md](references/figma-framework-detection.md) for the full algorithm, signal weights, error handling, and output schema.
See [figma-framework-signatures.md](references/figma-framework-signatures.md) for the framework signature registry (untitled_ui, shadcn_ui, material_ui, ant_design).

## composeDesignContext(frontendStack, figmaFramework, builderMCP, mode)

**Input**: `frontendStack` — Layer 1 output; `figmaFramework` — Layer 2 output (null in text-only mode); `builderMCP` — Layer 3 output (null when no builder); `mode` — `"url"` or `"describe"`
**Output**: unified `DesignContext` object with `synthesis_strategy` (`"library"` | `"hybrid"` | `"tailwind"`)

Merges all 3 detection layers into a single context consumed by `design-prototype` Phase 1+. Includes conflict resolution (e.g., Vue project + shadcn Figma → tailwind), mode guard for text-only paths, and timeout budget (5000ms default).

See [design-context.md](references/design-context.md) for the full composition algorithm, decision matrix, conflict resolution rules, parallelization strategy, and output schema.

## Integration Points

### devise Phase 0.5 (Pre-Brainstorm)

```
profile = discoverDesignSystem(repoRoot, sessionCacheDir)
IF profile.confidence >= talisman.devise.design_system_discovery.confidence_threshold:
  INJECT profile summary into brainstorm context
  SET brainstorm_context.design_system = profile

// discoverUIBuilder() runs immediately after discoverDesignSystem()
uiBuilder = discoverUIBuilder(sessionCacheDir, repoRoot)
IF uiBuilder is not null:
  INJECT builder summary into brainstorm context
  SET brainstorm_context.ui_builder = uiBuilder
```

### strive Worker Injection (Phase 1.5)

See [strive worker-prompts.md](../strive/references/worker-prompts.md) for the
component constraint injection protocol.

Workers receive a trimmed profile (max `talisman.strive.frontend_component_context.max_profile_lines` lines).

### arc Phase 2.8 Semantic Verification

During semantic verification, the profile validates that implementation code:
- Uses the correct component import paths (`@/components/ui/*` for shadcn/ui)
- Applies the correct variant system (cva() calls, not ad-hoc className strings)
- Respects dark mode strategy (no hardcoded colors, only semantic tokens)

## Configuration

```yaml
# talisman.yml
devise:
  design_system_discovery:
    enabled: true                # Master switch (default: true)
    confidence_threshold: 0.5   # Min confidence to inject profile into context

strive:
  frontend_component_context:
    enabled: true                # Inject profile into strive worker prompts (default: true)
    max_profile_lines: 200       # Cap profile size before injection (default: 200)
    token_cap_lines: 50          # Cap token constraint section (default: 50)

stack_awareness:
  design_compliance: true        # Enable design-system-compliance-reviewer (default: true)
```

## Cross-References

- [stacks](../stacks/SKILL.md) — Stack detection; design system discovery is a sub-layer
- [frontend-design-patterns](../frontend-design-patterns/SKILL.md) — Implementation patterns using the discovered system
- [design-sync](../design-sync/SKILL.md) — Figma design synchronization workflow
- [strive](../strive/SKILL.md) — Worker prompt injection for component constraints
- [untitledui-mcp](../untitledui-mcp/SKILL.md) — Built-in UntitledUI builder skill with `builder-protocol` frontmatter (detected by `discoverUIBuilder()`)
- [domain-inference.md](references/domain-inference.md) — Domain inference algorithm, signal maps, and worked examples
