@catchdrift/cli 0.1.18 → 0.1.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,229 @@
1
+ ---
2
+ description: "Sync Figma and/or Storybook into drift.config.ts and regenerate CLAUDE.md, .cursorrules, and .windsurfrules. Run after adding new DS components or updating Figma."
3
+ allowed-tools: Read, Glob, Grep, Bash, Edit, Write
4
+ argument-hint: "[figma | storybook | tokens | status | --dry-run]"
5
+ disable-model-invocation: true
6
+ ---
7
+
8
+ # /drift-sync — Sync Figma + Storybook → update manifest
9
+
10
+ Pull the latest component state from Figma and/or Storybook and keep
11
+ `drift.config.ts`, `CLAUDE.md`, `.cursorrules`, and `.windsurfrules` in sync.
12
+ Works with any product team — property management, SaaS, fintech, consumer, B2B.
13
+
14
+ ## Arguments: `$ARGUMENTS`
15
+ - *(no args)* — sync from both Storybook and Figma, update all config files
16
+ - `figma` — pull component list from Figma only (all pages)
17
+ - `storybook` — re-discover components from Storybook index only
18
+ - `tokens` — run figma-sync (design tokens + icons only, no component changes)
19
+ - `status` — report sync status without making changes (safe read-only audit)
20
+ - `--dry-run` — show what would change without writing any files
21
+
22
+ ---
23
+
24
+ ## Step 1 — Read current state
25
+
26
+ Read `drift.config.ts` (or `src/ds-coverage/config.ts`) to get:
27
+ - **Figma source** — the config may use either shape:
28
+ - Single file: `figmaFileKey` + optional `figmaComponentPages` (one-file setup)
29
+ - Multi-file: `figmaFiles: [{ key: string, componentPages?: string[] }, ...]` (components spread across multiple Figma files, e.g. Core DS, Icons, Patterns)
30
+ - Normalise both into a working array: `const files = config.figmaFiles ?? (config.figmaFileKey ? [{ key: config.figmaFileKey, componentPages: config.figmaComponentPages }] : [])`
31
+ - `storybookUrl`
32
+ - `components` — the current registry (component name → storyPath / figmaLink)
33
+ - `threshold`
34
+ - Any `approvedGaps` entries
35
+
36
+ **Component pages** are an inclusive filter — only pull components from these pages. If `componentPages` is not set for a file, include all pages (and flag any page whose name contains "wip", "in progress", "draft", "proposal", "graveyard", or "archive" as potentially unready so the team can review them).
37
+
38
+ ---
39
+
40
+ ## Step 2 — Dispatch
41
+
42
+ ### `status` → Read-only audit
43
+
44
+ Report sync status across all sources without changing anything:
45
+
46
+ ```
47
+ ## Drift Sync Status — <date>
48
+
49
+ Storybook: <storybookUrl>
50
+ Last successful sync: <date if known, else "unknown">
51
+ Reachable: ✅ / ❌
52
+
53
+ Figma files: <N> configured
54
+ figma.com/design/<key1> Reachable: ✅ / ❌
55
+ figma.com/design/<key2> Reachable: ✅ / ❌ (if multi-file)
56
+ FIGMA_API_TOKEN: ✅ set / ❌ missing
57
+
58
+ Config: drift.config.ts
59
+ Components registered: N
60
+ Missing storyPath: N
61
+ Missing figmaLink: N
62
+ Approved gaps: N
63
+
64
+ AI rules files:
65
+ CLAUDE.md: ✅ / ❌ (stale — last component: <X>)
66
+ .cursorrules: ✅ / ❌
67
+ .windsurfrules: ✅ / ❌
68
+ ```
69
+
70
+ ---
71
+
72
+ ### `tokens` or no args → sync tokens from Figma
73
+
74
+ Run:
75
+ ```bash
76
+ npm run figma-sync
77
+ ```
78
+
79
+ Requires `FIGMA_API_TOKEN`. If missing:
80
+ ```
81
+ export FIGMA_API_TOKEN=your-token
82
+ npm run figma-sync
83
+
84
+ Get token: figma.com → Profile → Settings → Security → Personal access tokens
85
+ ```
86
+
87
+ After running, report which token categories updated (colors added/changed, typography changes, spacing changes).
88
+
89
+ ---
90
+
91
+ ### `storybook` or no args → re-discover from Storybook
92
+
93
+ Fetch `{storybookUrl}/index.json`. The response has a flat map of all stories
94
+ across all story files. Parse it to get unique component names and story paths.
95
+
96
+ If Storybook isn't reachable, try the deployed URL (`chromaticUrl`) from config. If
97
+ neither is reachable, report:
98
+ ```
99
+ ⚠️ Storybook not reachable at <url>
100
+ Run `npm run storybook` first, or provide your deployed Storybook URL in drift.config.ts:
101
+ chromaticUrl: 'https://main--abc123.chromatic.com'
102
+ ```
103
+
104
+ Compare against `config.components` and report:
105
+
106
+ ```
107
+ ## Storybook sync
108
+
109
+ New (in Storybook, not in config):
110
+ + DataTable → story: data-table--default
111
+ + FilterBar → story: filters-filter-bar--default
112
+
113
+ Removed (in config, no matching story):
114
+ - OldWidget
115
+
116
+ Unchanged: 34 components
117
+ ```
118
+
119
+ Ask for each new/removed component before changing config. For removed components,
120
+ ask whether to delete from config or keep with a `deprecated: true` flag.
121
+
122
+ ---
123
+
124
+ ### `figma` or no args → pull all published components from Figma
125
+
126
+ **Key:** Figma components live on different pages. Use the dedicated
127
+ `/components` endpoint — it returns ALL published components across ALL pages
128
+ with page metadata included. Do NOT try to walk the file tree page by page.
129
+
130
+ Iterate over every file in the normalised `files` array (see Step 1). For each:
131
+ ```
132
+ GET https://api.figma.com/v1/files/{file.key}/components
133
+ Headers: X-Figma-Token: {FIGMA_API_TOKEN}
134
+ ```
135
+
136
+ Each component in the response has:
137
+ - `name` — full path e.g. `"Button/Primary/Default"` or `"Forms/Input/Filled"`
138
+ - `node_id` — unique ID for building the figmaLink URL
139
+ - `containing_frame.name` — the frame it lives in
140
+ - `containing_frame.pageName` — **the Figma page it's on**
141
+ - `description` — designer's notes (preserve this — use as component description in config)
142
+
143
+ Group and display results by file, then by page within each file. If multiple files are configured, prefix each section with the file key/URL so it's clear which file the components come from:
144
+ ```
145
+ ## Figma components found
146
+
147
+ ### figma.com/design/<key1> (Core DS)
148
+ 📄 Primitives (12 components)
149
+ ✅ Button → in config
150
+ ✅ Input → in config
151
+ ❌ Toggle → NOT in config (node: 123:456)
152
+ ❌ Checkbox → NOT in config (node: 123:789)
153
+
154
+ 📄 🚧 In Progress (3 components)
155
+ ⚠️ SearchBar → draft (will not be added)
156
+ ⚠️ FilterChip → draft (will not be added)
157
+
158
+ ### figma.com/design/<key2> (Icons & Patterns)
159
+ 📄 Patterns (8 components)
160
+ ✅ TenantsTable → in config
161
+ ❌ DataGrid → NOT in config (node: 456:123)
162
+ ```
163
+
164
+ For each file, apply the `componentPages` filter:
165
+ - If `componentPages` is set: only include components from those pages; skip everything else
166
+ - If not set: include all pages, but flag any page matching "wip/draft/graveyard/archive/proposal" in a separate "Unreviewed pages" section so the team can decide whether to add them
167
+
168
+ For each component NOT in config (excluding draft pages), ask:
169
+ ```
170
+ Add these to drift.config.ts?
171
+ - Toggle (key1 / Primitives page)
172
+ - Checkbox (key1 / Primitives page)
173
+ - DataGrid (key2 / Patterns page)
174
+
175
+ For each, I'll also add the figmaLink pointing to that node.
176
+ Reply with which ones to add, or "all" / "none".
177
+ ```
178
+
179
+ When adding, build the figmaLink URL using the file key that component came from:
180
+ ```
181
+ https://www.figma.com/design/{file.key}?node-id={node_id}
182
+ ```
183
+
184
+ Also report components in the codebase (from static scan of `src/`) that exist
185
+ in Storybook but have NO matching Figma component — these are code-first gaps:
186
+ ```
187
+ In code but not in Figma (consider pushing to Figma):
188
+ ⚡ OccupancyWidget — used 1× — run /drift-push OccupancyWidget to add
189
+ ⚡ FMKPIRow — used 1×
190
+ ```
191
+
192
+ ---
193
+
194
+ ## Step 3 — Update files
195
+
196
+ After confirmation (or immediately if `--dry-run` was NOT passed), update:
197
+
198
+ 1. **`drift.config.ts`** — add components with storyPath + figmaLink where available; mark removed with `deprecated: true` rather than deleting (preserves history)
199
+ 2. **`CLAUDE.md`** — regenerate only the components table (preserve all other content — rules, workflow, etc.)
200
+ 3. **`.cursorrules`** — same regeneration (Cursor reads this automatically)
201
+ 4. **`.windsurfrules`** — same regeneration (Windsurf reads this automatically); create if it doesn't exist
202
+
203
+ For AI rules files, regenerate ONLY the component table section, bounded by these markers:
204
+ ```
205
+ <!-- drift:components-start -->
206
+ ...
207
+ <!-- drift:components-end -->
208
+ ```
209
+ If markers don't exist, append the table at the end of the file.
210
+
211
+ Report:
212
+ ```
213
+ ## Sync complete — <date>
214
+
215
+ drift.config.ts — +3 added, 0 deprecated (37 total)
216
+ CLAUDE.md — components table updated (37 components)
217
+ .cursorrules — components table updated
218
+ .windsurfrules — created (37 components)
219
+
220
+ New figmaLinks added: Toggle, Checkbox, DataGrid
221
+ New storyPaths added: DataTable, FilterBar
222
+
223
+ Storybook links: 37/37 ✅
224
+ Figma links: 31/37 (6 missing — run /drift-push gaps to add them)
225
+
226
+ Next: npm run dev and press D to see the updated overlay.
227
+ ```
228
+
229
+ If `--dry-run` was passed, show what would change without writing anything.
@@ -0,0 +1,252 @@
1
+ ---
2
+ description: "Analyze DS coverage across the codebase. Find gaps, suggest DS replacements, migrate components, approve exceptions, promote candidates. Sub-commands: fix, approve, promote, manifest, check, history, audit."
3
+ allowed-tools: Read, Glob, Grep, Bash, Edit
4
+ argument-hint: "[fix <ComponentName> | approve <Name> \"<reason>\" | promote <Name> | manifest | check | history | audit]"
5
+ ---
6
+
7
+ # /drift — Design System Drift Analyzer
8
+
9
+ Analyze design system coverage for this codebase. Identify components that drift from
10
+ the DS, suggest replacements, and optionally migrate code. Works with any React
11
+ product team — property management, SaaS, fintech, e-commerce, etc.
12
+
13
+ ## Arguments: `$ARGUMENTS`
14
+
15
+ Supported sub-commands:
16
+ - *(no args)* — full coverage report + top gap analysis
17
+ - `fix <ComponentName>` — migrate one custom component to its DS equivalent
18
+ - `approve <ComponentName> "<rationale>"` — approve a gap with documented rationale
19
+ - `promote <ComponentName>` — flag a high-frequency custom component for DS promotion
20
+ - `manifest` — print the DS component registry with story + Figma links
21
+ - `check` — run the headless drift-check script and parse results
22
+ - `history` — show coverage trend over last N scans (from saved reports)
23
+ - `audit` — full audit mode: coverage + token violations + rationale gaps + promotion candidates
24
+
25
+ ---
26
+
27
+ ## Step 1 — Read the DS registry
28
+
29
+ Read `src/ds-coverage/config.ts` (or `drift.config.ts` at project root) to understand:
30
+ - Every registered DS component, its story path, and Figma link
31
+ - `threshold` — the CI pass/fail threshold
32
+ - `storybookUrl` and `figmaFileKey`
33
+
34
+ ---
35
+
36
+ ## Step 2 — Dispatch on $ARGUMENTS
37
+
38
+ ### No arguments → Full Coverage Report
39
+
40
+ 1. Glob `src/**/*.tsx` (excluding `src/stories/`, `src/tokens/`, `node_modules/`, `*.stories.*`, `*.test.*`, `*.spec.*`) to find all screens, views, and feature files.
41
+ 2. Grep each file for JSX component usage. Classify every component as:
42
+ - **DS** — name is in `config.components`
43
+ - **Approved gap** — custom, but has an approval entry (check for `// drift-approved:` comment or approval record)
44
+ - **Custom** — name is not in `config.components` and not approved
45
+ 3. Compute per-file and overall DS coverage %.
46
+ 4. List the top custom components by frequency (the gap map).
47
+ 5. For any custom component used ≥ 3 times:
48
+ - Check whether a DS equivalent exists → suggest it
49
+ - If used ≥ 5 times → flag as **promotion candidate**
50
+ 6. Print a report in this format:
51
+
52
+ ```
53
+ ## Drift Report — <date>
54
+
55
+ Overall DS coverage: XX% (threshold: XX%)
56
+ Status: ✅ PASS or 🔴 FAIL
57
+
58
+ ### By file
59
+ | File | DS | Custom | Approved | Coverage |
60
+ |------|----|--------|----------|----------|
61
+ | ... | .. | ... | ... | ...% |
62
+
63
+ ### Top gaps (custom components not in DS)
64
+ | Component | Uses | Status | Suggested DS replacement |
65
+ |-----------|------|--------|--------------------------|
66
+ | BtnGrp | 6 | ⚠️ Gap | Use `<Tabs>` (segmented variant) |
67
+ | LinkBtn | 5 | 🔁 Promote candidate | Use `<Button variant="ghost">` |
68
+ | AvatarRow | 3 | ✅ Approved — "needed for nav micro-interaction" | — |
69
+
70
+ ### Token violations
71
+ | File | Violation | Line |
72
+ |------|-----------|------|
73
+ | ... | Hardcoded `#3b82f6` — use `var(--ds-color-brand-500)` | 42 |
74
+
75
+ ### Promotion candidates (used ≥5× with no DS equivalent)
76
+ These components appear frequently enough to justify adding to the DS:
77
+ 1. <ComponentName> — used N× across N files
78
+ → Run `/drift promote <ComponentName>` to create a promotion request
79
+
80
+ ### Recommendations
81
+ 1. Highest impact: migrate <X> → saves N% coverage across N files
82
+ 2. ...
83
+ ```
84
+
85
+ ---
86
+
87
+ ### `fix <ComponentName>`
88
+
89
+ 1. Find all usages of `<ComponentName>` across `src/` (excluding stories and tests).
90
+ 2. Read the DS equivalent component file to understand its props API.
91
+ 3. For each usage, generate a code diff that replaces the custom component with the DS component, preserving existing behavior.
92
+ 4. Show a summary first:
93
+ ```
94
+ Found 6 usages of <ComponentName> across 3 files.
95
+ DS equivalent: <DSName> — props mapping:
96
+ old.size="large" → new.size="lg"
97
+ old.color="red" → new.variant="danger"
98
+
99
+ Estimated coverage improvement: +2.3%
100
+
101
+ Apply changes? (yes/no/preview)
102
+ ```
103
+ 5. After applying, re-run coverage calculation and show before/after.
104
+
105
+ ---
106
+
107
+ ### `approve <ComponentName> "<rationale>"`
108
+
109
+ Approve a custom component as an intentional exception to DS coverage rules.
110
+
111
+ Use this when a custom component is genuinely needed and cannot be replaced by a DS component. Rationale must include:
112
+ - Why no DS component covers this need
113
+ - Whether it should be proposed for DS inclusion in the future
114
+
115
+ 1. Verify the component is actually used in the codebase.
116
+ 2. Check it's not already approved.
117
+ 3. Add an approval entry to `drift.config.ts`:
118
+ ```ts
119
+ approvedGaps: {
120
+ '<ComponentName>': {
121
+ rationale: '<rationale>',
122
+ approvedBy: '<ask for name>',
123
+ approvedAt: '<today ISO date>',
124
+ promoteToDS: true/false, // ask: "Should this be proposed for DS inclusion?"
125
+ }
126
+ }
127
+ ```
128
+ 4. Also add an inline comment convention to the usage site so rationale travels with the code:
129
+ ```tsx
130
+ {/* drift:ignore reason="<rationale>" approvedBy="<name>" */}
131
+ <ComponentName ... />
132
+ ```
133
+ This is visible in code review without needing to cross-reference config.
134
+ 5. Confirm: "Approved. This component will show as ✅ Approved in drift reports and will not count against coverage."
135
+
136
+ ---
137
+
138
+ ### `promote <ComponentName>`
139
+
140
+ Flag a high-frequency custom component as a DS promotion candidate.
141
+
142
+ 1. Read the component file (or search for it if it's a one-off inline component).
143
+ 2. Count its usage frequency and list the files it appears in.
144
+ 3. Generate a promotion brief:
145
+ ```
146
+ ## DS Promotion Request: <ComponentName>
147
+
148
+ **Usage:** N× across N files
149
+ **Files:** list up to 5 most common locations
150
+
151
+ **Props API (current):**
152
+ <extracted interface or inferred from usage>
153
+
154
+ **Suggested DS entry:**
155
+ <ComponentName>: {
156
+ storyPath: '<suggested-story-path>',
157
+ }
158
+
159
+ **Design request:** This component appears frequently enough to warrant a
160
+ Figma design + DS review. Recommend filing a design request.
161
+
162
+ Next steps:
163
+ 1. Designer creates the spec in Figma
164
+ 2. Run /drift-push <ComponentName> to attach implementation notes
165
+ 3. After Figma review, run /drift-sync to register it officially
166
+ ```
167
+ 4. Ask if user wants to open a Jira ticket (if `jiraBaseUrl` is configured).
168
+
169
+ ---
170
+
171
+ ### `manifest`
172
+
173
+ Print a formatted table of all DS components from `config.components`:
174
+
175
+ ```
176
+ ## DS Component Registry — <date>
177
+
178
+ | Component | Story | Figma | Status |
179
+ |--------------------|-------|-------|--------|
180
+ | Button | ✅ | ✅ | Stable |
181
+ | Tabs | ✅ | — | Needs Figma |
182
+ | ... | ... | ... | ... |
183
+
184
+ Approved gaps (N):
185
+ | Component | Rationale | Approved by |
186
+ |--------------|-----------|-------------|
187
+ | CustomHeader | "..." | Michelle, 2026-01-15 |
188
+
189
+ Missing story paths: X components
190
+ Missing Figma links: X components
191
+ Run /drift-sync to fill gaps automatically.
192
+ ```
193
+
194
+ ---
195
+
196
+ ### `check`
197
+
198
+ Run the headless drift check and parse results:
199
+
200
+ ```bash
201
+ npm run build && npx vite preview --port 4173 &
202
+ npx wait-on http://localhost:4173 --timeout 30000
203
+ node scripts/drift-check.mjs --url http://localhost:4173 --json > /tmp/drift-report.json
204
+ ```
205
+
206
+ Then read `/tmp/drift-report.json` and print the full report format including:
207
+ - Coverage % vs threshold
208
+ - Per-route breakdown
209
+ - Token violations (hardcoded colors, spacing)
210
+ - Gap map with promotion candidates
211
+
212
+ ---
213
+
214
+ ### `history`
215
+
216
+ Read any saved `drift-report-*.json` files or GitHub Actions artifacts from `.github/`.
217
+ Show coverage trend:
218
+
219
+ ```
220
+ ## Coverage History
221
+
222
+ Date Coverage Delta Status
223
+ 2026-03-30 78% +2% 🔴 Below threshold
224
+ 2026-03-23 76% +1% 🔴 Below threshold
225
+ 2026-03-16 75% — 🔴 Below threshold
226
+
227
+ Trend: ↑ improving (+3% over 3 weeks)
228
+ At current rate, threshold (80%) reached in ~2 weeks.
229
+ ```
230
+
231
+ ---
232
+
233
+ ### `audit`
234
+
235
+ Full audit combining all modes:
236
+ 1. Run `check` (headless scan)
237
+ 2. Run the static analysis (no-args path)
238
+ 3. Cross-reference: flag any component approved as a gap that now has a DS equivalent
239
+ 4. List components that have been custom for ≥ 30 days (from git log if available)
240
+ 5. Output a single comprehensive report suitable for a DS quarterly review
241
+
242
+ ---
243
+
244
+ ## Style rules for output
245
+
246
+ - Lead with the numbers — coverage %, counts, file names
247
+ - Use exact component names from the codebase (case-sensitive)
248
+ - When suggesting a DS replacement, always show a before/after code snippet
249
+ - If coverage is below threshold, surface the 3 highest-impact gaps first with estimated improvement per fix
250
+ - Never suggest creating new components — only DS components from `config.components`
251
+ - For teams unfamiliar with the DS, always explain *why* a replacement is better, not just *what* to use
252
+ - Approved gaps always show ✅ and are excluded from failure calculations
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@catchdrift/cli",
3
- "version": "0.1.18",
3
+ "version": "0.1.20",
4
4
  "description": "CLI for Drift — install, check, and manage design system coverage for any React app.",
5
5
  "keywords": [
6
6
  "design-system",
@@ -31,6 +31,7 @@
31
31
  "bin",
32
32
  "src",
33
33
  "scripts",
34
+ "commands",
34
35
  "LICENSE",
35
36
  "README.md"
36
37
  ],
@@ -32,6 +32,7 @@ import {
32
32
  import {
33
33
  writeDriftConfig,
34
34
  writeAIRulesFiles,
35
+ writeClaudeSkills,
35
36
  patchAppEntry,
36
37
  writeGithubAction,
37
38
  } from '../lib/writers.mjs'
@@ -349,7 +350,8 @@ export async function init(argv) {
349
350
  storybookUrl: storybookUrl || '',
350
351
  figmaFiles: figmaFiles.length ? figmaFiles : undefined,
351
352
  })
352
- spinner.stop(`Written: ${rulesFiles.join(', ')}`)
353
+ const skillFiles = writeClaudeSkills(cwd)
354
+ spinner.stop(`Written: ${rulesFiles.join(', ')}${skillFiles.length ? ` + ${skillFiles.length} Claude skills` : ''}`)
353
355
 
354
356
  // ── Step 8: Install @catchdrift/overlay ──────────────────────────────────────
355
357
  const pkgJson = JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf8'))
@@ -395,66 +397,72 @@ export async function init(argv) {
395
397
  console.log('')
396
398
  p.outro(pc.green('Drift is set up ✓'))
397
399
 
398
- // Build next steps dynamically based on what was configured
399
- const nextSteps = []
400
-
401
- if (dsPackages?.length) {
402
- nextSteps.push(` ${pc.cyan('npx catchdrift sync')} Auto-populate registry from ${dsPackages.join(', ')}`)
403
- } else if (!sources.includes('storybook') || Object.keys(components).length === 0) {
404
- nextSteps.push(` ${pc.cyan('npx catchdrift sync')} Auto-populate registry once you have a DS package`)
405
- }
400
+ // ── What was set up (human-readable) ────────────────────────────────────────
401
+ const patchedShort = patched ? patched.replace(cwd + '/', '').replace(cwd, '') : null
402
+ console.log(`
403
+ ${pc.bold('What was set up:')}
404
+ ${pc.green('')} Overlay added to your app ${patchedShort ? pc.dim('(' + patchedShort + ')') : pc.yellow('— see manual step below')}
405
+ ${pc.green('✓')} AI rules written ${pc.dim(' your AI tools will use DS components by default')}
406
+ ${pc.green('')} Claude skills added ${pc.dim('— /drift-sync, /drift-scaffold, /drift-context, ...')}
407
+ ${addCI ? pc.green('✓') + ' GitHub check added ' + pc.dim('— every PR will show a coverage score') : pc.dim('○ GitHub check skipped')}`)
406
408
 
407
- nextSteps.push(` ${pc.cyan('npm run dev')} Start your app, then press ${pc.bold('D')} to open Drift`)
408
- nextSteps.push(` ${pc.cyan('npx catchdrift check')} Run a coverage scan ${pc.dim('(app must be running)')}`)
409
- nextSteps.push(` ${pc.cyan('git push')} First PR will show a drift delta comment`)
409
+ // ── Immediate next step specific to what was configured ───────────────────
410
+ console.log(`\n${pc.bold('Do this now:')}`)
410
411
 
411
- if (!sources.includes('storybook') && !storybook.found) {
412
- nextSteps.push(`\n ${pc.yellow('Storybook not set up')} — run ${pc.bold('npx storybook@latest init')} when ready,`)
413
- nextSteps.push(` then re-run ${pc.bold('npx catchdrift init')} to complete the coverage loop.`)
414
- }
412
+ if (figmaFiles.length > 0 && skillFiles.length > 0) {
413
+ console.log(`
414
+ Your components are in Figma. Pull them into the registry now:
415
415
 
