@catchdrift/cli 0.1.22 → 0.2.0

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.22",
3
+ "version": "0.2.0",
4
4
  "description": "CLI for Drift — install, check, and manage design system coverage for any React app.",
5
5
  "keywords": [
6
6
  "design-system",
@@ -93,9 +93,10 @@ export async function init(argv) {
93
93
  const dsSources = await p.multiselect({
94
94
  message: 'DS source (you can pick more than one)',
95
95
  options: [
96
- { value: 'figma', label: 'Figma', hint: 'components published in a Figma file' },
97
- { value: 'storybook', label: 'Storybook', hint: 'stories at a URL' },
98
- { value: 'package', label: 'npm package / path', hint: 'e.g. @acme/ui or ./src/components' },
96
+ { value: 'figma', label: 'Figma', hint: 'components published in a Figma file' },
97
+ { value: 'storybook', label: 'Storybook', hint: 'stories at a URL' },
98
+ { value: 'package', label: 'npm package / path', hint: 'e.g. @acme/ui or ./src/components' },
99
+ { value: 'fresh', label: 'No DS yet — bootstrap from scratch', hint: 'pick Material UI, shadcn, Ant Design, etc.' },
99
100
  { value: 'manual', label: 'I\'ll type component names myself', hint: 'you can always run /drift-sync later' },
100
101
  ],
101
102
  required: false,
@@ -104,6 +105,38 @@ export async function init(argv) {
104
105
 
105
106
  const sources = Array.isArray(dsSources) ? dsSources : []
106
107
 
108
+ // ── Step 2b: Bootstrap foundation picker ─────────────────────────────────────
109
+ // Fires when user picks "No DS yet — bootstrap from scratch"
110
+ const DS_FOUNDATIONS = [
111
+ { value: 'shadcn', label: 'shadcn/ui', hint: 'Radix + Tailwind — copy-paste components', pkg: '@radix-ui/react-dialog', install: 'npx shadcn@latest init', docs: 'https://ui.shadcn.com' },
112
+ { value: 'mui', label: 'Material UI', hint: 'Google Material Design — battle-tested ecosystem', pkg: '@mui/material', install: 'npm install @mui/material @emotion/react @emotion/styled', docs: 'https://mui.com/material-ui/' },
113
+ { value: 'antd', label: 'Ant Design', hint: 'Enterprise-grade — rich component set', pkg: 'antd', install: 'npm install antd', docs: 'https://ant.design/components/overview/' },
114
+ { value: 'chakra', label: 'Chakra UI', hint: 'Accessible, composable — great DX', pkg: '@chakra-ui/react', install: 'npm install @chakra-ui/react @emotion/react @emotion/styled framer-motion', docs: 'https://chakra-ui.com/docs/components' },
115
+ { value: 'radix', label: 'Radix Themes', hint: 'Headless primitives + opinionated theme layer', pkg: '@radix-ui/themes', install: 'npm install @radix-ui/themes', docs: 'https://www.radix-ui.com/themes' },
116
+ { value: 'primer', label: 'Primer (GitHub)', hint: "GitHub's DS — clean, minimal, production-grade", pkg: '@primer/react', install: 'npm install @primer/react styled-components', docs: 'https://primer.style/components' },
117
+ ]
118
+
119
+ let chosenFoundation = null
120
+ if (sources.includes('fresh')) {
121
+ p.log.info(`No DS yet — let's pick a component foundation to track.`)
122
+ console.log(pc.dim(` Preview any of these at their docs before deciding:\n`))
123
+ DS_FOUNDATIONS.forEach(f => {
124
+ console.log(` ${pc.bold(f.label.padEnd(18))} ${pc.dim(f.hint)}`)
125
+ console.log(` ${pc.dim(' '.repeat(18) + f.docs)}\n`)
126
+ })
127
+
128
+ const foundationChoice = await p.select({
129
+ message: 'Which foundation will your team use?',
130
+ options: DS_FOUNDATIONS.map(f => ({ value: f.value, label: f.label, hint: f.hint })),
131
+ })
132
+ if (p.isCancel(foundationChoice)) { p.cancel('Setup cancelled.'); process.exit(EXIT_CANCELED) }
133
+ chosenFoundation = DS_FOUNDATIONS.find(f => f.value === foundationChoice)
134
+
135
+ p.log.success(`${chosenFoundation.label} selected — Drift will track it via dsPackages.`)
136
+ p.log.info(`Install it now or later:\n ${pc.bold(chosenFoundation.install)}`)
137
+ p.log.info(`Docs + live preview: ${pc.cyan(chosenFoundation.docs)}`)
138
+ }
139
+
107
140
  // ── Storybook nudge — fire whenever Storybook isn't in the setup ─────────────
108
141
  const storybookNeeded = !sources.includes('storybook') && !storybook.found
109
142
  if (storybookNeeded && sources.length > 0) {
@@ -329,7 +362,40 @@ export async function init(argv) {
329
362
  })
330
363
  if (p.isCancel(aiToolsSelected)) { p.cancel('Setup cancelled.'); process.exit(EXIT_CANCELED) }
331
364
 
365
+ // ── Step 5b: Jira integration (optional) ─────────────────────────────────────
366
+ let jiraBaseUrl, jiraProjectKey
367
+ const wantJira = await p.confirm({
368
+ message: 'Connect Jira? (lets the overlay create tickets directly — skip to add later)',
369
+ initialValue: false,
370
+ })
371
+ if (p.isCancel(wantJira)) { p.cancel('Setup cancelled.'); process.exit(EXIT_CANCELED) }
372
+
373
+ if (wantJira) {
374
+ const jiraUrl = await p.text({
375
+ message: 'Jira base URL',
376
+ placeholder: 'https://yourcompany.atlassian.net',
377
+ validate: v => (!v?.trim() ? 'Required' : undefined),
378
+ })
379
+ if (p.isCancel(jiraUrl)) { p.cancel('Setup cancelled.'); process.exit(EXIT_CANCELED) }
380
+ jiraBaseUrl = jiraUrl?.trim().replace(/\/$/, '')
381
+
382
+ const jiraKey = await p.text({
383
+ message: 'Jira project key',
384
+ placeholder: 'DS',
385
+ defaultValue: 'DS',
386
+ })
387
+ if (p.isCancel(jiraKey)) { p.cancel('Setup cancelled.'); process.exit(EXIT_CANCELED) }
388
+ jiraProjectKey = jiraKey?.trim() || 'DS'
389
+
390
+ p.log.info(`Jira credentials saved to drift.config.ts.\n Add your API token in the Drift overlay → Settings → Integrations.`)
391
+ }
392
+
332
393
  // ── Step 6: Write drift.config.ts ────────────────────────────────────────────
394
+ // If fresh/bootstrap, merge chosenFoundation's package into dsPackages
395
+ const mergedDsPackages = chosenFoundation
396
+ ? [chosenFoundation.pkg, ...(dsPackages ?? [])].filter(Boolean)
397
+ : dsPackages
398
+
333
399
  const components = { ...sbComponents }
334
400
 
335
401
  spinner.start('Writing drift.config.ts...')
@@ -337,10 +403,12 @@ export async function init(argv) {
337
403
  storybookUrl: storybookUrl || (storybook.found ? storybook.url : undefined),
338
404
  chromaticUrl,
339
405
  figmaFiles: figmaFiles.length ? figmaFiles : undefined,
340
- dsPackages,
406
+ dsPackages: mergedDsPackages,
341
407
  threshold: Number(threshold) || 80,
342
408
  components,
343
409
  framework,
410
+ jiraBaseUrl,
411
+ jiraProjectKey,
344
412
  })
345
413
  if (figmaToken) writeEnvLocal(cwd, { figmaToken })
346
414
  spinner.stop('drift.config.ts written')
@@ -412,7 +480,20 @@ ${pc.bold('What was set up:')}
412
480
  // ── Immediate next step — specific to what was configured ───────────────────
413
481
  console.log(`\n${pc.bold('Do this now:')}`)
414
482
 
415
- if (figmaFiles.length > 0 && skillFiles.length > 0) {
483
+ if (chosenFoundation) {
484
+ console.log(`
485
+ You chose ${pc.bold(chosenFoundation.label)} as your foundation.
486
+
487
+ ${pc.cyan('1.')} Install it:
488
+ ${pc.bold(chosenFoundation.install)}
489
+ ${pc.cyan('2.')} Run: ${pc.bold('npx catchdrift sync')}
490
+ Scans your codebase for imports from ${pc.bold(chosenFoundation.pkg)} and registers them.
491
+ ${pc.cyan('3.')} Run: ${pc.bold('npm run dev')} → press ${pc.bold('D')} → see live coverage
492
+
493
+ ${pc.dim('Docs + live preview: ' + chosenFoundation.docs)}
494
+ ${pc.dim('Figma community library: search "' + chosenFoundation.label + '" at figma.com/community')}
495
+ `)
496
+ } else if (figmaFiles.length > 0 && skillFiles.length > 0) {
416
497
  console.log(`
417
498
  Your components are in Figma. Pull them into the registry now:
418
499
 
@@ -428,10 +509,10 @@ ${pc.bold('What was set up:')}
428
509
  ${pc.cyan('1.')} Run: ${pc.bold('npm run dev')}
429
510
  ${pc.cyan('2.')} Press ${pc.bold('D')} to open the Drift overlay — you should see real coverage.
430
511
  `)
431
- } else if (dsPackages?.length) {
512
+ } else if (mergedDsPackages?.length) {
432
513
  console.log(`
433
514
  ${pc.cyan('1.')} Run: ${pc.bold('npx catchdrift sync')}
434
- Scans your codebase for imports from ${dsPackages.join(', ')} and registers them.
515
+ Scans your codebase for imports from ${mergedDsPackages.join(', ')} and registers them.
435
516
  ${pc.cyan('2.')} Run: ${pc.bold('npm run dev')} → press ${pc.bold('D')} → see live coverage
436
517
  `)
437
518
  } else {
@@ -444,6 +525,11 @@ ${pc.bold('What was set up:')}
444
525
  `)
445
526
  }
446
527
 
528
+ if (jiraBaseUrl) {
529
+ console.log(` ${pc.dim('Jira:')} Connected to ${pc.bold(jiraBaseUrl)} (project: ${pc.bold(jiraProjectKey)})`)
530
+ console.log(` ${pc.dim(' → Add your API token in the overlay → Settings → Integrations')}\n`)
531
+ }
532
+
447
533
  // ── Ongoing daily commands ───────────────────────────────────────────────────
448
534
  console.log(`${pc.bold('Daily workflow (Claude Code):')}
449
535
  ${pc.blue('/drift-context')} Check DS health — run this before starting work
package/src/index.mjs CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  import pc from 'picocolors'
7
7
 
8
- const VERSION = '0.1.0'
8
+ const VERSION = '0.2.0'
9
9
 
10
10
  const HELP = `
11
11
  ${pc.bold(pc.blue('catchdrift'))} — Design system compliance for teams shipping with AI
@@ -13,7 +13,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url))
13
13
 
14
14
  // ── drift.config.ts ───────────────────────────────────────────────────────────
15
15
 
16
- export function writeDriftConfig(cwd, { storybookUrl, chromaticUrl, figmaFiles, dsPackages, threshold, components, framework }) {
16
+ export function writeDriftConfig(cwd, { storybookUrl, chromaticUrl, figmaFiles, dsPackages, threshold, components, framework, jiraBaseUrl, jiraProjectKey }) {
17
17
  const registry = buildComponentRegistry(components)
18
18
 
19
19
  const dsPackagesLine = dsPackages?.length
@@ -57,6 +57,8 @@ export function writeDriftConfig(cwd, { storybookUrl, chromaticUrl, figmaFiles,
57
57
  chromaticUrl ? ` chromaticUrl: '${chromaticUrl}',` : null,
58
58
  figmaFilesBlock,
59
59
  figmaTokenLine,
60
+ jiraBaseUrl ? ` jiraBaseUrl: '${jiraBaseUrl}',` : null,
61
+ jiraProjectKey ? ` jiraProjectKey: '${jiraProjectKey}',` : null,
60
62
  ` threshold: ${threshold},`,
61
63
  dsPackagesLine,
62
64
  ` components: {`,