@catchdrift/cli 0.1.22 → 0.2.1

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.1",
4
4
  "description": "CLI for Drift — install, check, and manage design system coverage for any React app.",
5
5
  "keywords": [
6
6
  "design-system",
@@ -85,24 +85,70 @@ export async function init(argv) {
85
85
  }
86
86
  }
87
87
 
88
- // ── Step 2: Where does the DS live? ─────────────────────────────────────────
88
+ // ── Step 2: Do they have a DS at all? ────────────────────────────────────────
89
89
  console.log('')
90
- p.log.step('Where does your design system live? Select all that apply.')
91
- console.log('')
92
-
93
- const dsSources = await p.multiselect({
94
- message: 'DS source (you can pick more than one)',
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' },
99
- { value: 'manual', label: 'I\'ll type component names myself', hint: 'you can always run /drift-sync later' },
100
- ],
101
- required: false,
90
+ const hasDS = await p.confirm({
91
+ message: 'Do you already have a design system?',
92
+ initialValue: true,
102
93
  })
103
- if (p.isCancel(dsSources)) { p.cancel('Setup cancelled.'); process.exit(EXIT_CANCELED) }
94
+ if (p.isCancel(hasDS)) { p.cancel('Setup cancelled.'); process.exit(EXIT_CANCELED) }
95
+
96
+ let dsSources = []
97
+ if (hasDS) {
98
+ // ── Step 2b: Where does it live? ───────────────────────────────────────────
99
+ console.log('')
100
+ p.log.step('Where does your design system live? Select all that apply.')
101
+ console.log('')
102
+
103
+ const picked = await p.multiselect({
104
+ message: 'DS source (you can pick more than one)',
105
+ options: [
106
+ { value: 'figma', label: 'Figma', hint: 'components published in a Figma file' },
107
+ { value: 'storybook', label: 'Storybook', hint: 'stories at a URL' },
108
+ { value: 'package', label: 'npm package / path', hint: 'e.g. @acme/ui or ./src/components' },
109
+ { value: 'manual', label: 'Type names manually', hint: 'you can always run /drift-sync later' },
110
+ ],
111
+ required: false,
112
+ })
113
+ if (p.isCancel(picked)) { p.cancel('Setup cancelled.'); process.exit(EXIT_CANCELED) }
114
+ dsSources = Array.isArray(picked) ? picked : []
115
+ } else {
116
+ dsSources = ['fresh']
117
+ }
118
+
119
+ const sources = dsSources
120
+
121
+ // ── Step 2c: Bootstrap foundation picker ─────────────────────────────────────
122
+ // Fires when user has no existing DS
123
+ const DS_FOUNDATIONS = [
124
+ { 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' },
125
+ { 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/' },
126
+ { value: 'antd', label: 'Ant Design', hint: 'Enterprise-grade — rich component set', pkg: 'antd', install: 'npm install antd', docs: 'https://ant.design/components/overview/' },
127
+ { 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' },
128
+ { 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' },
129
+ { 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' },
130
+ ]
131
+
132
+ let chosenFoundation = null
133
+ if (sources.includes('fresh')) {
134
+ p.log.info(`No DS yet — let's pick a component foundation to track.`)
135
+ console.log(pc.dim(` Preview any of these at their docs before deciding:\n`))
136
+ DS_FOUNDATIONS.forEach(f => {
137
+ console.log(` ${pc.bold(f.label.padEnd(18))} ${pc.dim(f.hint)}`)
138
+ console.log(` ${pc.dim(' '.repeat(18) + f.docs)}\n`)
139
+ })
140
+
141
+ const foundationChoice = await p.select({
142
+ message: 'Which foundation will your team use?',
143
+ options: DS_FOUNDATIONS.map(f => ({ value: f.value, label: f.label, hint: f.hint })),
144
+ })
145
+ if (p.isCancel(foundationChoice)) { p.cancel('Setup cancelled.'); process.exit(EXIT_CANCELED) }
146
+ chosenFoundation = DS_FOUNDATIONS.find(f => f.value === foundationChoice)
104
147
 
105
- const sources = Array.isArray(dsSources) ? dsSources : []
148
+ p.log.success(`${chosenFoundation.label} selected Drift will track it via dsPackages.`)
149
+ p.log.info(`Install it now or later:\n ${pc.bold(chosenFoundation.install)}`)
150
+ p.log.info(`Docs + live preview: ${pc.cyan(chosenFoundation.docs)}`)
151
+ }
106
152
 
107
153
  // ── Storybook nudge — fire whenever Storybook isn't in the setup ─────────────
108
154
  const storybookNeeded = !sources.includes('storybook') && !storybook.found
@@ -329,7 +375,40 @@ export async function init(argv) {
329
375
  })
330
376
  if (p.isCancel(aiToolsSelected)) { p.cancel('Setup cancelled.'); process.exit(EXIT_CANCELED) }
331
377
 
378
+ // ── Step 5b: Jira integration (optional) ─────────────────────────────────────
379
+ let jiraBaseUrl, jiraProjectKey
380
+ const wantJira = await p.confirm({
381
+ message: 'Connect Jira? (lets the overlay create tickets directly — skip to add later)',
382
+ initialValue: false,
383
+ })
384
+ if (p.isCancel(wantJira)) { p.cancel('Setup cancelled.'); process.exit(EXIT_CANCELED) }
385
+
386
+ if (wantJira) {
387
+ const jiraUrl = await p.text({
388
+ message: 'Jira base URL',
389
+ placeholder: 'https://yourcompany.atlassian.net',
390
+ validate: v => (!v?.trim() ? 'Required' : undefined),
391
+ })
392
+ if (p.isCancel(jiraUrl)) { p.cancel('Setup cancelled.'); process.exit(EXIT_CANCELED) }
393
+ jiraBaseUrl = jiraUrl?.trim().replace(/\/$/, '')
394
+
395
+ const jiraKey = await p.text({
396
+ message: 'Jira project key',
397
+ placeholder: 'DS',
398
+ defaultValue: 'DS',
399
+ })
400
+ if (p.isCancel(jiraKey)) { p.cancel('Setup cancelled.'); process.exit(EXIT_CANCELED) }
401
+ jiraProjectKey = jiraKey?.trim() || 'DS'
402
+
403
+ p.log.info(`Jira credentials saved to drift.config.ts.\n Add your API token in the Drift overlay → Settings → Integrations.`)
404
+ }
405
+
332
406
  // ── Step 6: Write drift.config.ts ────────────────────────────────────────────
407
+ // If fresh/bootstrap, merge chosenFoundation's package into dsPackages
408
+ const mergedDsPackages = chosenFoundation
409
+ ? [chosenFoundation.pkg, ...(dsPackages ?? [])].filter(Boolean)
410
+ : dsPackages
411
+
333
412
  const components = { ...sbComponents }
334
413
 
335
414
  spinner.start('Writing drift.config.ts...')
@@ -337,10 +416,12 @@ export async function init(argv) {
337
416
  storybookUrl: storybookUrl || (storybook.found ? storybook.url : undefined),
338
417
  chromaticUrl,
339
418
  figmaFiles: figmaFiles.length ? figmaFiles : undefined,
340
- dsPackages,
419
+ dsPackages: mergedDsPackages,
341
420
  threshold: Number(threshold) || 80,
342
421
  components,
343
422
  framework,
423
+ jiraBaseUrl,
424
+ jiraProjectKey,
344
425
  })
345
426
  if (figmaToken) writeEnvLocal(cwd, { figmaToken })
346
427
  spinner.stop('drift.config.ts written')
@@ -412,7 +493,20 @@ ${pc.bold('What was set up:')}
412
493
  // ── Immediate next step — specific to what was configured ───────────────────
413
494
  console.log(`\n${pc.bold('Do this now:')}`)
414
495
 
415
- if (figmaFiles.length > 0 && skillFiles.length > 0) {
496
+ if (chosenFoundation) {
497
+ console.log(`
498
+ You chose ${pc.bold(chosenFoundation.label)} as your foundation.
499
+
500
+ ${pc.cyan('1.')} Install it:
501
+ ${pc.bold(chosenFoundation.install)}
502
+ ${pc.cyan('2.')} Run: ${pc.bold('npx catchdrift sync')}
503
+ Scans your codebase for imports from ${pc.bold(chosenFoundation.pkg)} and registers them.
504
+ ${pc.cyan('3.')} Run: ${pc.bold('npm run dev')} → press ${pc.bold('D')} → see live coverage
505
+
506
+ ${pc.dim('Docs + live preview: ' + chosenFoundation.docs)}
507
+ ${pc.dim('Figma community library: search "' + chosenFoundation.label + '" at figma.com/community')}
508
+ `)
509
+ } else if (figmaFiles.length > 0 && skillFiles.length > 0) {
416
510
  console.log(`
417
511
  Your components are in Figma. Pull them into the registry now:
418
512
 
@@ -428,10 +522,10 @@ ${pc.bold('What was set up:')}
428
522
  ${pc.cyan('1.')} Run: ${pc.bold('npm run dev')}
429
523
  ${pc.cyan('2.')} Press ${pc.bold('D')} to open the Drift overlay — you should see real coverage.
430
524
  `)
431
- } else if (dsPackages?.length) {
525
+ } else if (mergedDsPackages?.length) {
432
526
  console.log(`
433
527
  ${pc.cyan('1.')} Run: ${pc.bold('npx catchdrift sync')}
434
- Scans your codebase for imports from ${dsPackages.join(', ')} and registers them.
528
+ Scans your codebase for imports from ${mergedDsPackages.join(', ')} and registers them.
435
529
  ${pc.cyan('2.')} Run: ${pc.bold('npm run dev')} → press ${pc.bold('D')} → see live coverage
436
530
  `)
437
531
  } else {
@@ -444,6 +538,11 @@ ${pc.bold('What was set up:')}
444
538
  `)
445
539
  }
446
540
 
541
+ if (jiraBaseUrl) {
542
+ console.log(` ${pc.dim('Jira:')} Connected to ${pc.bold(jiraBaseUrl)} (project: ${pc.bold(jiraProjectKey)})`)
543
+ console.log(` ${pc.dim(' → Add your API token in the overlay → Settings → Integrations')}\n`)
544
+ }
545
+
447
546
  // ── Ongoing daily commands ───────────────────────────────────────────────────
448
547
  console.log(`${pc.bold('Daily workflow (Claude Code):')}
449
548
  ${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.1'
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: {`,