416
- console.log(`
417
- ${pc.bold('What was created:')}
418
- drift.config.ts ${pc.dim('DS component registry')}
419
- ${rulesFiles.map(f => f.padEnd(34)).join('\n ')}${pc.dim('AI constraints')}
420
- ${addCI ? '.github/workflows/drift-check.yml ' + pc.dim('CI drift check on every PR') : ''}
421
- ${patched ? patched.padEnd(34) + pc.dim('DriftOverlay added (dev-only)') : ''}
422
-
423
- ${pc.bold('Next steps:')}
424
- ${nextSteps.join('\n')}
425
-
426
- ${pc.bold('Your team\'s daily commands (Claude Code):')}
427
- ${pc.blue('/drift-context')} ${pc.dim('See DS state at a glance — run this first')}
428
- ${pc.blue('/drift-prd')} ${pc.dim('Generate a component inventory for a PRD')}
429
- ${pc.blue('/drift-scaffold')} ${pc.dim('Scaffold a new screen using only DS components')}
430
- ${pc.blue('/drift check')} ${pc.dim('Verify coverage before submitting a PR')}
431
- ${pc.blue('/drift-sync')} ${pc.dim('Re-sync registry after adding DS components')}
432
- ${pc.blue('/drift fix <X>')} ${pc.dim('Migrate a custom component to its DS equivalent')}
433
-
434
- ${pc.dim('Docs: https://catchdrift.ai · Issues: https://github.com/dyoon92/design-drift/issues')}
416
+ ${pc.cyan('1.')} Open ${pc.bold('Claude Code')} in this project folder
417
+ ${pc.cyan('2.')} Run: ${pc.blue('/drift-sync figma')}
418
+ This reads your Figma file and registers all your DS components.
419
+ ${pc.cyan('3.')} Run: ${pc.bold('npm run dev')} → press ${pc.bold('D')} → see live coverage
435
420
  `)
