From 5a5b5d566d8873770c7eea8927344832c664cfb9 Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Wed, 11 Nov 2026 17:03:21 -0300 Subject: [PATCH] feat(cli): optimize init command for LLM and non-interactive workflows --- exec/cli.js | 41 +++---- public/llms.txt | 2 +- scripts/cli.ts | 10 +- scripts/commands.ts | 229 ++++++++++++++++++++++++++++++-------- scripts/generate-llms.ts | 2 +- scripts/tests/cli.test.ts | 96 ++++++++++++++++ scripts/utils.ts | 32 ++++-- src/pages/cli.mdx | 9 +- 8 files changed, 342 insertions(+), 79 deletions(-) diff --git a/exec/cli.js b/exec/cli.js index 621c005a..95d3b40e 100755 --- a/exec/cli.js +++ b/exec/cli.js @@ -1,31 +1,32 @@ #!/usr/bin/env node -import{Command as we}from"commander";import u from"fs-extra";import h from"path";import f from"chalk";import U from"ora";import{fileURLToPath as ce}from"url";import{execSync as de}from"child_process";import le from"enquirer";var x={accordion:{name:"Accordion",files:["src/dashkit/Accordion/Accordion.tsx","src/dashkit/Accordion/AccordionItem.tsx","src/dashkit/Accordion/AccordionTrigger.tsx","src/dashkit/Accordion/AccordionContent.tsx","src/dashkit/Accordion/AccordionContext.ts","src/dashkit/Accordion/Accordion.test.tsx","src/dashkit/Accordion/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","react-icons"]},avatar:{name:"Avatar",files:["src/dashkit/Avatar/Avatar.tsx","src/dashkit/Avatar/AvatarGroup.tsx","src/dashkit/Avatar/Avatar.test.tsx","src/dashkit/Avatar/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","class-variance-authority"]},badge:{name:"Badge",files:["src/dashkit/Badge/Badge.tsx","src/dashkit/Badge/FloatBadge.tsx","src/dashkit/Badge/Badge.test.tsx","src/dashkit/Badge/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","class-variance-authority"]},floatbadge:{name:"FloatBadge",files:["src/dashkit/Badge/FloatBadge.tsx"],dependencies:["clsx","tailwind-merge"],registryDependencies:["badge"]},button:{name:"Button",files:["src/dashkit/Button/Button.tsx","src/dashkit/Button/Button.test.tsx","src/dashkit/Button/index.ts"],registryDependencies:["spinner"],dependencies:["clsx","tailwind-merge","class-variance-authority"]},card:{name:"Card",files:["src/dashkit/Card/Card.tsx","src/dashkit/Card/CardHeader.tsx","src/dashkit/Card/CardContent.tsx","src/dashkit/Card/CardFooter.tsx","src/dashkit/Card/Card.test.tsx","src/dashkit/Card/index.ts"],registryDependencies:["badge","button"],dependencies:["framer-motion","clsx","tailwind-merge","class-variance-authority"]},chip:{name:"Chip",files:["src/dashkit/Chip/Chip.tsx","src/dashkit/Chip/Chip.test.tsx","src/dashkit/Chip/index.ts"],dependencies:["clsx","tailwind-merge","class-variance-authority","react-icons"]},checkbox:{name:"Checkbox",files:["src/dashkit/Checkbox/Checkbox.tsx","src/dashkit/Checkbox/Checkbox.test.tsx","src/dashkit/Checkbox/index.ts"],dependencies:["clsx","tailwind-merge"]},drawer:{name:"Drawer",files:["src/dashkit/Drawer/Drawer.tsx","src/dashkit/Drawer/useDrawer.ts","src/dashkit/Drawer/Drawer.test.tsx","src/dashkit/Drawer/index.ts"],registryDependencies:["backdrop"],dependencies:["framer-motion","clsx","tailwind-merge","class-variance-authority","react-icons"]},dropdown:{name:"Dropdown",files:["src/dashkit/Dropdown/Dropdown.tsx","src/dashkit/Dropdown/DropdownContent.tsx","src/dashkit/Dropdown/DropdownItem.tsx","src/dashkit/Dropdown/DropdownLabel.tsx","src/dashkit/Dropdown/DropdownSeparator.tsx","src/dashkit/Dropdown/DropdownTrigger.tsx","src/dashkit/Dropdown/useDropdown.ts","src/dashkit/Dropdown/Dropdown.test.tsx","src/dashkit/Dropdown/index.ts"],dependencies:["clsx","tailwind-merge","react-icons"]},imageexpander:{name:"ImageExpander",files:["src/dashkit/ImageExpander/ImageExpander.tsx","src/dashkit/ImageExpander/useImageExpander.ts","src/dashkit/ImageExpander/ImageExpander.test.tsx","src/dashkit/ImageExpander/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","react-icons"]},input:{name:"Input",files:["src/dashkit/Input/Input.tsx","src/dashkit/Input/Input.test.tsx","src/dashkit/Input/index.ts"],dependencies:["clsx","tailwind-merge","class-variance-authority"]},modal:{name:"Modal",files:["src/dashkit/Modal/Modal.tsx","src/dashkit/Modal/ModalContent.tsx","src/dashkit/Modal/ModalFooter.tsx","src/dashkit/Modal/ModalHeader.tsx","src/dashkit/Modal/useModal.ts","src/dashkit/Modal/Modal.test.tsx","src/dashkit/Modal/index.ts"],registryDependencies:["backdrop","iconbutton"],dependencies:["framer-motion","clsx","tailwind-merge","class-variance-authority","react-icons"]},otpinput:{name:"OtpInput",files:["src/dashkit/OtpInput/OtpInput.tsx","src/dashkit/OtpInput/useOtpInput.ts","src/dashkit/OtpInput/OtpInput.test.tsx","src/dashkit/OtpInput/index.ts"],dependencies:["clsx","tailwind-merge"]},radio:{name:"Radio",files:["src/dashkit/Radio/Radio.tsx","src/dashkit/Radio/useRadio.ts","src/dashkit/Radio/Radio.test.tsx","src/dashkit/Radio/index.ts"],dependencies:["clsx","tailwind-merge"]},select:{name:"Select",files:["src/dashkit/Select/Select.tsx","src/dashkit/Select/useSelect.ts","src/dashkit/Select/Select.test.tsx","src/dashkit/Select/index.ts"],dependencies:["clsx","tailwind-merge","class-variance-authority","react-icons"]},switch:{name:"Switch",files:["src/dashkit/Switch/Switch.tsx","src/dashkit/Switch/useSwitch.ts","src/dashkit/Switch/Switch.test.tsx","src/dashkit/Switch/index.ts"],dependencies:["clsx","tailwind-merge"]},slider:{name:"Slider",files:["src/dashkit/Slider/Slider.tsx","src/dashkit/Slider/useSlider.ts","src/dashkit/Slider/Slider.test.tsx","src/dashkit/Slider/index.ts"],dependencies:["clsx","tailwind-merge"]},surface:{name:"Surface",files:["src/dashkit/Surface/Surface.tsx","src/dashkit/Surface/Surface.test.tsx","src/dashkit/Surface/index.ts"],dependencies:["clsx","tailwind-merge","class-variance-authority"]},tabs:{name:"Tabs",files:["src/dashkit/Tabs/Tabs.tsx","src/dashkit/Tabs/Tabs.test.tsx","src/dashkit/Tabs/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","class-variance-authority"]},toast:{name:"Toast",files:["src/dashkit/Toast/Toast.tsx","src/dashkit/Toast/useToast.ts","src/dashkit/Toast/Toast.test.tsx","src/dashkit/Toast/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","react-icons"]},stepper:{name:"Stepper",files:["src/dashkit/Stepper/Stepper.tsx","src/dashkit/Stepper/Stepper.test.tsx","src/dashkit/Stepper/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","react-icons"]},pagination:{name:"Pagination",files:["src/dashkit/Pagination/Pagination.tsx","src/dashkit/Pagination/Pagination.test.tsx","src/dashkit/Pagination/index.ts"],dependencies:["clsx","tailwind-merge","react-icons","framer-motion"]},spinner:{name:"Spinner",files:["src/dashkit/Spinner/Spinner.tsx","src/dashkit/Spinner/Spinner.test.tsx","src/dashkit/Spinner/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge"]},navbar:{name:"Navbar",files:["src/dashkit/Navbar/Navbar.tsx","src/dashkit/Navbar/NavbarActions.tsx","src/dashkit/Navbar/NavbarBrand.tsx","src/dashkit/Navbar/NavbarLinks.tsx","src/dashkit/Navbar/Navbar.test.tsx","src/dashkit/Navbar/index.ts"],dependencies:["clsx","tailwind-merge","class-variance-authority"]},skeleton:{name:"Skeleton",files:["src/dashkit/Skeleton/Skeleton.tsx","src/dashkit/Skeleton/Skeleton.test.tsx","src/dashkit/Skeleton/index.ts"],dependencies:["clsx","tailwind-merge","class-variance-authority"]},breadcrumb:{name:"Breadcrumb",files:["src/dashkit/Breadcrumb/Breadcrumb.tsx","src/dashkit/Breadcrumb/BreadcrumbList.tsx","src/dashkit/Breadcrumb/BreadcrumbItem.tsx","src/dashkit/Breadcrumb/BreadcrumbSeparator.tsx","src/dashkit/Breadcrumb/BreadcrumbEllipsis.tsx","src/dashkit/Breadcrumb/Breadcrumb.test.tsx","src/dashkit/Breadcrumb/index.ts"],dependencies:["clsx","tailwind-merge","react-icons"]},iconbutton:{name:"IconButton",files:["src/dashkit/IconButton/IconButton.tsx","src/dashkit/IconButton/IconButton.test.tsx","src/dashkit/IconButton/index.ts"],dependencies:["clsx","tailwind-merge","class-variance-authority"]},areachart:{name:"AreaChart",files:["src/dashkit/AreaChart/AreaChart.tsx","src/dashkit/AreaChart/useAreaChart.ts","src/dashkit/AreaChart/AreaChart.test.tsx","src/dashkit/AreaChart/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge"]},barchart:{name:"BarChart",files:["src/dashkit/BarChart/BarChart.tsx","src/dashkit/BarChart/useBarChart.ts","src/dashkit/BarChart/BarChart.test.tsx","src/dashkit/BarChart/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge"]},linechart:{name:"LineChart",files:["src/dashkit/LineChart/LineChart.tsx","src/dashkit/LineChart/useLineChart.ts","src/dashkit/LineChart/LineChart.test.tsx","src/dashkit/LineChart/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge"]},piechart:{name:"PieChart",files:["src/dashkit/PieChart/PieChart.tsx","src/dashkit/PieChart/usePieChart.ts","src/dashkit/PieChart/PieChart.test.tsx","src/dashkit/PieChart/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge"]},progressbar:{name:"ProgressBar",files:["src/dashkit/ProgressBar/ProgressBar.tsx","src/dashkit/ProgressBar/useProgressBar.ts","src/dashkit/ProgressBar/ProgressBar.test.tsx","src/dashkit/ProgressBar/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","class-variance-authority"]},buttongroup:{name:"ButtonGroup",files:["src/dashkit/ButtonGroup/ButtonGroup.tsx","src/dashkit/ButtonGroup/ButtonGroup.test.tsx","src/dashkit/ButtonGroup/index.ts"],dependencies:["clsx","tailwind-merge","class-variance-authority"]},combobox:{name:"Combobox",files:["src/dashkit/Combobox/Combobox.tsx","src/dashkit/Combobox/useCombobox.ts","src/dashkit/Combobox/Combobox.test.tsx","src/dashkit/Combobox/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","react-icons"],registryDependencies:["chip"]},divider:{name:"Divider",files:["src/dashkit/Divider/Divider.tsx","src/dashkit/Divider/Divider.test.tsx","src/dashkit/Divider/index.ts"],dependencies:["clsx","tailwind-merge","class-variance-authority"]},sidebar:{name:"Sidebar",files:["src/dashkit/Sidebar/Sidebar.tsx","src/dashkit/Sidebar/useSidebar.ts","src/dashkit/Sidebar/SidebarContext.tsx","src/dashkit/Sidebar/Sidebar.test.tsx","src/dashkit/Sidebar/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","react-icons"],registryDependencies:["iconbutton","tooltip","backdrop"]},"float-action-menu":{name:"FloatActionMenu",files:["src/dashkit/FloatActionMenu/FloatActionMenu.tsx","src/dashkit/FloatActionMenu/useFloatActionMenu.ts","src/dashkit/FloatActionMenu/FloatActionMenu.test.tsx","src/dashkit/FloatActionMenu/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","react-icons"],registryDependencies:["iconbutton"]},textarea:{name:"Textarea",files:["src/dashkit/Textarea/Textarea.tsx","src/dashkit/Textarea/useTextarea.ts","src/dashkit/Textarea/Textarea.test.tsx","src/dashkit/Textarea/index.ts"],dependencies:["clsx","tailwind-merge"]},datepicker:{name:"DatePicker",files:["src/dashkit/DatePicker/DatePicker.tsx","src/dashkit/DatePicker/useDatePicker.ts","src/dashkit/DatePicker/DatePicker.test.tsx","src/dashkit/DatePicker/index.ts"],dependencies:["clsx","tailwind-merge","react-icons"]},table:{name:"Table",files:["src/dashkit/Table/Table.tsx","src/dashkit/Table/Table.test.tsx","src/dashkit/Table/index.ts"],dependencies:["clsx","tailwind-merge"]},animatenumber:{name:"AnimateNumber",files:["src/dashkit/AnimateNumber/AnimateNumber.tsx","src/dashkit/AnimateNumber/Digit.tsx","src/dashkit/AnimateNumber/AnimateNumber.test.tsx","src/dashkit/AnimateNumber/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge"]},circularprogress:{name:"CircularProgress",files:["src/dashkit/CircularProgress/CircularProgress.tsx","src/dashkit/CircularProgress/CircularProgress.test.tsx","src/dashkit/CircularProgress/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge"]},dock:{name:"Dock",files:["src/dashkit/Dock/Dock.tsx","src/dashkit/Dock/DockItem.tsx","src/dashkit/Dock/Dock.test.tsx","src/dashkit/Dock/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge"]},popover:{name:"Popover",files:["src/dashkit/Popover/Popover.tsx","src/dashkit/Popover/PopoverContent.tsx","src/dashkit/Popover/PopoverTrigger.tsx","src/dashkit/Popover/usePopover.ts","src/dashkit/Popover/Popover.test.tsx","src/dashkit/Popover/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge"]},tooltip:{name:"Tooltip",files:["src/dashkit/Tooltip/Tooltip.tsx","src/dashkit/Tooltip/Tooltip.test.tsx","src/dashkit/Tooltip/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge"]},systemlogs:{name:"SystemLogs",files:["src/dashkit/SystemLogs/SystemLogs.tsx","src/dashkit/SystemLogs/SystemLogs.test.tsx","src/dashkit/SystemLogs/index.ts"],dependencies:["clsx","tailwind-merge","react-icons"],registryDependencies:["badge"]},datefield:{name:"DateField",files:["src/dashkit/DateField/DateField.tsx","src/dashkit/DateField/useDateField.ts","src/dashkit/DateField/DateField.test.tsx","src/dashkit/DateField/index.ts"],dependencies:["clsx","tailwind-merge"]},"navigation-menu":{name:"NavigationMenu",files:["src/dashkit/NavigationMenu/NavigationMenu.tsx","src/dashkit/NavigationMenu/NavigationMenuContent.tsx","src/dashkit/NavigationMenu/NavigationMenuContext.tsx","src/dashkit/NavigationMenu/NavigationMenuItem.tsx","src/dashkit/NavigationMenu/NavigationMenuLink.tsx","src/dashkit/NavigationMenu/NavigationMenuList.tsx","src/dashkit/NavigationMenu/NavigationMenuTrigger.tsx","src/dashkit/NavigationMenu/NavigationMenuViewport.tsx","src/dashkit/NavigationMenu/useNavigationMenu.ts","src/dashkit/NavigationMenu/useNavigationMenuViewport.ts","src/dashkit/NavigationMenu/NavigationMenu.test.tsx","src/dashkit/NavigationMenu/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","react-icons"]},backdrop:{name:"Backdrop",files:["src/dashkit/Backdrop/Backdrop.tsx","src/dashkit/Backdrop/Backdrop.test.tsx","src/dashkit/Backdrop/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge"]},scrollarea:{name:"ScrollArea",files:["src/dashkit/ScrollArea/ScrollArea.tsx","src/dashkit/ScrollArea/ScrollArea.test.tsx","src/dashkit/ScrollArea/index.ts"],dependencies:["clsx","tailwind-merge","class-variance-authority"]},statscard:{name:"StatsCard",files:["src/dashkit/StatsCard/StatsCard.tsx","src/dashkit/StatsCard/StatsCard.test.tsx","src/dashkit/StatsCard/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","react-icons"],registryDependencies:["card","animatenumber","divider"]},colorpicker:{name:"ColorPicker",files:["src/dashkit/ColorPicker/ColorPicker.tsx","src/dashkit/ColorPicker/ColorPicker.test.tsx","src/dashkit/ColorPicker/index.ts"],dependencies:["clsx","tailwind-merge","react-icons"],registryDependencies:["popover","input"]},copyfield:{name:"CopyField",files:["src/dashkit/CopyField/CopyField.tsx","src/dashkit/CopyField/CopyField.test.tsx","src/dashkit/CopyField/index.ts"],dependencies:["clsx","tailwind-merge","react-icons"],registryDependencies:["input","iconbutton","toast"]},listtile:{name:"ListTile",files:["src/dashkit/ListTile/ListTile.tsx","src/dashkit/ListTile/ListTile.test.tsx","src/dashkit/ListTile/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge"]}};import A from"fs-extra";import I from"path";import _ from"chalk";import z from"ora";import v from"fs-extra";import S from"path";var q="dashkit.json";async function H(e){await v.writeJson(S.resolve(process.cwd(),q),e,{spaces:2})}async function F(){let e=S.resolve(process.cwd(),q);if(await v.pathExists(e))try{return await v.readJson(e)}catch{return null}return null}async function P(){let e=process.cwd(),t=await v.pathExists(S.resolve(e,"src")),s=await v.pathExists(S.resolve(e,"app"))||await v.pathExists(S.resolve(e,"src","app"));return{hasSrc:t,hasApp:s}}function $(e){let t=e.hasSrc?"src":"";return{components:S.join(t,"dashkit"),css:S.join(t,"dashkit")}}async function O(e=process.cwd()){let t=["src/index.css","src/main.css","src/app.css","src/globals.css","src/app/globals.css","app/globals.css","styles/globals.css","index.css"];for(let s of t){let i=S.resolve(e,s);if(await v.pathExists(i))return i}return null}function G(e,t){let s=S.relative(S.dirname(e),t).replace(/\\/g,"/");return s.startsWith(".")||(s=`./${s}`),s}async function V(e,t){if(!await v.pathExists(e))return{updated:!1,reason:"main_css_not_found"};let s=await v.readFile(e,"utf8");if(s.includes("dashkit.css"))return{updated:!1,reason:"already_imported"};let i=G(e,t),r=`@import "${i}";`,m=/(@import\s+['"]tailwindcss['"];?\r?\n)/,o;return m.test(s)?o=s.replace(m,`$1${r} +import{Command as Re}from"commander";import h from"fs-extra";import u from"path";import f from"chalk";import ee from"ora";import{fileURLToPath as Ae}from"url";import{execSync as $e}from"child_process";import Ie from"enquirer";var w={accordion:{name:"Accordion",files:["src/dashkit/Accordion/Accordion.tsx","src/dashkit/Accordion/AccordionItem.tsx","src/dashkit/Accordion/AccordionTrigger.tsx","src/dashkit/Accordion/AccordionContent.tsx","src/dashkit/Accordion/AccordionContext.ts","src/dashkit/Accordion/Accordion.test.tsx","src/dashkit/Accordion/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","react-icons"]},avatar:{name:"Avatar",files:["src/dashkit/Avatar/Avatar.tsx","src/dashkit/Avatar/AvatarGroup.tsx","src/dashkit/Avatar/Avatar.test.tsx","src/dashkit/Avatar/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","class-variance-authority"]},badge:{name:"Badge",files:["src/dashkit/Badge/Badge.tsx","src/dashkit/Badge/FloatBadge.tsx","src/dashkit/Badge/Badge.test.tsx","src/dashkit/Badge/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","class-variance-authority"]},floatbadge:{name:"FloatBadge",files:["src/dashkit/Badge/FloatBadge.tsx"],dependencies:["clsx","tailwind-merge"],registryDependencies:["badge"]},button:{name:"Button",files:["src/dashkit/Button/Button.tsx","src/dashkit/Button/Button.test.tsx","src/dashkit/Button/index.ts"],registryDependencies:["spinner"],dependencies:["clsx","tailwind-merge","class-variance-authority"]},card:{name:"Card",files:["src/dashkit/Card/Card.tsx","src/dashkit/Card/CardHeader.tsx","src/dashkit/Card/CardContent.tsx","src/dashkit/Card/CardFooter.tsx","src/dashkit/Card/Card.test.tsx","src/dashkit/Card/index.ts"],registryDependencies:["badge","button"],dependencies:["framer-motion","clsx","tailwind-merge","class-variance-authority"]},chip:{name:"Chip",files:["src/dashkit/Chip/Chip.tsx","src/dashkit/Chip/Chip.test.tsx","src/dashkit/Chip/index.ts"],dependencies:["clsx","tailwind-merge","class-variance-authority","react-icons"]},checkbox:{name:"Checkbox",files:["src/dashkit/Checkbox/Checkbox.tsx","src/dashkit/Checkbox/Checkbox.test.tsx","src/dashkit/Checkbox/index.ts"],dependencies:["clsx","tailwind-merge"]},drawer:{name:"Drawer",files:["src/dashkit/Drawer/Drawer.tsx","src/dashkit/Drawer/useDrawer.ts","src/dashkit/Drawer/Drawer.test.tsx","src/dashkit/Drawer/index.ts"],registryDependencies:["backdrop"],dependencies:["framer-motion","clsx","tailwind-merge","class-variance-authority","react-icons"]},dropdown:{name:"Dropdown",files:["src/dashkit/Dropdown/Dropdown.tsx","src/dashkit/Dropdown/DropdownContent.tsx","src/dashkit/Dropdown/DropdownItem.tsx","src/dashkit/Dropdown/DropdownLabel.tsx","src/dashkit/Dropdown/DropdownSeparator.tsx","src/dashkit/Dropdown/DropdownTrigger.tsx","src/dashkit/Dropdown/useDropdown.ts","src/dashkit/Dropdown/Dropdown.test.tsx","src/dashkit/Dropdown/index.ts"],dependencies:["clsx","tailwind-merge","react-icons"]},imageexpander:{name:"ImageExpander",files:["src/dashkit/ImageExpander/ImageExpander.tsx","src/dashkit/ImageExpander/useImageExpander.ts","src/dashkit/ImageExpander/ImageExpander.test.tsx","src/dashkit/ImageExpander/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","react-icons"]},input:{name:"Input",files:["src/dashkit/Input/Input.tsx","src/dashkit/Input/Input.test.tsx","src/dashkit/Input/index.ts"],dependencies:["clsx","tailwind-merge","class-variance-authority"]},modal:{name:"Modal",files:["src/dashkit/Modal/Modal.tsx","src/dashkit/Modal/ModalContent.tsx","src/dashkit/Modal/ModalFooter.tsx","src/dashkit/Modal/ModalHeader.tsx","src/dashkit/Modal/useModal.ts","src/dashkit/Modal/Modal.test.tsx","src/dashkit/Modal/index.ts"],registryDependencies:["backdrop","iconbutton"],dependencies:["framer-motion","clsx","tailwind-merge","class-variance-authority","react-icons"]},otpinput:{name:"OtpInput",files:["src/dashkit/OtpInput/OtpInput.tsx","src/dashkit/OtpInput/useOtpInput.ts","src/dashkit/OtpInput/OtpInput.test.tsx","src/dashkit/OtpInput/index.ts"],dependencies:["clsx","tailwind-merge"]},radio:{name:"Radio",files:["src/dashkit/Radio/Radio.tsx","src/dashkit/Radio/useRadio.ts","src/dashkit/Radio/Radio.test.tsx","src/dashkit/Radio/index.ts"],dependencies:["clsx","tailwind-merge"]},select:{name:"Select",files:["src/dashkit/Select/Select.tsx","src/dashkit/Select/useSelect.ts","src/dashkit/Select/Select.test.tsx","src/dashkit/Select/index.ts"],dependencies:["clsx","tailwind-merge","class-variance-authority","react-icons"]},switch:{name:"Switch",files:["src/dashkit/Switch/Switch.tsx","src/dashkit/Switch/useSwitch.ts","src/dashkit/Switch/Switch.test.tsx","src/dashkit/Switch/index.ts"],dependencies:["clsx","tailwind-merge"]},slider:{name:"Slider",files:["src/dashkit/Slider/Slider.tsx","src/dashkit/Slider/useSlider.ts","src/dashkit/Slider/Slider.test.tsx","src/dashkit/Slider/index.ts"],dependencies:["clsx","tailwind-merge"]},surface:{name:"Surface",files:["src/dashkit/Surface/Surface.tsx","src/dashkit/Surface/Surface.test.tsx","src/dashkit/Surface/index.ts"],dependencies:["clsx","tailwind-merge","class-variance-authority"]},tabs:{name:"Tabs",files:["src/dashkit/Tabs/Tabs.tsx","src/dashkit/Tabs/Tabs.test.tsx","src/dashkit/Tabs/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","class-variance-authority"]},toast:{name:"Toast",files:["src/dashkit/Toast/Toast.tsx","src/dashkit/Toast/useToast.ts","src/dashkit/Toast/Toast.test.tsx","src/dashkit/Toast/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","react-icons"]},stepper:{name:"Stepper",files:["src/dashkit/Stepper/Stepper.tsx","src/dashkit/Stepper/Stepper.test.tsx","src/dashkit/Stepper/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","react-icons"]},pagination:{name:"Pagination",files:["src/dashkit/Pagination/Pagination.tsx","src/dashkit/Pagination/Pagination.test.tsx","src/dashkit/Pagination/index.ts"],dependencies:["clsx","tailwind-merge","react-icons","framer-motion"]},spinner:{name:"Spinner",files:["src/dashkit/Spinner/Spinner.tsx","src/dashkit/Spinner/Spinner.test.tsx","src/dashkit/Spinner/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge"]},navbar:{name:"Navbar",files:["src/dashkit/Navbar/Navbar.tsx","src/dashkit/Navbar/NavbarActions.tsx","src/dashkit/Navbar/NavbarBrand.tsx","src/dashkit/Navbar/NavbarLinks.tsx","src/dashkit/Navbar/Navbar.test.tsx","src/dashkit/Navbar/index.ts"],dependencies:["clsx","tailwind-merge","class-variance-authority"]},skeleton:{name:"Skeleton",files:["src/dashkit/Skeleton/Skeleton.tsx","src/dashkit/Skeleton/Skeleton.test.tsx","src/dashkit/Skeleton/index.ts"],dependencies:["clsx","tailwind-merge","class-variance-authority"]},breadcrumb:{name:"Breadcrumb",files:["src/dashkit/Breadcrumb/Breadcrumb.tsx","src/dashkit/Breadcrumb/BreadcrumbList.tsx","src/dashkit/Breadcrumb/BreadcrumbItem.tsx","src/dashkit/Breadcrumb/BreadcrumbSeparator.tsx","src/dashkit/Breadcrumb/BreadcrumbEllipsis.tsx","src/dashkit/Breadcrumb/Breadcrumb.test.tsx","src/dashkit/Breadcrumb/index.ts"],dependencies:["clsx","tailwind-merge","react-icons"]},iconbutton:{name:"IconButton",files:["src/dashkit/IconButton/IconButton.tsx","src/dashkit/IconButton/IconButton.test.tsx","src/dashkit/IconButton/index.ts"],dependencies:["clsx","tailwind-merge","class-variance-authority"]},areachart:{name:"AreaChart",files:["src/dashkit/AreaChart/AreaChart.tsx","src/dashkit/AreaChart/useAreaChart.ts","src/dashkit/AreaChart/AreaChart.test.tsx","src/dashkit/AreaChart/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge"]},barchart:{name:"BarChart",files:["src/dashkit/BarChart/BarChart.tsx","src/dashkit/BarChart/useBarChart.ts","src/dashkit/BarChart/BarChart.test.tsx","src/dashkit/BarChart/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge"]},linechart:{name:"LineChart",files:["src/dashkit/LineChart/LineChart.tsx","src/dashkit/LineChart/useLineChart.ts","src/dashkit/LineChart/LineChart.test.tsx","src/dashkit/LineChart/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge"]},piechart:{name:"PieChart",files:["src/dashkit/PieChart/PieChart.tsx","src/dashkit/PieChart/usePieChart.ts","src/dashkit/PieChart/PieChart.test.tsx","src/dashkit/PieChart/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge"]},progressbar:{name:"ProgressBar",files:["src/dashkit/ProgressBar/ProgressBar.tsx","src/dashkit/ProgressBar/useProgressBar.ts","src/dashkit/ProgressBar/ProgressBar.test.tsx","src/dashkit/ProgressBar/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","class-variance-authority"]},buttongroup:{name:"ButtonGroup",files:["src/dashkit/ButtonGroup/ButtonGroup.tsx","src/dashkit/ButtonGroup/ButtonGroup.test.tsx","src/dashkit/ButtonGroup/index.ts"],dependencies:["clsx","tailwind-merge","class-variance-authority"]},combobox:{name:"Combobox",files:["src/dashkit/Combobox/Combobox.tsx","src/dashkit/Combobox/useCombobox.ts","src/dashkit/Combobox/Combobox.test.tsx","src/dashkit/Combobox/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","react-icons"],registryDependencies:["chip"]},divider:{name:"Divider",files:["src/dashkit/Divider/Divider.tsx","src/dashkit/Divider/Divider.test.tsx","src/dashkit/Divider/index.ts"],dependencies:["clsx","tailwind-merge","class-variance-authority"]},sidebar:{name:"Sidebar",files:["src/dashkit/Sidebar/Sidebar.tsx","src/dashkit/Sidebar/useSidebar.ts","src/dashkit/Sidebar/SidebarContext.tsx","src/dashkit/Sidebar/Sidebar.test.tsx","src/dashkit/Sidebar/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","react-icons"],registryDependencies:["iconbutton","tooltip","backdrop"]},"float-action-menu":{name:"FloatActionMenu",files:["src/dashkit/FloatActionMenu/FloatActionMenu.tsx","src/dashkit/FloatActionMenu/useFloatActionMenu.ts","src/dashkit/FloatActionMenu/FloatActionMenu.test.tsx","src/dashkit/FloatActionMenu/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","react-icons"],registryDependencies:["iconbutton"]},textarea:{name:"Textarea",files:["src/dashkit/Textarea/Textarea.tsx","src/dashkit/Textarea/useTextarea.ts","src/dashkit/Textarea/Textarea.test.tsx","src/dashkit/Textarea/index.ts"],dependencies:["clsx","tailwind-merge"]},datepicker:{name:"DatePicker",files:["src/dashkit/DatePicker/DatePicker.tsx","src/dashkit/DatePicker/useDatePicker.ts","src/dashkit/DatePicker/DatePicker.test.tsx","src/dashkit/DatePicker/index.ts"],dependencies:["clsx","tailwind-merge","react-icons"]},table:{name:"Table",files:["src/dashkit/Table/Table.tsx","src/dashkit/Table/Table.test.tsx","src/dashkit/Table/index.ts"],dependencies:["clsx","tailwind-merge"]},animatenumber:{name:"AnimateNumber",files:["src/dashkit/AnimateNumber/AnimateNumber.tsx","src/dashkit/AnimateNumber/Digit.tsx","src/dashkit/AnimateNumber/AnimateNumber.test.tsx","src/dashkit/AnimateNumber/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge"]},circularprogress:{name:"CircularProgress",files:["src/dashkit/CircularProgress/CircularProgress.tsx","src/dashkit/CircularProgress/CircularProgress.test.tsx","src/dashkit/CircularProgress/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge"]},dock:{name:"Dock",files:["src/dashkit/Dock/Dock.tsx","src/dashkit/Dock/DockItem.tsx","src/dashkit/Dock/Dock.test.tsx","src/dashkit/Dock/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge"]},popover:{name:"Popover",files:["src/dashkit/Popover/Popover.tsx","src/dashkit/Popover/PopoverContent.tsx","src/dashkit/Popover/PopoverTrigger.tsx","src/dashkit/Popover/usePopover.ts","src/dashkit/Popover/Popover.test.tsx","src/dashkit/Popover/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge"]},tooltip:{name:"Tooltip",files:["src/dashkit/Tooltip/Tooltip.tsx","src/dashkit/Tooltip/Tooltip.test.tsx","src/dashkit/Tooltip/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge"]},systemlogs:{name:"SystemLogs",files:["src/dashkit/SystemLogs/SystemLogs.tsx","src/dashkit/SystemLogs/SystemLogs.test.tsx","src/dashkit/SystemLogs/index.ts"],dependencies:["clsx","tailwind-merge","react-icons"],registryDependencies:["badge"]},datefield:{name:"DateField",files:["src/dashkit/DateField/DateField.tsx","src/dashkit/DateField/useDateField.ts","src/dashkit/DateField/DateField.test.tsx","src/dashkit/DateField/index.ts"],dependencies:["clsx","tailwind-merge"]},"navigation-menu":{name:"NavigationMenu",files:["src/dashkit/NavigationMenu/NavigationMenu.tsx","src/dashkit/NavigationMenu/NavigationMenuContent.tsx","src/dashkit/NavigationMenu/NavigationMenuContext.tsx","src/dashkit/NavigationMenu/NavigationMenuItem.tsx","src/dashkit/NavigationMenu/NavigationMenuLink.tsx","src/dashkit/NavigationMenu/NavigationMenuList.tsx","src/dashkit/NavigationMenu/NavigationMenuTrigger.tsx","src/dashkit/NavigationMenu/NavigationMenuViewport.tsx","src/dashkit/NavigationMenu/useNavigationMenu.ts","src/dashkit/NavigationMenu/useNavigationMenuViewport.ts","src/dashkit/NavigationMenu/NavigationMenu.test.tsx","src/dashkit/NavigationMenu/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","react-icons"]},backdrop:{name:"Backdrop",files:["src/dashkit/Backdrop/Backdrop.tsx","src/dashkit/Backdrop/Backdrop.test.tsx","src/dashkit/Backdrop/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge"]},scrollarea:{name:"ScrollArea",files:["src/dashkit/ScrollArea/ScrollArea.tsx","src/dashkit/ScrollArea/ScrollArea.test.tsx","src/dashkit/ScrollArea/index.ts"],dependencies:["clsx","tailwind-merge","class-variance-authority"]},statscard:{name:"StatsCard",files:["src/dashkit/StatsCard/StatsCard.tsx","src/dashkit/StatsCard/StatsCard.test.tsx","src/dashkit/StatsCard/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge","react-icons"],registryDependencies:["card","animatenumber","divider"]},colorpicker:{name:"ColorPicker",files:["src/dashkit/ColorPicker/ColorPicker.tsx","src/dashkit/ColorPicker/ColorPicker.test.tsx","src/dashkit/ColorPicker/index.ts"],dependencies:["clsx","tailwind-merge","react-icons"],registryDependencies:["popover","input"]},copyfield:{name:"CopyField",files:["src/dashkit/CopyField/CopyField.tsx","src/dashkit/CopyField/CopyField.test.tsx","src/dashkit/CopyField/index.ts"],dependencies:["clsx","tailwind-merge","react-icons"],registryDependencies:["input","iconbutton","toast"]},listtile:{name:"ListTile",files:["src/dashkit/ListTile/ListTile.tsx","src/dashkit/ListTile/ListTile.test.tsx","src/dashkit/ListTile/index.ts"],dependencies:["framer-motion","clsx","tailwind-merge"]}};import F from"fs-extra";import B from"path";import Z from"chalk";import Q from"ora";import v from"fs-extra";import M from"path";var ne="dashkit.json";function S(e){let t=e.trim().replace(/\\/g,"/").replace(/^\.\/+/,"").replace(/\/+$/,"");return t==="."?"":t}function U(e,t="dashkit"){let s=S(e)||S(t)||"dashkit";return(s.split("/").pop()??"").toLowerCase().includes("dashkit")?s:`${s}/dashkit`}async function re(e){let t={componentsDir:S(e.componentsDir),cssDir:S(e.cssDir)};await v.writeJson(M.resolve(process.cwd(),ne),t,{spaces:2})}async function $(){let e=M.resolve(process.cwd(),ne);if(await v.pathExists(e))try{return await v.readJson(e)}catch{return null}return null}async function I(){let e=process.cwd(),t=await v.pathExists(M.resolve(e,"src")),s=await v.pathExists(M.resolve(e,"app"))||await v.pathExists(M.resolve(e,"src","app"));return{hasSrc:t,hasApp:s}}function T(e){let t=e.hasSrc?"src/dashkit":"dashkit";return{components:t,css:t}}async function Y(e=process.cwd()){let t=["src/index.css","src/main.css","src/app.css","src/globals.css","src/app/globals.css","app/globals.css","styles/globals.css","index.css"];for(let s of t){let i=M.resolve(e,s);if(await v.pathExists(i))return i}return null}function X(e,t){let s=M.relative(M.dirname(e),t).replace(/\\/g,"/");return s.startsWith(".")||(s=`./${s}`),s}async function ae(e,t){if(!await v.pathExists(e))return{updated:!1,reason:"main_css_not_found"};let s=await v.readFile(e,"utf8");if(s.includes("dashkit.css"))return{updated:!1,reason:"already_imported"};let i=X(e,t),r=`@import "${i}";`,p=/(@import\s+['"]tailwindcss['"];?)(?:\r?\n|$)/,o;return p.test(s)?o=s.replace(p,`$1 +${r} `):o=`${r} -${s}`,await v.writeFile(e,o,"utf8"),{updated:!0,importPath:i}}import j from"chalk";var ae={success:"[SUCCESS]",warning:"[WARNING]",error:"[ERROR]",info:"[INFO]"},oe={success:j.green,warning:j.yellow,error:j.red,info:j.blue};function L(e,t="info"){let s=ae[t],i=oe[t];(t==="error"?console.error:console.log)(i(`${s} ${e}`))}var a={success:e=>L(e,"success"),warning:e=>L(e,"warning"),error:e=>L(e,"error"),info:e=>L(e,"info"),bold:e=>console.log(j.bold(e))};function M(){let e=process.cwd();return A.existsSync(I.resolve(e,"bun.lock"))||A.existsSync(I.resolve(e,"bun.lockb"))?"bun":A.existsSync(I.resolve(e,"pnpm-lock.yaml"))?"pnpm":A.existsSync(I.resolve(e,"yarn.lock"))?"yarn":A.existsSync(I.resolve(e,"package-lock.json"))?"npm":"bun"}async function N(e){let t=I.resolve(process.cwd(),"package.json");if(!await A.pathExists(t))return e;try{let s=await A.readJson(t),i={...s.dependencies,...s.devDependencies};return e.filter(r=>!i[r])}catch{return e}}async function K(){a.bold(` +${s}`,await v.writeFile(e,o,"utf8"),{updated:!0,importPath:i}}import L from"chalk";var Se={success:"[SUCCESS]",warning:"[WARNING]",error:"[ERROR]",info:"[INFO]"},Pe={success:L.green,warning:L.yellow,error:L.red,info:L.blue};function V(e,t="info"){let s=Se[t],i=Pe[t];(t==="error"?console.error:console.log)(i(`${s} ${e}`))}var a={success:e=>V(e,"success"),warning:e=>V(e,"warning"),error:e=>V(e,"error"),info:e=>V(e,"info"),bold:e=>console.log(L.bold(e))};function G(){let e=process.cwd();return F.existsSync(B.resolve(e,"bun.lock"))||F.existsSync(B.resolve(e,"bun.lockb"))?"bun":F.existsSync(B.resolve(e,"pnpm-lock.yaml"))?"pnpm":F.existsSync(B.resolve(e,"yarn.lock"))?"yarn":F.existsSync(B.resolve(e,"package-lock.json"))?"npm":"bun"}async function R(e){let t=B.resolve(process.cwd(),"package.json");if(!await F.pathExists(t))return e;try{let s=await F.readJson(t),i={...s.dependencies,...s.devDependencies};return e.filter(r=>!i[r])}catch{return e}}async function oe(){a.bold(` Dashkit UI - Health Check -`);let e=await P(),t=await F(),s=(t==null?void 0:t.cssDir)??$(e).css,i=0,r=z(`Verifying ${_.bold("core dependencies")}...`).start(),m=await N(["react","react-dom","typescript","tailwindcss"]);r.stop(),m.length===0?a.success("React, TypeScript, and TailwindCSS are correctly installed."):a.warning(`Missing some core dependencies: ${m.join(", ")}`);let o=z(`Verifying ${_.bold("dashkit.css")}...`).start(),c=I.resolve(process.cwd(),s,"dashkit.css");o.stop(),A.existsSync(c)?a.success(`dashkit.css found in ${s===""?"root":s+"/"}.`):(a.error('dashkit.css not found. Please run "dashkit init".'),i++);let d=z(`Verifying ${_.bold("design system context")}...`).start(),n=await O();if(d.stop(),n){let p=I.relative(process.cwd(),n).replace(/\\/g,"/");a.success(`Basic styling (${p}) found.`);try{(await A.readFile(n,"utf8")).includes("dashkit.css")?a.success(`dashkit.css imported in ${p}.`):a.warning(`${p} found, but dashkit.css is not imported in it.`)}catch{}}else{let p=I.join(e.hasSrc?"src":"","index.css");a.info(`Design system base (${p}) not found.`)}let l=M();a.bold(` -Environment Info:`),a.bold(` Package Manager: ${l}`),a.bold(` Project Root: ${process.cwd()} +`);let e=await I(),t=await $(),s=(t==null?void 0:t.cssDir)??T(e).css,i=0,r=Q(`Verifying ${Z.bold("core dependencies")}...`).start(),p=await R(["react","react-dom","typescript","tailwindcss"]);r.stop(),p.length===0?a.success("React, TypeScript, and TailwindCSS are correctly installed."):a.warning(`Missing some core dependencies: ${p.join(", ")}`);let o=Q(`Verifying ${Z.bold("dashkit.css")}...`).start(),l=B.resolve(process.cwd(),s,"dashkit.css");o.stop(),F.existsSync(l)?a.success(`dashkit.css found in ${s===""?"root":s+"/"}.`):(a.error('dashkit.css not found. Please run "dashkit init".'),i++);let c=Q(`Verifying ${Z.bold("design system context")}...`).start(),n=await Y();if(c.stop(),n){let m=B.relative(process.cwd(),n).replace(/\\/g,"/");a.success(`Basic styling (${m}) found.`);try{(await F.readFile(n,"utf8")).includes("dashkit.css")?a.success(`dashkit.css imported in ${m}.`):a.warning(`${m} found, but dashkit.css is not imported in it.`)}catch{}}else{let m=B.join(e.hasSrc?"src":"","index.css");a.info(`Design system base (${m}) not found.`)}let d=G();a.bold(` +Environment Info:`),a.bold(` Package Manager: ${d}`),a.bold(` Project Root: ${process.cwd()} `),i===0?a.success(`Everything looks good! Your component library is healthy. `):(a.error(`Found ${i} issues. Please address them before distributing. -`),process.exit(1))}var{MultiSelect:pe,Input:W,Confirm:J}=le,me=ce(import.meta.url),he=h.dirname(me),E=h.resolve(he,"..");async function ue(e,t){if(await u.pathExists(e))return!0;try{if(await u.pathExists(t))return await u.copy(t,e),a.success("dashkit.css installed."),!0}catch{return!1}return!1}async function Z(e,t=!1){let s=h.resolve(E,"src","dashkit","utils");if(!await u.pathExists(s))return[];let i=(await u.readdir(s)).filter(m=>/\.(ts|tsx)$/.test(m)&&!/\.(test|spec)\.(ts|tsx)$/.test(m)),r=[];await u.ensureDir(e);for(let m of i){let o=h.join(e,m);!t&&await u.pathExists(o)||(await u.copy(h.join(s,m),o),r.push(m))}return r}async function fe(e,t,s=!1,i=!1){let r=i?e.files:e.files.filter(n=>!/\.(test|spec)\.(ts|tsx)$/.test(n)),m=[];for(let n of r){let l=h.resolve(E,n),p=h.join(t,h.basename(n));if(!await u.pathExists(l))throw new Error(`File not found: ${l}`);m.push({sourcePath:l,targetPath:p,exists:await u.pathExists(p)})}let o=m.filter(n=>n.exists),c=s;if(o.length>0&&!c&&process.stdin.isTTY&&process.stdout.isTTY)try{c=await new J({name:"overwrite",message:`${o.length} file(s) for ${e.name} already exist. Overwrite?`,initial:!1}).run()}catch{c=!1}let d={copied:0,kept:[]};for(let n of m){if(n.exists&&!c){d.kept.push(h.basename(n.targetPath));continue}await u.copy(n.sourcePath,n.targetPath),d.copied+=1}return d.kept.length>0&&a.warning(`Kept existing file(s) for ${e.name}: ${d.kept.join(", ")} (use --force to overwrite).`),d}async function ge(e){let t=await N(e);if(t.length===0)return;let s=M(),r=`${s} ${s==="npm"?"install":"add"} ${t.join(" ")}`,m=U("Installing dependencies...").start();try{de(r,{stdio:"inherit",cwd:process.cwd()}),m.stop(),a.success("Dependencies installed.")}catch{m.stop(),a.error("Failed to install dependencies. Please run:"),a.bold(` ${r}`),process.exitCode=1}}async function X(e,t,s=!1,i=new Set,r=new Set){let m=e.toLowerCase(),o=x[m];if(!o)return a.error(`Component "${e}" not found in registry.`),process.exitCode=1,!1;if(r.has(m))return!0;r.add(m);let c=U(s?`Adding dependency ${f.bold(o.name)}...`:`Adding ${f.bold(o.name)}...`).start();try{let d=h.resolve(process.cwd(),t.output,o.name);if(await u.ensureDir(d),await fe(o,d,t.force,t.tests),o.dependencies&&o.dependencies.forEach(n=>i.add(n)),o.registryDependencies)for(let n of o.registryDependencies)await X(n,t,!0,i,r);return c.stop(),s||a.success(`Component ${f.bold(o.name)} added.`),!0}catch(d){return c.stop(),a.error(`Error adding ${s?"dependency ":""}${o.name}`),d instanceof Error&&a.error(d.message),process.exitCode=1,!1}}async function xe(){let e=Object.keys(x).map(t=>({name:t,message:x[t].name,value:t})).sort((t,s)=>t.message.localeCompare(s.message));try{return await new pe({name:"components",message:"Select components to add (Space to select, Enter to confirm):",choices:e,symbols:{indicator:(s,i)=>i.enabled?"[\u25CF]":"[ ]"}}).run()}catch{process.exit(0)}}async function ke(e){if(!await u.pathExists(e))return;let t=await u.readdir(e,{withFileTypes:!0}),s=[];for(let c of t){if(!c.isDirectory()||c.name==="utils")continue;let d=h.join(e,c.name,"index.ts");await u.pathExists(d)&&s.push(c.name)}if(s.length===0)return;s.sort((c,d)=>c.localeCompare(d));let i=h.join(e,"index.ts"),r=[];if(await u.pathExists(i)){let c=await u.readFile(i,"utf8");for(let d of c.split(/\r?\n/)){let n=d.trim();n.length>0&&r.push(n)}}let m=new Set(r);for(let c of s)m.add(`export * from './${c}';`);let o=Array.from(m).sort((c,d)=>c.localeCompare(d));await u.writeFile(i,o.join(` +`),process.exit(1))}var{MultiSelect:Te,Input:ce,Confirm:q}=Ie,Fe=Ae(import.meta.url),Be=u.dirname(Fe),_=u.resolve(Be,"..");async function je(e,t,s=!1){if(!await h.pathExists(t))throw new Error(`Source file not found: ${t}`);return!s&&await h.pathExists(e)?"kept":(await h.ensureDir(u.dirname(e)),await h.copy(t,e),"installed")}async function de(e,t=!1){let s=u.resolve(_,"src","dashkit","utils");if(!await h.pathExists(s))return[];let i=(await h.readdir(s)).filter(p=>/\.(ts|tsx)$/.test(p)&&!/\.(test|spec)\.(ts|tsx)$/.test(p)),r=[];await h.ensureDir(e);for(let p of i){let o=u.join(e,p);!t&&await h.pathExists(o)||(await h.copy(u.join(s,p),o),r.push(p))}return r}async function Ne(e,t,s=!1,i=!1){let r=i?e.files:e.files.filter(n=>!/\.(test|spec)\.(ts|tsx)$/.test(n)),p=[];for(let n of r){let d=u.resolve(_,n),m=u.join(t,u.basename(n));if(!await h.pathExists(d))throw new Error(`File not found: ${d}`);p.push({sourcePath:d,targetPath:m,exists:await h.pathExists(m)})}let o=p.filter(n=>n.exists),l=s;if(o.length>0&&!l&&process.stdin.isTTY&&process.stdout.isTTY)try{l=await new q({name:"overwrite",message:`${o.length} file(s) for ${e.name} already exist. Overwrite?`,initial:!1}).run()}catch{l=!1}let c={copied:0,kept:[]};for(let n of p){if(n.exists&&!l){c.kept.push(u.basename(n.targetPath));continue}await h.copy(n.sourcePath,n.targetPath),c.copied+=1}return c.kept.length>0&&a.warning(`Kept existing file(s) for ${e.name}: ${c.kept.join(", ")} (use --force to overwrite).`),c}async function le(e,t=!1){let s=await R(e);if(s.length===0)return;let i=G(),p=`${i} ${i==="npm"?"install":"add"} ${s.join(" ")}`,o=ee({text:"Installing dependencies...",isSilent:t||!process.stdout.isTTY}).start();try{$e(p,{stdio:t?"pipe":"inherit",cwd:process.cwd()}),o.stop(),t||a.success("Dependencies installed.")}catch{o.stop(),t||(a.error("Failed to install dependencies. Please run:"),a.bold(` ${p}`)),process.exitCode=1}}async function pe(e,t,s=!1,i=new Set,r=new Set){let p=e.toLowerCase(),o=w[p];if(!o)return a.error(`Component "${e}" not found in registry.`),process.exitCode=1,!1;if(r.has(p))return!0;r.add(p);let l=ee(s?`Adding dependency ${f.bold(o.name)}...`:`Adding ${f.bold(o.name)}...`).start();try{let c=u.resolve(process.cwd(),t.output,o.name);if(await h.ensureDir(c),await Ne(o,c,t.force,t.tests),o.dependencies&&o.dependencies.forEach(n=>i.add(n)),o.registryDependencies)for(let n of o.registryDependencies)await pe(n,t,!0,i,r);return l.stop(),s||a.success(`Component ${f.bold(o.name)} added.`),!0}catch(c){return l.stop(),a.error(`Error adding ${s?"dependency ":""}${o.name}`),c instanceof Error&&a.error(c.message),process.exitCode=1,!1}}async function Me(){let e=Object.keys(w).map(t=>({name:t,message:w[t].name,value:t})).sort((t,s)=>t.message.localeCompare(s.message));try{return await new Te({name:"components",message:"Select components to add (Space to select, Enter to confirm):",choices:e,symbols:{indicator:(s,i)=>i.enabled?"[\u25CF]":"[ ]"}}).run()}catch{process.exit(0)}}async function Ee(e){if(!await h.pathExists(e))return;let t=await h.readdir(e,{withFileTypes:!0}),s=[];for(let l of t){if(!l.isDirectory()||l.name==="utils")continue;let c=u.join(e,l.name,"index.ts");await h.pathExists(c)&&s.push(l.name)}if(s.length===0)return;s.sort((l,c)=>l.localeCompare(c));let i=u.join(e,"index.ts"),r=[];if(await h.pathExists(i)){let l=await h.readFile(i,"utf8");for(let c of l.split(/\r?\n/)){let n=c.trim();n.length>0&&r.push(n)}}let p=new Set(r);for(let l of s)p.add(`export * from './${l}';`);let o=Array.from(p).sort((l,c)=>l.localeCompare(c));await h.writeFile(i,o.join(` `)+` -`,"utf8")}async function Q(e,t){let s=await P(),i=$(s),r=await F();t.output||(t.output=(r==null?void 0:r.componentsDir)??i.components);let m=h.resolve(process.cwd(),t.output,"utils"),o=await Z(m,t.force);o.length>0&&a.success(`Installed utils/${o.join(", utils/")}.`);let c=(r==null?void 0:r.cssDir)??i.css,d=h.resolve(process.cwd(),c,"dashkit.css");await u.pathExists(d)||a.warning(`dashkit.css not found at ${d} \u2014 run \`dashkit init\` to install it (component styles need its tokens).`);let n=Array.isArray(e)?e:e?[e]:[],l;if(t.all)l=Object.keys(x);else if(n.length>0)l=n;else if(!process.stdin.isTTY||!process.stdout.isTTY){a.error("No component specified in non-interactive mode. Usage: dashkit add "),process.exitCode=1;return}else l=await xe();if(l.length>0){let p=new Set,g=new Set,k=!1;for(let b of l)await X(b,t,!1,g,p)&&(k=!0);if(k){if(t.install)await ge(Array.from(g));else{let w=await N(Array.from(g));if(w.length>0){let C=M(),D=C==="npm"?"install":"add";a.info(`Install missing dependencies: ${f.bold(`${C} ${D} ${w.join(" ")}`)}`)}}let b=h.resolve(process.cwd(),t.output);await ke(b)}}}async function ee(e={}){let t=await P(),s=$(t),i=await F(),r=(i==null?void 0:i.componentsDir)??s.components,m=h.resolve(process.cwd(),r),c=Object.keys(x).sort((l,p)=>x[l].name.localeCompare(x[p].name)).map(l=>{let p=x[l],g=h.join(m,p.name),k=u.existsSync(g);return{key:l,name:p.name,installed:k,dependencies:p.dependencies??[],registryDependencies:p.registryDependencies??[]}}),d=e.installed?c.filter(l=>l.installed):c;if(e.json){console.log(JSON.stringify(d,null,2));return}let n=c.filter(l=>l.installed).length;a.bold(` +`,"utf8")}async function me(e,t){let s=await I(),i=T(s),r=await $();t.output||(t.output=(r==null?void 0:r.componentsDir)??i.components);let p=u.resolve(process.cwd(),t.output,"utils"),o=await de(p,t.force);o.length>0&&a.success(`Installed utils/${o.join(", utils/")}.`);let l=(r==null?void 0:r.cssDir)??i.css,c=u.resolve(process.cwd(),l,"dashkit.css");await h.pathExists(c)||a.warning(`dashkit.css not found at ${c} \u2014 run \`dashkit init\` to install it (component styles need its tokens).`);let n=Array.isArray(e)?e:e?[e]:[],d;if(t.all)d=Object.keys(w);else if(n.length>0)d=n;else if(!process.stdin.isTTY||!process.stdout.isTTY){a.error("No component specified in non-interactive mode. Usage: dashkit add "),process.exitCode=1;return}else d=await Me();if(d.length>0){let m=new Set,g=new Set,x=!1;for(let b of d)await pe(b,t,!1,g,m)&&(x=!0);if(x){if(t.install)await le(Array.from(g));else{let k=await R(Array.from(g));if(k.length>0){let C=G(),y=C==="npm"?"install":"add";a.info(`Install missing dependencies: ${f.bold(`${C} ${y} ${k.join(" ")}`)}`)}}let b=u.resolve(process.cwd(),t.output);await Ee(b)}}}async function ue(e={}){let t=await I(),s=T(t),i=await $(),r=(i==null?void 0:i.componentsDir)??s.components,p=u.resolve(process.cwd(),r),l=Object.keys(w).sort((d,m)=>w[d].name.localeCompare(w[m].name)).map(d=>{let m=w[d],g=u.join(p,m.name),x=h.existsSync(g);return{key:d,name:m.name,installed:x,dependencies:m.dependencies??[],registryDependencies:m.registryDependencies??[]}}),c=e.installed?l.filter(d=>d.installed):l;if(e.json){console.log(JSON.stringify(c,null,2));return}let n=l.filter(d=>d.installed).length;a.bold(` Dashkit Components Registry -`),a.info(`Components directory: ${f.bold(r)}`),a.info(`Total: ${f.bold(String(c.length))} components (${f.green(`${n} installed`)}) -`);for(let l of d){let p=l.installed?f.green("[installed]"):f.dim("[available]"),g=f.bold(l.name.padEnd(20)),k=l.registryDependencies.length>0?f.cyan(` (requires: ${l.registryDependencies.join(", ")})`):"";console.log(` ${p} ${g}${k}`)}console.log(),e.installed||a.info(`Tip: Run ${f.bold("dashkit add ")} to install a component.`)}async function te(e={}){var m;let t=await P(),s=$(t),i=await O(),r;try{let o,c,d,n;if(!!e.yes||!process.stdin.isTTY||!process.stdout.isTTY)o=s.components,c=s.css||".",d=!0,n=!!i;else{if(o=await new W({name:"componentsDir",message:"Where would you like to store your components?",initial:s.components}).run(),c=await new W({name:"cssDir",message:"Where would you like to store your dashkit.css?",initial:s.css||"."}).run(),i){let R=h.relative(process.cwd(),i).replace(/\\/g,"/");n=await new J({name:"injectCss",message:`Import dashkit.css into ${R}?`,initial:!0}).run()}else n=!1;d=await new J({name:"save",message:"Save these settings to dashkit.json?",initial:!0}).run()}(m=o.replace(/\\/g,"/").split("/").pop())!=null&&m.includes("dashkit")||(o=h.join(o,"dashkit").replace(/\\/g,"/"));let p=c==="."?"":c;r=U("Initializing Dashkit...").start();let g=h.resolve(process.cwd(),p,"dashkit.css"),k=h.resolve(E,"src","dashkit","dashkit.css");await ue(g,k);let b=h.resolve(process.cwd(),o),w=h.join(b,"utils");await u.ensureDir(w),await Z(w);let C=null;if(n&&i&&(await V(i,g)).updated&&(C=h.relative(process.cwd(),i).replace(/\\/g,"/")),d&&await H({componentsDir:o,cssDir:p}),r.stop(),a.success("Dashkit initialized successfully."),a.bold(` -Initialization Summary:`),a.info(` Components Dir: ${f.bold(o)}`),a.info(` CSS Dir: ${f.bold(p||"root")}`),C)a.info(` Main CSS: ${f.bold(C)} (imported dashkit.css)`);else if(i){let y=h.relative(process.cwd(),i).replace(/\\/g,"/"),B=G(i,g);a.info(` Main CSS: ${f.bold(y)} (add: @import "${B}";)`)}d&&a.info(" Configuration: dashkit.json created");let D=await N(["clsx","tailwind-merge"]);if(D.length>0){let y=M(),B=y==="npm"?"install":"add";a.info(` Base Packages: ${f.bold(`${y} ${B} ${D.join(" ")}`)}`)}}catch(o){o instanceof Error&&o.message==="cancelled"&&process.exit(0),a.error("Failed to initialize Dashkit."),o instanceof Error&&a.error(o.message),process.exitCode=1}finally{r==null||r.stop()}}async function se(e,t={}){let s=e.toLowerCase(),i=Object.keys(x).find(w=>w===s||x[w].name.toLowerCase()===s);if(!i){a.error(`Component "${e}" not found in registry.`),process.exitCode=1;return}let r=x[i],m=await P(),o=$(m),c=await F(),d=(c==null?void 0:c.componentsDir)??o.components,n=h.resolve(process.cwd(),d,r.name),l=await u.pathExists(n),p=r.files.filter(w=>!/\.(test|spec)\.(ts|tsx)$/.test(w)),g=[],k=[];for(let w of p){let C=h.resolve(E,w);if(!await u.pathExists(C))continue;let D=await u.readFile(C,"utf8");for(let y of D.matchAll(/export\s+(?:function|const)\s+([A-Za-z0-9_]+)/g))g.includes(y[1])||g.push(y[1]);for(let y of D.matchAll(/export\s+(?:interface|type)\s+[A-Za-z0-9_]*Props\b[\s\S]*?(?=\nexport\s|\nconst\s|\nfunction\s|$)/g))k.push(y[0].trim())}let b={key:i,name:r.name,installed:l,installCommand:`bunx carllosnc/dashkit add ${i}`,files:p,dependencies:r.dependencies??[],registryDependencies:r.registryDependencies??[],exports:g,propsDefinitions:k};if(t.json){console.log(JSON.stringify(b,null,2));return}if(a.bold(` +`),a.info(`Components directory: ${f.bold(r)}`),a.info(`Total: ${f.bold(String(l.length))} components (${f.green(`${n} installed`)}) +`);for(let d of c){let m=d.installed?f.green("[installed]"):f.dim("[available]"),g=f.bold(d.name.padEnd(20)),x=d.registryDependencies.length>0?f.cyan(` (requires: ${d.registryDependencies.join(", ")})`):"";console.log(` ${m} ${g}${x}`)}console.log(),e.installed||a.info(`Tip: Run ${f.bold("dashkit add ")} to install a component.`)}async function he(e={}){let t=await I(),s=T(t),i=await $(),r=S((i==null?void 0:i.componentsDir)??s.components)||s.components,p=S((i==null?void 0:i.cssDir)??s.css),o=await Y(),l=!!e.json,c;try{let n=e.componentsDir??e.output,d=!!(e.yes||e.json||n!==void 0||e.cssDir!==void 0),m=!!(process.env.CI||process.env.CLAUDECODE||process.env.CURSOR_AGENT||process.env.CODEX_SANDBOX||process.env.ANTIGRAVITY_AGENT),g=d||m||!process.stdin.isTTY||!process.stdout.isTTY,x,b,k,C,y,P=typeof e.aiRules=="string"?e.aiRules:"AGENTS.md";if(g){x=n??r;let D=U(x,r);e.cssDir!==void 0?b=e.cssDir:n!==void 0&&(i==null?void 0:i.cssDir)===void 0?b=D:b=p||".",k=!0,C=!!o,y=e.aiRules!==!1}else{x=await new ce({name:"componentsDir",message:"Where would you like to store your components?",initial:r}).run();let W=U(x,r),ve=(i==null?void 0:i.cssDir)!==void 0?p||".":W;if(b=await new ce({name:"cssDir",message:"Where would you like to store your dashkit.css?",initial:ve}).run(),o){let ie=u.relative(process.cwd(),o).replace(/\\/g,"/");C=await new q({name:"injectCss",message:`Import dashkit.css into ${ie}?`,initial:!0}).run()}else C=!1;e.aiRules===!1?y=!1:typeof e.aiRules=="string"?y=!0:y=await new q({name:"aiRules",message:`Generate AI assistant instructions (${P})?`,initial:!0}).run(),k=await new q({name:"save",message:"Save these settings to dashkit.json?",initial:!0}).run()}let N=U(x,r),A=S(b),H=A?`${A}/dashkit.css`:"dashkit.css";c=ee({text:"Initializing Dashkit...",isSilent:l||!process.stdout.isTTY}).start();let K=u.resolve(process.cwd(),A,"dashkit.css"),be=u.resolve(_,"src","dashkit","dashkit.css"),Ce=await je(K,be,e.force),ye=u.resolve(process.cwd(),N),te=u.join(ye,"utils");await h.ensureDir(te);let De=await de(te,e.force),O="skipped";if(C&&o){let D=await ae(o,K);D.updated?O="imported":D.reason==="already_imported"&&(O="already_imported")}k&&await re({componentsDir:N,cssDir:A});let z=null;y&&(z=await ge(P,{componentsDir:N,cssDir:A})),c.stop();let se=["clsx","tailwind-merge"],E=await R(se);if(E.length>0){let D=await h.pathExists(u.resolve(process.cwd(),"package.json"));e.install!==!1&&D&&(await le(E,l),E=await R(se))}let J=o?u.relative(process.cwd(),o).replace(/\\/g,"/"):null;if(l){console.log(JSON.stringify({initialized:!0,componentsDir:N,cssDir:A,cssFile:H,cssStatus:Ce,mainCssFile:J,cssImportStatus:O,utilsInstalled:De,configFile:k?"dashkit.json":null,aiRulesFile:z,missingDependencies:E},null,2));return}if(a.success("Dashkit initialized successfully."),a.bold(` +Initialization Summary:`),a.info(` Components Dir: ${f.bold(N)}`),a.info(` CSS Dir: ${f.bold(A||"root")}`),J&&o)if(O==="imported"||O==="already_imported")a.info(` Main CSS: ${f.bold(J)} (imported dashkit.css)`);else{let D=X(o,K);a.info(` Main CSS: ${f.bold(J)} (add: @import "${D}";)`)}if(k&&a.info(" Configuration: dashkit.json saved"),z&&a.info(` AI Rules: ${f.bold(z)} (updated)`),E.length>0){let D=G(),W=D==="npm"?"install":"add";a.info(` Base Packages: ${f.bold(`${D} ${W} ${E.join(" ")}`)}`)}}catch(n){n instanceof Error&&n.message==="cancelled"&&process.exit(0),c==null||c.stop(),a.error("Failed to initialize Dashkit."),n instanceof Error&&a.error(n.message),process.exitCode=1}finally{c==null||c.stop()}}async function fe(e,t={}){let s=e.toLowerCase(),i=Object.keys(w).find(k=>k===s||w[k].name.toLowerCase()===s);if(!i){a.error(`Component "${e}" not found in registry.`),process.exitCode=1;return}let r=w[i],p=await I(),o=T(p),l=await $(),c=(l==null?void 0:l.componentsDir)??o.components,n=u.resolve(process.cwd(),c,r.name),d=await h.pathExists(n),m=r.files.filter(k=>!/\.(test|spec)\.(ts|tsx)$/.test(k)),g=[],x=[];for(let k of m){let C=u.resolve(_,k);if(!await h.pathExists(C))continue;let y=await h.readFile(C,"utf8");for(let P of y.matchAll(/export\s+(?:function|const)\s+([A-Za-z0-9_]+)/g))g.includes(P[1])||g.push(P[1]);for(let P of y.matchAll(/export\s+(?:interface|type)\s+[A-Za-z0-9_]*Props\b[\s\S]*?(?=\nexport\s|\nconst\s|\nfunction\s|$)/g))x.push(P[0].trim())}let b={key:i,name:r.name,installed:d,installCommand:`bunx carllosnc/dashkit add ${i}`,files:m,dependencies:r.dependencies??[],registryDependencies:r.registryDependencies??[],exports:g,propsDefinitions:x};if(t.json){console.log(JSON.stringify(b,null,2));return}if(a.bold(` ${r.name} (${i}) -`),a.info(`Status: ${l?f.green("installed"):f.dim("available")}`),a.info(`Install: ${f.bold(b.installCommand)}`),a.info(`Exports: ${g.join(", ")||r.name}`),a.info(`NPM Dependencies: ${b.dependencies.join(", ")||"none"}`),a.info(`Registry Dependencies: ${b.registryDependencies.join(", ")||"none"}`),k.length>0){a.bold(` -Props Definitions:`);for(let w of k)console.log(` -${w}`)}console.log()}async function ie(e={}){let t=await P(),s=$(t),i=await F(),r=((i==null?void 0:i.componentsDir)??s.components).replace(/\\/g,"/"),m=((i==null?void 0:i.cssDir)??(s.css||".")).replace(/\\/g,"/"),o=m==="."||m===""?"dashkit.css":`${m}/dashkit.css`,c=e.output||"AGENTS.md",d=h.resolve(process.cwd(),c),n=Object.keys(x).sort((p,g)=>p.localeCompare(g)),l=["","## Dashkit UI \u2014 AI Assistant Guidelines","",`- **Components Directory**: \`${r}\` (import from \`${r}/\` or barrel \`${r}\`)`,`- **Theme & Tokens File**: \`${o}\``,"- **Install Components**: `bunx carllosnc/dashkit add `","- **Inspect Component Props**: `bunx carllosnc/dashkit info --json`",`- **Available Components (${n.length})**: ${n.map(p=>`\`${p}\``).join(", ")}`,"","### 3-Tier AI Customization Protocol",`1. **Global Theme (\`${o}\`)**: Edit \`:root\` / \`.dark\` variables (\`--ds-primary\`, \`--ds-bg\`, \`--ds-card\`, \`--radius\`, \`--ds-primary-gradient\`) or set \`data-primary\` / \`data-base\` attributes. Never scatter inline theme classes across pages.`,`2. **Component Variants (\`${r}//.tsx\`)**: Edit \`cva(...)\` variants directly inside the component file when changing a component across the app.`,'3. **Instance Overrides (`className` & `data-slot`)**: Use `className` (merged via `cn()`) or `data-slot` selectors (`className="[&_[data-slot=label]]:uppercase"`) for one-off tweaks.',"","### Component API Patterns","- **Compound (`children`)**: `Card` (`CardHeader`, `CardContent`, `CardFooter`), `Tabs` (`TabsList`, `TabsTrigger`, `TabsContent`), `Accordion` (`AccordionItem`, `AccordionTrigger`, `AccordionContent`), `Modal` (`ModalHeader`, `ModalContent`, `ModalFooter`), `Drawer` (`DrawerHeader`, `DrawerContent`, `DrawerFooter`), `Dropdown`, `Popover`, `Table`, `Navbar`, `Breadcrumb`, `Stepper`.","- **Prop-Driven (`options` / props)**: `Select` & `Combobox` use `options={[{ value, label }]}`; `Input`, `Textarea`, `DatePicker`, `DateField`, `ColorPicker`, `CopyField`, `OtpInput`, `Checkbox`, `Radio`, `Switch`, `Slider` take `label`, `description`/`helperText`, `error` as props; `StatsCard` takes `title`, `value`, `trend`, `status`, `chart`.",""].join(` -`);if(await u.pathExists(d)){let p=await u.readFile(d,"utf8"),g=/[\s\S]*?/,k=g.test(p)?p.replace(g,l):`${p.trimEnd()} +`),a.info(`Status: ${d?f.green("installed"):f.dim("available")}`),a.info(`Install: ${f.bold(b.installCommand)}`),a.info(`Exports: ${g.join(", ")||r.name}`),a.info(`NPM Dependencies: ${b.dependencies.join(", ")||"none"}`),a.info(`Registry Dependencies: ${b.registryDependencies.join(", ")||"none"}`),x.length>0){a.bold(` +Props Definitions:`);for(let k of x)console.log(` +${k}`)}console.log()}async function ge(e="AGENTS.md",t){let s=await I(),i=T(s),r=t??await $(),p=S((r==null?void 0:r.componentsDir)??i.components)||i.components,o=S((r==null?void 0:r.cssDir)??i.css),l=o?`${o}/dashkit.css`:"dashkit.css",c=u.resolve(process.cwd(),e),n=Object.keys(w).sort((m,g)=>m.localeCompare(g)),d=["","## Dashkit UI \u2014 AI Assistant Guidelines","",`- **Components Directory**: \`${p}\` (import from \`${p}/\` or barrel \`${p}\`)`,`- **Theme & Tokens File**: \`${l}\``,"- **Install Components**: `bunx carllosnc/dashkit add `","- **Inspect Component Props**: `bunx carllosnc/dashkit info --json`",`- **Available Components (${n.length})**: ${n.map(m=>`\`${m}\``).join(", ")}`,"","### 3-Tier AI Customization Protocol",`1. **Global Theme (\`${l}\`)**: Edit \`:root\` / \`.dark\` variables (\`--ds-primary\`, \`--ds-bg\`, \`--ds-card\`, \`--radius\`, \`--ds-primary-gradient\`) or set \`data-primary\` / \`data-base\` attributes. Never scatter inline theme classes across pages.`,`2. **Component Variants (\`${p}//.tsx\`)**: Edit \`cva(...)\` variants directly inside the component file when changing a component across the app.`,'3. **Instance Overrides (`className` & `data-slot`)**: Use `className` (merged via `cn()`) or `data-slot` selectors (`className="[&_[data-slot=label]]:uppercase"`) for one-off tweaks.',"","### Component API Patterns","- **Compound (`children`)**: `Card` (`CardHeader`, `CardContent`, `CardFooter`), `Tabs` (`TabsList`, `TabsTrigger`, `TabsContent`), `Accordion` (`AccordionItem`, `AccordionTrigger`, `AccordionContent`), `Modal` (`ModalHeader`, `ModalContent`, `ModalFooter`), `Drawer` (`DrawerHeader`, `DrawerContent`, `DrawerFooter`), `Dropdown`, `Popover`, `Table`, `Navbar`, `Breadcrumb`, `Stepper`.","- **Prop-Driven (`options` / props)**: `Select` & `Combobox` use `options={[{ value, label }]}`; `Input`, `Textarea`, `DatePicker`, `DateField`, `ColorPicker`, `CopyField`, `OtpInput`, `Checkbox`, `Radio`, `Switch`, `Slider` take `label`, `description`/`helperText`, `error` as props; `StatsCard` takes `title`, `value`, `trend`, `status`, `chart`.",""].join(` +`);if(await h.pathExists(c)){let m=await h.readFile(c,"utf8"),g=/[\s\S]*?/,x=g.test(m)?m.replace(g,d):`${m.trimEnd()} -${l} -`;await u.writeFile(d,k,"utf8")}else await u.ensureDir(h.dirname(d)),await u.writeFile(d,`# Project AI Instructions +${d} +`;await h.writeFile(c,x,"utf8")}else await h.ensureDir(u.dirname(c)),await h.writeFile(c,`# Project AI Instructions -${l} -`,"utf8");a.success(`AI rules written to ${c}.`)}async function ne(e,t={}){let s=await P(),i=$(s),r=await F(),m=(r==null?void 0:r.componentsDir)??i.components,o=h.resolve(process.cwd(),m),c;if(e){let n=e.toLowerCase(),l=Object.keys(x).find(p=>p===n||x[p].name.toLowerCase()===n);if(!l){a.error(`Component "${e}" not found in registry.`),process.exitCode=1;return}c=[l]}else c=Object.keys(x).filter(n=>u.existsSync(h.join(o,x[n].name))).sort((n,l)=>x[n].name.localeCompare(x[l].name));let d=[];for(let n of c){let l=x[n],p=h.join(o,l.name),g=l.files.filter(C=>!/\.(test|spec)\.(ts|tsx)$/.test(C)),k=[],b=[];for(let C of g){let D=h.basename(C),y=h.resolve(E,C),B=h.join(p,D);if(!await u.pathExists(B)){b.push(D);continue}let[Y,R]=await Promise.all([u.readFile(y,"utf8"),u.readFile(B,"utf8")]);Y.replace(/\r\n/g,` -`)!==R.replace(/\r\n/g,` -`)&&k.push(D)}let w=b.length===g.length?"missing":k.length>0||b.length>0?"modified":"up-to-date";d.push({key:n,name:l.name,status:w,modifiedFiles:k,missingFiles:b})}if(t.json){console.log(JSON.stringify(d,null,2));return}if(a.bold(` +${d} +`,"utf8");return e.replace(/\\/g,"/")}async function xe(e={}){let t=e.output||"AGENTS.md";await ge(t),a.success(`AI rules written to ${t}.`)}async function ke(e,t={}){let s=await I(),i=T(s),r=await $(),p=(r==null?void 0:r.componentsDir)??i.components,o=u.resolve(process.cwd(),p),l;if(e){let n=e.toLowerCase(),d=Object.keys(w).find(m=>m===n||w[m].name.toLowerCase()===n);if(!d){a.error(`Component "${e}" not found in registry.`),process.exitCode=1;return}l=[d]}else l=Object.keys(w).filter(n=>h.existsSync(u.join(o,w[n].name))).sort((n,d)=>w[n].name.localeCompare(w[d].name));let c=[];for(let n of l){let d=w[n],m=u.join(o,d.name),g=d.files.filter(C=>!/\.(test|spec)\.(ts|tsx)$/.test(C)),x=[],b=[];for(let C of g){let y=u.basename(C),P=u.resolve(_,C),N=u.join(m,y);if(!await h.pathExists(N)){b.push(y);continue}let[A,H]=await Promise.all([h.readFile(P,"utf8"),h.readFile(N,"utf8")]);A.replace(/\r\n/g,` +`)!==H.replace(/\r\n/g,` +`)&&x.push(y)}let k=b.length===g.length?"missing":x.length>0||b.length>0?"modified":"up-to-date";c.push({key:n,name:d.name,status:k,modifiedFiles:x,missingFiles:b})}if(t.json){console.log(JSON.stringify(c,null,2));return}if(a.bold(` Dashkit Component Diff -`),d.length===0){a.info("No installed components found to compare.");return}for(let n of d)if(n.status==="up-to-date")console.log(` ${f.green("[up-to-date]")} ${f.bold(n.name)}`);else if(n.status==="missing")console.log(` ${f.red("[not installed]")} ${f.bold(n.name)}`);else{let l=[...n.modifiedFiles.map(p=>`${p} (modified)`),...n.missingFiles.map(p=>`${p} (missing)`)].join(", ");console.log(` ${f.yellow("[modified]")} ${f.bold(n.name.padEnd(20))} ${f.dim(l)}`)}console.log()}async function re(){await K()}var T=new we;T.name("dashkit").description("Dashkit CLI - Add premium components to your project").version("1.0.0");T.command("add").description("Add one or more components to your project").argument("[components...]","Components to add").option("-a, --all","Add all available components in the registry").option("-o, --output ","Output directory (defaults to smart detection)").option("--no-install","Do not automatically install dependencies").option("-f, --force","Overwrite existing component files").option("-t, --tests","Include test files for the component (*.test.tsx)").action(Q);T.command("diff").description("Compare installed component files with the upstream Dashkit registry").argument("[component]","Optional component slug or name to inspect").option("--json","Output diff report as JSON").action(ne);T.command("info").description("Show component metadata, dependencies, exports, and props").argument("","Component slug or name to inspect").option("--json","Output component metadata as JSON").action(se);T.command("list").description("List all available components in Dashkit registry").option("--installed","Show only components installed in the current project").option("--json","Output component list as JSON").action(ee);T.command("init").description("Initialize Dashkit in your project (installs dashkit.css)").option("-y, --yes","Accept all defaults without prompting").action(te);T.command("doctor").description("Run diagnostics on the registry and project structure").action(re);T.command("ai-rules").description("Generate AI assistant instructions (AGENTS.md / .cursorrules) for your project").option("-o, --output ","Target file path (defaults to AGENTS.md)").action(ie);T.parseAsync(); +`),c.length===0){a.info("No installed components found to compare.");return}for(let n of c)if(n.status==="up-to-date")console.log(` ${f.green("[up-to-date]")} ${f.bold(n.name)}`);else if(n.status==="missing")console.log(` ${f.red("[not installed]")} ${f.bold(n.name)}`);else{let d=[...n.modifiedFiles.map(m=>`${m} (modified)`),...n.missingFiles.map(m=>`${m} (missing)`)].join(", ");console.log(` ${f.yellow("[modified]")} ${f.bold(n.name.padEnd(20))} ${f.dim(d)}`)}console.log()}async function we(){await oe()}var j=new Re;j.name("dashkit").description("Dashkit CLI - Add premium components to your project").version("1.0.0");j.command("add").description("Add one or more components to your project").argument("[components...]","Components to add").option("-a, --all","Add all available components in the registry").option("-o, --output ","Output directory (defaults to smart detection)").option("--no-install","Do not automatically install dependencies").option("-f, --force","Overwrite existing component files").option("-t, --tests","Include test files for the component (*.test.tsx)").action(me);j.command("diff").description("Compare installed component files with the upstream Dashkit registry").argument("[component]","Optional component slug or name to inspect").option("--json","Output diff report as JSON").action(ke);j.command("info").description("Show component metadata, dependencies, exports, and props").argument("","Component slug or name to inspect").option("--json","Output component metadata as JSON").action(fe);j.command("list").description("List all available components in Dashkit registry").option("--installed","Show only components installed in the current project").option("--json","Output component list as JSON").action(ue);j.command("init").description("Initialize Dashkit in your project (installs dashkit.css, utils, dashkit.json, and AI rules)").option("-y, --yes","Accept all defaults without prompting").option("-c, --components-dir ","Directory to store Dashkit components").option("-o, --output ","Alias for --components-dir").option("--css-dir ","Directory to store dashkit.css").option("--ai-rules [file]","Generate AI assistant instructions (defaults to AGENTS.md)").option("--no-ai-rules","Skip generating AI assistant instructions").option("-f, --force","Overwrite existing dashkit.css and utility files").option("--no-install","Do not automatically install base dependencies").option("--json","Output initialization result as JSON for LLMs and scripts").action(he);j.command("doctor").description("Run diagnostics on the registry and project structure").action(we);j.command("ai-rules").description("Generate AI assistant instructions (AGENTS.md / .cursorrules) for your project").option("-o, --output ","Target file path (defaults to AGENTS.md)").action(xe);j.parseAsync(); diff --git a/public/llms.txt b/public/llms.txt index 765573d9..756f57f1 100644 --- a/public/llms.txt +++ b/public/llms.txt @@ -22,7 +22,7 @@ bunx carllosnc/dashkit add sidebar ## CLI Reference -- `dashkit init [-y]`: Configures dashkit.json and injects design token stylesheet (dashkit.css). +- `dashkit init [-y] [-c ] [--css-dir ] [--ai-rules [file]] [--no-ai-rules] [-f] [--no-install] [--json]`: Configures `dashkit.json`, installs `dashkit.css` and `utils/`, injects `@import` into the main stylesheet, and generates AI assistant instructions (`AGENTS.md`). - `dashkit add [--all] [-o ] [-t] [-f] [--no-install]`: Scaffolds one or more components, resolves dependencies in a single batch, and updates the root `index.ts` barrel file. - `dashkit info [--json]`: Outputs component exports, dependencies, and TypeScript Props definitions. - `dashkit list [--installed] [--json]`: Lists all registry components with status. diff --git a/scripts/cli.ts b/scripts/cli.ts index 02c333e2..958aa55e 100644 --- a/scripts/cli.ts +++ b/scripts/cli.ts @@ -43,8 +43,16 @@ program program .command('init') - .description('Initialize Dashkit in your project (installs dashkit.css)') + .description('Initialize Dashkit in your project (installs dashkit.css, utils, dashkit.json, and AI rules)') .option('-y, --yes', 'Accept all defaults without prompting') + .option('-c, --components-dir ', 'Directory to store Dashkit components') + .option('-o, --output ', 'Alias for --components-dir') + .option('--css-dir ', 'Directory to store dashkit.css') + .option('--ai-rules [file]', 'Generate AI assistant instructions (defaults to AGENTS.md)') + .option('--no-ai-rules', 'Skip generating AI assistant instructions') + .option('-f, --force', 'Overwrite existing dashkit.css and utility files') + .option('--no-install', 'Do not automatically install base dependencies') + .option('--json', 'Output initialization result as JSON for LLMs and scripts') .action(handleInit); program diff --git a/scripts/commands.ts b/scripts/commands.ts index e9af1b06..d29f0970 100644 --- a/scripts/commands.ts +++ b/scripts/commands.ts @@ -10,6 +10,8 @@ import { runDoctor, getPackageManager, getMissingDependencies } from './doctor'; import { getProjectInfo, getFolderStructure, + normalizeDirPath, + normalizeComponentsDir, saveConfig, loadConfig, findMainCssFile, @@ -61,19 +63,22 @@ export interface CliOptions { all?: boolean; } -async function installDashkitCss(targetPath: string, sourcePath: string) { - if (await fs.pathExists(targetPath)) return true; +async function installDashkitCss( + targetPath: string, + sourcePath: string, + force = false +): Promise<'installed' | 'kept'> { + if (!(await fs.pathExists(sourcePath))) { + throw new Error(`Source file not found: ${sourcePath}`); + } - try { - if (await fs.pathExists(sourcePath)) { - await fs.copy(sourcePath, targetPath); - logger.success('dashkit.css installed.'); - return true; - } - } catch { - return false; + if (!force && (await fs.pathExists(targetPath))) { + return 'kept'; } - return false; + + await fs.ensureDir(path.dirname(targetPath)); + await fs.copy(sourcePath, targetPath); + return 'installed'; } // Components import shared helpers from ../utils (cn.ts, scrollLock.ts, ...). @@ -170,7 +175,7 @@ async function copyComponentFiles( return result; } -async function installDependencies(deps: string[]) { +async function installDependencies(deps: string[], silent = false) { const missingDeps = await getMissingDependencies(deps); if (missingDeps.length === 0) return; @@ -178,15 +183,20 @@ async function installDependencies(deps: string[]) { const installCmd = pm === 'npm' ? 'install' : 'add'; const command = `${pm} ${installCmd} ${missingDeps.join(' ')}`; - const spinner = ora(`Installing dependencies...`).start(); + const spinner = ora({ + text: 'Installing dependencies...', + isSilent: silent || !process.stdout.isTTY + }).start(); try { - execSync(command, { stdio: 'inherit', cwd: process.cwd() }); + execSync(command, { stdio: silent ? 'pipe' : 'inherit', cwd: process.cwd() }); spinner.stop(); - logger.success('Dependencies installed.'); + if (!silent) logger.success('Dependencies installed.'); } catch { spinner.stop(); - logger.error('Failed to install dependencies. Please run:'); - logger.bold(` ${command}`); + if (!silent) { + logger.error('Failed to install dependencies. Please run:'); + logger.bold(` ${command}`); + } process.exitCode = 1; } } @@ -444,39 +454,97 @@ export async function handleList(options: ListOptions = {}) { export interface InitOptions { yes?: boolean; + componentsDir?: string; + output?: string; + cssDir?: string; + aiRules?: boolean | string; + force?: boolean; + install?: boolean; + json?: boolean; +} + +export interface InitResultPayload { + initialized: boolean; + componentsDir: string; + cssDir: string; + cssFile: string; + cssStatus: 'installed' | 'kept'; + mainCssFile: string | null; + cssImportStatus: 'imported' | 'already_imported' | 'skipped'; + utilsInstalled: string[]; + configFile: string | null; + aiRulesFile: string | null; + missingDependencies: string[]; } export async function handleInit(options: InitOptions = {}) { const info = await getProjectInfo(); const defaults = getFolderStructure(info); + const existingConfig = await loadConfig(); + const initialComponentsDir = normalizeDirPath(existingConfig?.componentsDir ?? defaults.components) || defaults.components; + const initialCssDir = normalizeDirPath(existingConfig?.cssDir ?? defaults.css); const mainCssFile = await findMainCssFile(); + const silent = Boolean(options.json); let spinner: ReturnType | undefined; try { - let componentsDir: string; + const explicitComponentsDir = options.componentsDir ?? options.output; + const hasExplicitFlags = Boolean( + options.yes || + options.json || + explicitComponentsDir !== undefined || + options.cssDir !== undefined + ); + const isAgentOrCiEnv = Boolean( + process.env.CI || + process.env.CLAUDECODE || + process.env.CURSOR_AGENT || + process.env.CODEX_SANDBOX || + process.env.ANTIGRAVITY_AGENT + ); + const isNonInteractive = + hasExplicitFlags || + isAgentOrCiEnv || + !process.stdin.isTTY || + !process.stdout.isTTY; + + let componentsDirRaw: string; let cssDirRaw: string; let shouldSave: boolean; let shouldInjectCss: boolean; - - const isNonInteractive = Boolean(options.yes) || !process.stdin.isTTY || !process.stdout.isTTY; + let shouldGenerateAiRules: boolean; + const aiRulesTarget = typeof options.aiRules === 'string' ? options.aiRules : 'AGENTS.md'; if (isNonInteractive) { - componentsDir = defaults.components; - cssDirRaw = defaults.css || '.'; + componentsDirRaw = explicitComponentsDir ?? initialComponentsDir; + const resolvedComponentsPreview = normalizeComponentsDir(componentsDirRaw, initialComponentsDir); + if (options.cssDir !== undefined) { + cssDirRaw = options.cssDir; + } else if (explicitComponentsDir !== undefined && existingConfig?.cssDir === undefined) { + cssDirRaw = resolvedComponentsPreview; + } else { + cssDirRaw = initialCssDir || '.'; + } shouldSave = true; shouldInjectCss = Boolean(mainCssFile); + shouldGenerateAiRules = options.aiRules !== false; } else { const componentsDirPrompt = new Input({ name: 'componentsDir', message: 'Where would you like to store your components?', - initial: defaults.components + initial: initialComponentsDir }); - componentsDir = await componentsDirPrompt.run(); + componentsDirRaw = await componentsDirPrompt.run(); + + const normalizedPreview = normalizeComponentsDir(componentsDirRaw, initialComponentsDir); + const suggestedCssDir = existingConfig?.cssDir !== undefined + ? (initialCssDir || '.') + : normalizedPreview; const cssDirPrompt = new Input({ name: 'cssDir', message: 'Where would you like to store your dashkit.css?', - initial: defaults.css || '.' + initial: suggestedCssDir }); cssDirRaw = await cssDirPrompt.run(); @@ -492,6 +560,19 @@ export async function handleInit(options: InitOptions = {}) { shouldInjectCss = false; } + if (options.aiRules === false) { + shouldGenerateAiRules = false; + } else if (typeof options.aiRules === 'string') { + shouldGenerateAiRules = true; + } else { + const confirmAiRules = new Confirm({ + name: 'aiRules', + message: `Generate AI assistant instructions (${aiRulesTarget})?`, + initial: true + }); + shouldGenerateAiRules = await confirmAiRules.run(); + } + const confirmPrompt = new Confirm({ name: 'save', message: 'Save these settings to dashkit.json?', @@ -500,17 +581,19 @@ export async function handleInit(options: InitOptions = {}) { shouldSave = await confirmPrompt.run(); } - if (!componentsDir.replace(/\\/g, '/').split('/').pop()?.includes('dashkit')) { - componentsDir = path.join(componentsDir, 'dashkit').replace(/\\/g, '/'); - } - const cssDir = cssDirRaw === '.' ? '' : cssDirRaw; + const componentsDir = normalizeComponentsDir(componentsDirRaw, initialComponentsDir); + const cssDir = normalizeDirPath(cssDirRaw); + const cssFile = cssDir ? `${cssDir}/dashkit.css` : 'dashkit.css'; - spinner = ora('Initializing Dashkit...').start(); + spinner = ora({ + text: 'Initializing Dashkit...', + isSilent: silent || !process.stdout.isTTY + }).start(); // Ensure CSS installation const targetPath = path.resolve(process.cwd(), cssDir, 'dashkit.css'); const sourcePath = path.resolve(projectRoot, 'src', 'dashkit', 'dashkit.css'); - await installDashkitCss(targetPath, sourcePath); + const cssStatus = await installDashkitCss(targetPath, sourcePath, options.force); // Create dashkit and utils folders const fullComponentsDir = path.resolve(process.cwd(), componentsDir); @@ -519,13 +602,15 @@ export async function handleInit(options: InitOptions = {}) { // Copy shared utils (cn.ts, scrollLock.ts, ...) so every component's // ../utils imports resolve in the user's project. - await copyUtilsFiles(utilsDir); + const copiedUtils = await copyUtilsFiles(utilsDir, options.force); - let injectedCssFile: string | null = null; + let cssImportStatus: 'imported' | 'already_imported' | 'skipped' = 'skipped'; if (shouldInjectCss && mainCssFile) { const injectResult = await injectDashkitCss(mainCssFile, targetPath); if (injectResult.updated) { - injectedCssFile = path.relative(process.cwd(), mainCssFile).replace(/\\/g, '/'); + cssImportStatus = 'imported'; + } else if (injectResult.reason === 'already_imported') { + cssImportStatus = 'already_imported'; } } @@ -533,22 +618,61 @@ export async function handleInit(options: InitOptions = {}) { await saveConfig({ componentsDir, cssDir }); } + let writtenAiRulesFile: string | null = null; + if (shouldGenerateAiRules) { + writtenAiRulesFile = await writeAiRulesFile(aiRulesTarget, { componentsDir, cssDir }); + } + spinner.stop(); + + const baseDeps = ['clsx', 'tailwind-merge']; + let missingBaseDeps = await getMissingDependencies(baseDeps); + if (missingBaseDeps.length > 0) { + const hasPkgJson = await fs.pathExists(path.resolve(process.cwd(), 'package.json')); + if (options.install !== false && hasPkgJson) { + await installDependencies(missingBaseDeps, silent); + missingBaseDeps = await getMissingDependencies(baseDeps); + } + } + + const relMainCss = mainCssFile + ? path.relative(process.cwd(), mainCssFile).replace(/\\/g, '/') + : null; + + if (silent) { + const payload: InitResultPayload = { + initialized: true, + componentsDir, + cssDir, + cssFile, + cssStatus, + mainCssFile: relMainCss, + cssImportStatus, + utilsInstalled: copiedUtils, + configFile: shouldSave ? 'dashkit.json' : null, + aiRulesFile: writtenAiRulesFile, + missingDependencies: missingBaseDeps, + }; + console.log(JSON.stringify(payload, null, 2)); + return; + } + logger.success('Dashkit initialized successfully.'); logger.bold('\nInitialization Summary:'); logger.info(` Components Dir: ${chalk.bold(componentsDir)}`); logger.info(` CSS Dir: ${chalk.bold(cssDir || 'root')}`); - if (injectedCssFile) { - logger.info(` Main CSS: ${chalk.bold(injectedCssFile)} (imported dashkit.css)`); - } else if (mainCssFile) { - const relMain = path.relative(process.cwd(), mainCssFile).replace(/\\/g, '/'); - const relImport = getRelativeCssImport(mainCssFile, targetPath); - logger.info(` Main CSS: ${chalk.bold(relMain)} (add: @import "${relImport}";)`); + if (relMainCss && mainCssFile) { + if (cssImportStatus === 'imported' || cssImportStatus === 'already_imported') { + logger.info(` Main CSS: ${chalk.bold(relMainCss)} (imported dashkit.css)`); + } else { + const relImport = getRelativeCssImport(mainCssFile, targetPath); + logger.info(` Main CSS: ${chalk.bold(relMainCss)} (add: @import "${relImport}";)`); + } } - if (shouldSave) logger.info(' Configuration: dashkit.json created'); + if (shouldSave) logger.info(' Configuration: dashkit.json saved'); + if (writtenAiRulesFile) logger.info(` AI Rules: ${chalk.bold(writtenAiRulesFile)} (updated)`); - const missingBaseDeps = await getMissingDependencies(['clsx', 'tailwind-merge']); if (missingBaseDeps.length > 0) { const pm = getPackageManager(); const installCmd = pm === 'npm' ? 'install' : 'add'; @@ -559,6 +683,7 @@ export async function handleInit(options: InitOptions = {}) { if (error instanceof Error && error.message === 'cancelled') { process.exit(0); } + spinner?.stop(); logger.error('Failed to initialize Dashkit.'); if (error instanceof Error) logger.error(error.message); process.exitCode = 1; @@ -650,14 +775,16 @@ export interface AiRulesOptions { output?: string; } -export async function handleAiRules(options: AiRulesOptions = {}) { +async function writeAiRulesFile( + targetFile = 'AGENTS.md', + configOverride?: { componentsDir: string; cssDir: string } +): Promise { const info = await getProjectInfo(); const defaults = getFolderStructure(info); - const config = await loadConfig(); - const componentsDir = (config?.componentsDir ?? defaults.components).replace(/\\/g, '/'); - const cssDir = (config?.cssDir ?? (defaults.css || '.')).replace(/\\/g, '/'); - const cssFile = cssDir === '.' || cssDir === '' ? 'dashkit.css' : `${cssDir}/dashkit.css`; - const targetFile = options.output || 'AGENTS.md'; + const config = configOverride ?? (await loadConfig()); + const componentsDir = normalizeDirPath(config?.componentsDir ?? defaults.components) || defaults.components; + const cssDir = normalizeDirPath(config?.cssDir ?? defaults.css); + const cssFile = cssDir ? `${cssDir}/dashkit.css` : 'dashkit.css'; const targetPath = path.resolve(process.cwd(), targetFile); const allSlugs = Object.keys(registry).sort((a, b) => a.localeCompare(b)); @@ -695,6 +822,12 @@ export async function handleAiRules(options: AiRulesOptions = {}) { await fs.writeFile(targetPath, `# Project AI Instructions\n\n${rulesBlock}\n`, 'utf8'); } + return targetFile.replace(/\\/g, '/'); +} + +export async function handleAiRules(options: AiRulesOptions = {}) { + const targetFile = options.output || 'AGENTS.md'; + await writeAiRulesFile(targetFile); logger.success(`AI rules written to ${targetFile}.`); } diff --git a/scripts/generate-llms.ts b/scripts/generate-llms.ts index f32eabc9..661e68ec 100644 --- a/scripts/generate-llms.ts +++ b/scripts/generate-llms.ts @@ -212,7 +212,7 @@ export function generateLlmsTxt(): string { lines.push('```\n'); lines.push('## CLI Reference\n'); - lines.push('- `dashkit init [-y]`: Configures dashkit.json and injects design token stylesheet (dashkit.css).'); + lines.push('- `dashkit init [-y] [-c ] [--css-dir ] [--ai-rules [file]] [--no-ai-rules] [-f] [--no-install] [--json]`: Configures `dashkit.json`, installs `dashkit.css` and `utils/`, injects `@import` into the main stylesheet, and generates AI assistant instructions (`AGENTS.md`).'); lines.push('- `dashkit add [--all] [-o ] [-t] [-f] [--no-install]`: Scaffolds one or more components, resolves dependencies in a single batch, and updates the root `index.ts` barrel file.'); lines.push('- `dashkit info [--json]`: Outputs component exports, dependencies, and TypeScript Props definitions.'); lines.push('- `dashkit list [--installed] [--json]`: Lists all registry components with status.'); diff --git a/scripts/tests/cli.test.ts b/scripts/tests/cli.test.ts index 0d39abfb..c5330605 100644 --- a/scripts/tests/cli.test.ts +++ b/scripts/tests/cli.test.ts @@ -3,6 +3,7 @@ import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import { execSync } from 'child_process'; import fs from 'fs-extra'; import path from 'path'; +import { normalizeComponentsDir, normalizeDirPath } from '../utils'; const CLI_PATH = path.resolve(__dirname, '../cli.ts'); const TEMP_DIR = path.resolve(__dirname, '../../tmp/cli-tests'); @@ -454,4 +455,99 @@ describe('Dashkit CLI', () => { expect(switchDiff?.status).toBe("modified"); expect(switchDiff?.modifiedFiles).toContain("Switch.tsx"); }, 30000); + + it("should normalize paths with POSIX forward slashes and avoid duplicate /dashkit on trailing slashes", () => { + expect(normalizeComponentsDir("src\\dashkit")).toBe("src/dashkit"); + expect(normalizeComponentsDir("src/dashkit/")).toBe("src/dashkit"); + expect(normalizeComponentsDir("src/components/")).toBe("src/components/dashkit"); + expect(normalizeDirPath("./src\\styles/")).toBe("src/styles"); + expect(normalizeDirPath(".")).toBe(""); + }); + + it("should respect existing dashkit.json and support --force on init", async () => { + const testDir = path.join(TEMP_DIR, "init-existing-and-force"); + await fs.ensureDir(path.join(testDir, "src")); + // Single-line CSS without trailing newline + const mainCss = path.join(testDir, "src/index.css"); + await fs.writeFile(mainCss, '@import "tailwindcss";'); + + await fs.writeJson(path.join(testDir, "dashkit.json"), { + componentsDir: "src\\custom-ui\\dashkit/", + cssDir: "src\\styles/" + }); + + execSync(`bun x tsx ${CLI_PATH} init --yes --no-install`, { + cwd: testDir, + stdio: "pipe", + timeout: SPAWN_TIMEOUT + }); + + const savedConfig = await fs.readJson(path.join(testDir, "dashkit.json")); + expect(savedConfig.componentsDir).toBe("src/custom-ui/dashkit"); + expect(savedConfig.cssDir).toBe("src/styles"); + + const installedCss = path.join(testDir, "src/styles/dashkit.css"); + const installedCn = path.join(testDir, "src/custom-ui/dashkit/utils/cn.ts"); + expect(await fs.pathExists(installedCss)).toBe(true); + expect(await fs.pathExists(installedCn)).toBe(true); + + // Verify single-line @import "tailwindcss"; had dashkit.css injected after it + const mainCssContent = await fs.readFile(mainCss, "utf8"); + expect(mainCssContent.indexOf('@import "tailwindcss";')).toBeLessThan( + mainCssContent.indexOf("dashkit.css") + ); + + // Customize dashkit.css and utils/cn.ts locally + await fs.writeFile(installedCss, "/* custom css */\n"); + await fs.writeFile(installedCn, "// custom cn\n"); + + // Re-run init without --force: should preserve local modifications + execSync(`bun x tsx ${CLI_PATH} init --yes --no-install`, { + cwd: testDir, + stdio: "pipe", + timeout: SPAWN_TIMEOUT + }); + expect(await fs.readFile(installedCss, "utf8")).toBe("/* custom css */\n"); + expect(await fs.readFile(installedCn, "utf8")).toBe("// custom cn\n"); + + // Re-run init with --force: should restore upstream files + execSync(`bun x tsx ${CLI_PATH} init --yes --force --no-install`, { + cwd: testDir, + stdio: "pipe", + timeout: SPAWN_TIMEOUT + }); + expect(await fs.readFile(installedCss, "utf8")).not.toBe("/* custom css */\n"); + expect(await fs.readFile(installedCn, "utf8")).not.toBe("// custom cn\n"); + }, 30000); + + it("should support LLM-first one-shot init with --json, -c, --css-dir, and automatic AI rules", async () => { + const testDir = path.join(TEMP_DIR, "init-llm-json"); + await fs.ensureDir(path.join(testDir, "src")); + await fs.writeFile(path.join(testDir, "src/index.css"), '@import "tailwindcss";\n'); + + const jsonOutput = execSync( + `bun x tsx ${CLI_PATH} init -c src/ui --css-dir src/styles --ai-rules .cursorrules --no-install --json`, + { + cwd: testDir, + encoding: "utf8", + stdio: "pipe", + timeout: SPAWN_TIMEOUT + } + ); + + const parsed = JSON.parse(jsonOutput); + expect(parsed.initialized).toBe(true); + expect(parsed.componentsDir).toBe("src/ui/dashkit"); + expect(parsed.cssDir).toBe("src/styles"); + expect(parsed.cssFile).toBe("src/styles/dashkit.css"); + expect(parsed.cssStatus).toBe("installed"); + expect(parsed.mainCssFile).toBe("src/index.css"); + expect(parsed.cssImportStatus).toBe("imported"); + expect(parsed.aiRulesFile).toBe(".cursorrules"); + expect(parsed.utilsInstalled).toContain("cn.ts"); + + const cursorRules = await fs.readFile(path.join(testDir, ".cursorrules"), "utf8"); + expect(cursorRules).toContain("src/ui/dashkit"); + expect(cursorRules).toContain("src/styles/dashkit.css"); + }, 30000); }); diff --git a/scripts/utils.ts b/scripts/utils.ts index 89efd88c..6fba134b 100644 --- a/scripts/utils.ts +++ b/scripts/utils.ts @@ -13,8 +13,26 @@ export interface DashkitConfig { const CONFIG_FILE = 'dashkit.json'; +export function normalizeDirPath(rawPath: string): string { + const cleaned = rawPath.trim().replace(/\\/g, '/').replace(/^\.\/+/, '').replace(/\/+$/, ''); + return cleaned === '.' ? '' : cleaned; +} + +export function normalizeComponentsDir(rawDir: string, fallback = 'dashkit'): string { + const cleaned = normalizeDirPath(rawDir) || normalizeDirPath(fallback) || 'dashkit'; + const lastSegment = cleaned.split('/').pop() ?? ''; + if (!lastSegment.toLowerCase().includes('dashkit')) { + return `${cleaned}/dashkit`; + } + return cleaned; +} + export async function saveConfig(config: DashkitConfig) { - await fs.writeJson(path.resolve(process.cwd(), CONFIG_FILE), config, { spaces: 2 }); + const normalized: DashkitConfig = { + componentsDir: normalizeDirPath(config.componentsDir), + cssDir: normalizeDirPath(config.cssDir), + }; + await fs.writeJson(path.resolve(process.cwd(), CONFIG_FILE), normalized, { spaces: 2 }); } export async function loadConfig(): Promise { @@ -38,11 +56,11 @@ export async function getProjectInfo(): Promise { } export function getFolderStructure(info: ProjectInfo) { - const base = info.hasSrc ? 'src' : ''; + const dir = info.hasSrc ? 'src/dashkit' : 'dashkit'; return { - components: path.join(base, 'dashkit'), - css: path.join(base, 'dashkit') + components: dir, + css: dir }; } @@ -92,12 +110,12 @@ export async function injectDashkitCss( const importPath = getRelativeCssImport(mainCssPath, dashkitCssPath); const importStatement = `@import "${importPath}";`; - // Look for @import "tailwindcss" (with single or double quotes, optional semicolon) - const twImportRegex = /(@import\s+['"]tailwindcss['"];?\r?\n)/; + // Look for @import "tailwindcss" (with single or double quotes, optional semicolon, optional trailing newline) + const twImportRegex = /(@import\s+['"]tailwindcss['"];?)(?:\r?\n|$)/; let newContent: string; if (twImportRegex.test(content)) { - newContent = content.replace(twImportRegex, `$1${importStatement}\n`); + newContent = content.replace(twImportRegex, `$1\n${importStatement}\n`); } else { newContent = `${importStatement}\n${content}`; } diff --git a/src/pages/cli.mdx b/src/pages/cli.mdx index 022fee03..05e86d51 100644 --- a/src/pages/cli.mdx +++ b/src/pages/cli.mdx @@ -44,13 +44,20 @@ bunx carllosnc/dashkit ai-rules ### `init` -Initializes Dashkit in your repository (`dashkit.css`, `utils/`, and `dashkit.json`). +Initializes Dashkit in your repository (`dashkit.css`, `utils/`, `dashkit.json`, and `AGENTS.md` AI instructions). ```bash bunx carllosnc/dashkit init [options] ``` - `-y, --yes`: Accept all detected defaults without interactive prompts. +- `-c, --components-dir ` (or `-o, --output `): Target directory for Dashkit components. +- `--css-dir `: Target directory for `dashkit.css`. +- `--ai-rules [file]`: Target file for AI assistant instructions (defaults to `AGENTS.md`). +- `--no-ai-rules`: Skip generating AI assistant instructions. +- `-f, --force`: Overwrite existing `dashkit.css` and `utils/` files. +- `--no-install`: Skip automatic installation of missing base NPM dependencies (`clsx`, `tailwind-merge`). +- `--json`: Output structured JSON summary for LLMs and automation scripts. ### `add`