@catchdrift/cli 0.1.7 → 0.1.8

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@catchdrift/cli",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "CLI for Drift — install, check, and manage design system coverage for any React app.",
5
5
  "keywords": [
6
6
  "design-system",
@@ -132,27 +132,51 @@ export async function init(argv) {
132
132
  // ── Step 3a: Figma ───────────────────────────────────────────────────────────
133
133
  let figmaFileKey, figmaToken, figmaWIPPages
134
134
  if (sources.includes('figma')) {
135
- figmaFileKey = await p.text({
136
- message: 'Figma file key',
137
- placeholder: 'Found in figma.com/design/THIS_KEY/... (paste just the key)',
138
- validate: v => (!v?.trim() ? 'Required — paste the key from your Figma URL' : undefined),
135
+ // Accept full URL or raw key
136
+ const figmaInput = await p.text({
137
+ message: 'Paste your Figma file URL (or just the file key)',
138
+ placeholder: 'https://www.figma.com/design/ABC123.../My-Design-File',
139
+ hint: 'Open your Figma file in a browser and copy the full URL from the address bar',
140
+ validate: v => (!v?.trim() ? 'Required' : undefined),
139
141
  })
140
- if (p.isCancel(figmaFileKey)) { p.cancel('Setup cancelled.'); process.exit(EXIT_CANCELED) }
141
- figmaFileKey = figmaFileKey?.trim() || undefined
142
+ if (p.isCancel(figmaInput)) { p.cancel('Setup cancelled.'); process.exit(EXIT_CANCELED) }
143
+ figmaFileKey = extractFigmaFileKey(figmaInput.trim())
144
+ if (!figmaFileKey) {
145
+ p.log.warn(`Could not extract a file key from "${figmaInput.trim()}". Using it as-is.`)
146
+ figmaFileKey = figmaInput.trim()
147
+ }
148
+
149
+ // Token — show exact steps + required scopes
150
+ console.log('')
151
+ p.log.step('Create a Figma access token — takes about 60 seconds:')
152
+ console.log(`
153
+ 1. Open ${pc.cyan('figma.com')} → click your avatar (top-left) → ${pc.bold('Settings')}
154
+ 2. Go to the ${pc.bold('Security')} tab → click ${pc.bold('Generate new token')}
155
+ 3. Give it a name (e.g. "Drift") and set an expiry
156
+ 4. Enable these scopes:
157
+ ${pc.green('✓')} File content → ${pc.bold('Read only')}
158
+ ${pc.green('✓')} File comments → ${pc.bold('Write')}
159
+ ${pc.green('✓')} File variables → ${pc.bold('Read only')}
160
+ 5. Click ${pc.bold('Generate token')} and copy it — ${pc.yellow("you won't be able to see it again")}
161
+ `)
142
162
 
143
163
  figmaToken = await p.text({
144
- message: 'Figma personal access token',
145
- placeholder: 'figd_... (figma.com → Profile → Settings → Security → Personal access tokens)',
146
- hint: 'Used to fetch your real page list. Store in FIGMA_API_TOKEN env var — not committed to git.',
164
+ message: 'Paste your Figma token here',
165
+ placeholder: 'figd_...',
166
+ hint: 'Stored in your local FIGMA_API_TOKEN env var — never committed to git',
167
+ validate: v => (!v?.trim() ? 'Required — paste the token you just generated' : undefined),
147
168
  })
148
169
  if (p.isCancel(figmaToken)) { p.cancel('Setup cancelled.'); process.exit(EXIT_CANCELED) }
149
170
  figmaToken = figmaToken?.trim() || undefined
150
171
 
151
- // Fetch actual pages from Figma so the user picks from real names
172
+ // Validate token + fetch pages in one call
152
173
  if (figmaToken && figmaFileKey) {
153
- spinner.start('Fetching pages from Figma...')
174
+ spinner.start('Connecting to Figma...')
154
175
  const pages = await fetchFigmaPages(figmaFileKey, figmaToken)
155
- spinner.stop(pages ? `Found ${pages.length} pages` : 'Could not reach Figma — skipping page selection')
176
+ spinner.stop(pages
177
+ ? pc.green(`Connected ✓ Found ${pages.length} pages`)
178
+ : pc.yellow('Could not reach Figma — check your token scopes. You can re-run `npx catchdrift init` to retry.')
179
+ )
156
180
 
157
181
  if (pages?.length) {
158
182
  const selected = await p.multiselect({
@@ -339,6 +363,12 @@ ${pc.dim('Docs: https://catchdrift.ai · Issues: https://github.com/dyoon92/de
339
363
 
340
364
  // ── Helpers ───────────────────────────────────────────────────────────────────
341
365
 
366
+ function extractFigmaFileKey(input) {
367
+ // Matches: figma.com/design/KEY/... or figma.com/file/KEY/...
368
+ const match = input.match(/figma\.com\/(?:design|file)\/([a-zA-Z0-9]+)/)
369
+ return match ? match[1] : null
370
+ }
371
+
342
372
  async function fetchFigmaPages(fileKey, token) {
343
373
  try {
344
374
  const res = await fetch(`https://api.figma.com/v1/files/${fileKey}?depth=1`, {