421
+ } else if (sources.includes('storybook') && Object.keys(components).length > 0) {
422
+ console.log(`
423
+ Your components were imported from Storybook ✓
436
424
 
437
- if (!patched) {
438
- console.log(`${pc.yellow('Manual step needed')} add DriftOverlay to your app entry point:
425
+ ${pc.cyan('1.')} Run: ${pc.bold('npm run dev')}
426
+ ${pc.cyan('2.')} Press ${pc.bold('D')} to open the Drift overlay you should see real coverage.
427
+ `)
428
+ } else if (dsPackages?.length) {
429
+ console.log(`
430
+ ${pc.cyan('1.')} Run: ${pc.bold('npx catchdrift sync')}
431
+ Scans your codebase for imports from ${dsPackages.join(', ')} and registers them.
432
+ ${pc.cyan('2.')} Run: ${pc.bold('npm run dev')} → press ${pc.bold('D')} → see live coverage
433
+ `)
434
+ } else {
435
+ console.log(`
436
+ ${pc.cyan('1.')} Run: ${pc.bold('npm run dev')} → press ${pc.bold('D')} → the overlay opens
437
+ Coverage will show 0% until you register your DS components.
438
+ ${pc.cyan('2.')} Add your components to ${pc.bold('drift.config.ts')} or connect a source:
439
+ • Figma: re-run ${pc.bold('npx catchdrift init')} and select Figma
440
+ • npm: add ${pc.bold('dsPackages')} to drift.config.ts, then run ${pc.bold('npx catchdrift sync')}
439
441
  `)
440
- console.log(` ${pc.dim('// src/main.tsx')}`)
441
- console.log(` import { DriftOverlay } from '@catchdrift/overlay'`)
442
- console.log(` import driftConfig from '../drift.config'`)
443
- console.log('')
444
- console.log(` ${pc.dim('// Last child in your root render:')}`)
445
- console.log(` {import.meta.env.DEV && <DriftOverlay config={driftConfig} />}`)
446
- console.log('')
447
442
  }
448
443
 
449
- if (dsPackages?.length) {
450
- console.log(`${pc.blue('Tip:')} Run ${pc.bold('npx catchdrift sync')} to auto-populate your component registry from ${dsPackages.join(', ')}.`)
451
- console.log('')
444
+ // ── Ongoing daily commands ───────────────────────────────────────────────────
445
+ console.log(`${pc.bold('Daily workflow (Claude Code):')}
446
+ ${pc.blue('/drift-context')} Check DS health — run this before starting work
447
+ ${pc.blue('/drift-scaffold')} Build a new screen using only DS components
448
+ ${pc.blue('/drift-sync')} Update registry after Figma or Storybook changes
449
+ ${pc.blue('npx catchdrift check')} Check coverage before submitting a PR
450
+ `)
451
+
452
+ if (!sources.includes('storybook') && !storybook.found) {
453
+ console.log(`${pc.yellow('Note:')} Storybook isn't set up yet. Run ${pc.bold('npx storybook@latest init')} when ready,`)
454
+ console.log(` then re-run ${pc.bold('npx catchdrift init')} to complete the coverage loop.\n`)
452
455
  }
453
456
 
454
- if (figmaFiles.length > 0) {
455
- console.log(`${pc.blue('Tip:')} Open the Drift overlay (press D), go to Settings, and paste your Figma personal access token to enable Figma component sync.`)
456
- console.log('')
457
+ if (!patchedShort) {
458
+ console.log(`${pc.yellow('Manual step needed')} add the overlay to your app entry point:`)
459
+ console.log(` import { DriftOverlay } from '@catchdrift/overlay'`)
460
+ console.log(` import driftConfig from './drift.config'`)
461
+ console.log(` // inside your root render, as the last child:`)
462
+ console.log(` {import.meta.env.DEV && <DriftOverlay config={driftConfig} />}\n`)
457
463
  }
464
+
465
+ console.log(pc.dim('Docs: https://catchdrift.ai · Issues: https://github.com/dyoon92/design-drift/issues'))
458
466
  }
459
467
 
460
468
  // ── Helpers ───────────────────────────────────────────────────────────────────