1 Commits

Author SHA1 Message Date
Cursor Agent bda5c0e3ed feat(plugins): token-map atom impl (Phase 6/7 entry slice)
Plan P1 / spec §10 / §21.3.1.

apps/daemon/src/plugins/atoms/token-map.ts ships the daemon-side
crosswalk behind the SKILL.md fragment landed in §3.M3. Given a
project cwd that already has a token bag (`code/tokens.json` from
design-extract OR `figma/tokens.json` from figma-extract) plus the
active design system's token vocabulary, the runner produces:

  <cwd>/token-map/colors.json
  <cwd>/token-map/typography.json
  <cwd>/token-map/spacing.json
  <cwd>/token-map/radius.json
  <cwd>/token-map/shadow.json
  <cwd>/token-map/unmatched.json     [{ source, sourceName?, kind, reason, hint? }]
  <cwd>/token-map/meta.json          { sourceKind, generatedAt,
                                       atomDigest, designSystemId?,
                                       targetTokenCount,
                                       sourceTokenCount,
                                       matchedTokenCount }

Match strategies (deterministic, first-win):

  1. Exact value match  (#5b8def === #5b8def).
  2. Normalised hex     (#abc → #aabbcc, ignores case).
  3. Fuzzy name         (--color-primary → --ds-color-primary; strips
                         leading -- / ds- / odds- / theme- prefixes
                         and lower-cases).

Anything else lands unmatched[] with one of:

  - 'no-target-equivalent'  no target with same value/name
  - 'target-collision'      multiple sources mapped to one target
                            (first wins; subsequent listed unmatched
                             with hint pointing at the first claimant)
  - 'invalid-source'        malformed source value

Strict mode aborts on any unmatched; soft mode (default) populates
unmatched.json + continues so the human can audit.

Helper:

  parseDesignSystemTokens(body) → DesignSystemToken[]

  Heuristic DESIGN.md token extraction. Lifts CSS custom properties
  (`--ds-color-fg: #111`) and markdown table rows that look like
  token declarations. Daemon callers may pass a hand-curated bag
  instead of relying on this parser.

Existing patch-edit typecheck noise fixed in passing:
  - PatchStepRecord now extends Omit<RewriteStep, 'rationale'> so
    optional override is sound under exactOptionalPropertyTypes.
  - rationale assignment guarded against undefined.

Daemon tests: 1588 → 1600 (+12 cases on plugins-token-map: exact
match, normalised hex, fuzzy name, no-target-equivalent, target-
collision, strict mode abort, multi-kind crosswalk, code+figma
disk inputs round-trip + bucket persistence + missing-input error,
DESIGN.md CSS custom property + markdown table parsers).

Co-authored-by: Tom Huang <1043269994@qq.com>
2026-05-09 15:24:22 +00:00