@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 +1 -1
- package/src/commands/init.mjs +93 -7
- package/src/index.mjs +1 -1
- package/src/lib/writers.mjs +3 -1
package/package.json
CHANGED
package/src/commands/init.mjs
CHANGED
|
@@ -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',
|
|
97
|
-
{ value: 'storybook', label: 'Storybook',
|
|
98
|
-
{ value: 'package', label: 'npm package / path',
|
|
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 (
|
|
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 (
|
|
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 ${
|
|
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
package/src/lib/writers.mjs
CHANGED
|
@@ -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: {`,
|