@grampro/kit 0.0.0-stage → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gbs/skills/gbs-components/SKILL.md +190 -0
- package/.gbs/skills/gbs-components/references/accessibility.md +102 -0
- package/.gbs/skills/gbs-components/references/data-display.md +185 -0
- package/.gbs/skills/gbs-components/references/data-grid.md +116 -0
- package/.gbs/skills/gbs-components/references/forms.md +202 -0
- package/.gbs/skills/gbs-components/references/install.md +211 -0
- package/.gbs/skills/gbs-components/references/overlays.md +206 -0
- package/.gbs/skills/gbs-components/references/pickers.md +83 -0
- package/.gbs/skills/gbs-components/references/styling.md +246 -0
- package/.gbs/skills/gbs-components/references/toaster.md +57 -0
- package/CHANGELOG.md +139 -0
- package/LICENSE +15 -0
- package/README.md +193 -2
- package/index.cjs +1370 -0
- package/package.json +74 -4
- package/schema/passport-v1.schema.json +204 -0
- package/source/components/accordion/README.md +45 -0
- package/source/components/accordion/core/index.ts +2 -0
- package/source/components/accordion/core/open.ts +56 -0
- package/source/components/accordion/core/types.ts +17 -0
- package/source/components/accordion/index.ts +8 -0
- package/source/components/accordion/passport.json +259 -0
- package/source/components/accordion/react/Accordion.tsx +133 -0
- package/source/components/accordion/react/AccordionItem.tsx +120 -0
- package/source/components/accordion/react/context.ts +20 -0
- package/source/components/accordion/react/props.ts +13 -0
- package/source/components/accordion/styles.css +207 -0
- package/source/components/alert/README.md +39 -0
- package/source/components/alert/core/index.ts +2 -0
- package/source/components/alert/core/live.ts +25 -0
- package/source/components/alert/core/types.ts +7 -0
- package/source/components/alert/index.ts +5 -0
- package/source/components/alert/passport.json +250 -0
- package/source/components/alert/react/Alert.tsx +113 -0
- package/source/components/alert/react/icons.tsx +21 -0
- package/source/components/alert/react/locale.ts +5 -0
- package/source/components/alert/react/props.ts +11 -0
- package/source/components/alert/styles.css +154 -0
- package/source/components/avatar/core/identity.ts +103 -0
- package/source/components/avatar/core/index.ts +2 -0
- package/source/components/avatar/core/types.ts +12 -0
- package/source/components/avatar/index.ts +7 -0
- package/source/components/avatar/passport.json +294 -0
- package/source/components/avatar/react/Avatar.tsx +118 -0
- package/source/components/avatar/react/AvatarGroup.tsx +74 -0
- package/source/components/avatar/react/locale.ts +11 -0
- package/source/components/avatar/react/props.ts +6 -0
- package/source/components/avatar/styles.css +225 -0
- package/source/components/badge/README.md +37 -0
- package/source/components/badge/core/count.ts +37 -0
- package/source/components/badge/core/index.ts +2 -0
- package/source/components/badge/core/types.ts +11 -0
- package/source/components/badge/index.ts +7 -0
- package/source/components/badge/passport.json +332 -0
- package/source/components/badge/react/Badge.tsx +89 -0
- package/source/components/badge/react/Tag.tsx +82 -0
- package/source/components/badge/react/locale.ts +5 -0
- package/source/components/badge/react/props.ts +6 -0
- package/source/components/badge/styles.css +203 -0
- package/source/components/breadcrumb/README.md +34 -0
- package/source/components/breadcrumb/core/index.ts +4 -0
- package/source/components/breadcrumb/core/trail.ts +42 -0
- package/source/components/breadcrumb/core/types.ts +20 -0
- package/source/components/breadcrumb/index.ts +5 -0
- package/source/components/breadcrumb/passport.json +243 -0
- package/source/components/breadcrumb/react/Breadcrumb.tsx +177 -0
- package/source/components/breadcrumb/react/locale.ts +6 -0
- package/source/components/breadcrumb/react/props.ts +14 -0
- package/source/components/breadcrumb/styles.css +138 -0
- package/source/components/button/README.md +32 -0
- package/source/components/button/core/index.ts +3 -0
- package/source/components/button/core/state.ts +16 -0
- package/source/components/button/core/types.ts +28 -0
- package/source/components/button/index.ts +5 -0
- package/source/components/button/passport.json +402 -0
- package/source/components/button/passport.manual.json +31 -0
- package/source/components/button/react/Button.tsx +173 -0
- package/source/components/button/react/locale.ts +5 -0
- package/source/components/button/react/props.ts +23 -0
- package/source/components/button/styles.css +232 -0
- package/source/components/card/README.md +60 -0
- package/source/components/card/core/index.ts +4 -0
- package/source/components/card/core/trend.ts +32 -0
- package/source/components/card/core/types.ts +5 -0
- package/source/components/card/index.ts +5 -0
- package/source/components/card/passport.json +337 -0
- package/source/components/card/react/Card.tsx +133 -0
- package/source/components/card/react/Stat.tsx +127 -0
- package/source/components/card/react/props.ts +4 -0
- package/source/components/card/styles.css +230 -0
- package/source/components/checkbox/README.md +36 -0
- package/source/components/checkbox/core/group.ts +30 -0
- package/source/components/checkbox/core/index.ts +3 -0
- package/source/components/checkbox/core/types.ts +17 -0
- package/source/components/checkbox/index.ts +9 -0
- package/source/components/checkbox/passport.json +456 -0
- package/source/components/checkbox/react/Checkbox.tsx +147 -0
- package/source/components/checkbox/react/CheckboxGroup.tsx +149 -0
- package/source/components/checkbox/react/context.ts +16 -0
- package/source/components/checkbox/react/locale.ts +5 -0
- package/source/components/checkbox/react/props.ts +6 -0
- package/source/components/checkbox/styles.css +211 -0
- package/source/components/combobox/README.md +170 -0
- package/source/components/combobox/core/filter.ts +155 -0
- package/source/components/combobox/core/index.ts +16 -0
- package/source/components/combobox/core/types.ts +48 -0
- package/source/components/combobox/core/virtual.ts +67 -0
- package/source/components/combobox/index.ts +13 -0
- package/source/components/combobox/passport.json +456 -0
- package/source/components/combobox/react/Listbox.tsx +310 -0
- package/source/components/combobox/react/MultiSelect.tsx +265 -0
- package/source/components/combobox/react/Popover.tsx +38 -0
- package/source/components/combobox/react/Select.tsx +200 -0
- package/source/components/combobox/react/icons.tsx +11 -0
- package/source/components/combobox/react/locale.ts +16 -0
- package/source/components/combobox/react/props.ts +88 -0
- package/source/components/combobox/react/useCombobox.ts +409 -0
- package/source/components/combobox/styles.css +419 -0
- package/source/components/data-grid/README.md +233 -0
- package/source/components/data-grid/agent/coerce.ts +368 -0
- package/source/components/data-grid/agent/contract.ts +410 -0
- package/source/components/data-grid/agent/dataset.ts +92 -0
- package/source/components/data-grid/agent/engine.ts +470 -0
- package/source/components/data-grid/agent/executors.ts +155 -0
- package/source/components/data-grid/agent/index.ts +79 -0
- package/source/components/data-grid/agent/intent.ts +324 -0
- package/source/components/data-grid/agent/operations.ts +335 -0
- package/source/components/data-grid/agent/validate.ts +630 -0
- package/source/components/data-grid/agent/webmcp.ts +107 -0
- package/source/components/data-grid/core/columnHelper.ts +21 -0
- package/source/components/data-grid/core/columns.ts +288 -0
- package/source/components/data-grid/core/filtering.ts +237 -0
- package/source/components/data-grid/core/grid.ts +638 -0
- package/source/components/data-grid/core/index.ts +14 -0
- package/source/components/data-grid/core/rows.ts +64 -0
- package/source/components/data-grid/core/sorting.ts +133 -0
- package/source/components/data-grid/core/state.ts +82 -0
- package/source/components/data-grid/core/store.ts +52 -0
- package/source/components/data-grid/core/types.ts +302 -0
- package/source/components/data-grid/core/values.ts +98 -0
- package/source/components/data-grid/core/virtual.ts +85 -0
- package/source/components/data-grid/export/csv.ts +32 -0
- package/source/components/data-grid/export/download.ts +12 -0
- package/source/components/data-grid/export/pdf-font.ts +139 -0
- package/source/components/data-grid/export/pdf-writer.ts +113 -0
- package/source/components/data-grid/export/pdf.ts +379 -0
- package/source/components/data-grid/export/table.ts +73 -0
- package/source/components/data-grid/export/xlsx.ts +145 -0
- package/source/components/data-grid/export/zip.ts +87 -0
- package/source/components/data-grid/index.ts +14 -0
- package/source/components/data-grid/passport.json +1051 -0
- package/source/components/data-grid/passport.manual.json +255 -0
- package/source/components/data-grid/react/AskGrid.tsx +164 -0
- package/source/components/data-grid/react/Cell.tsx +282 -0
- package/source/components/data-grid/react/ColumnMenu.tsx +135 -0
- package/source/components/data-grid/react/DataGrid.tsx +335 -0
- package/source/components/data-grid/react/FilterForm.tsx +142 -0
- package/source/components/data-grid/react/HeaderRow.tsx +332 -0
- package/source/components/data-grid/react/Pagination.tsx +107 -0
- package/source/components/data-grid/react/Popover.tsx +35 -0
- package/source/components/data-grid/react/Row.tsx +85 -0
- package/source/components/data-grid/react/Toolbar.tsx +267 -0
- package/source/components/data-grid/react/Viewport.tsx +167 -0
- package/source/components/data-grid/react/context.ts +57 -0
- package/source/components/data-grid/react/hooks.ts +83 -0
- package/source/components/data-grid/react/icons.tsx +45 -0
- package/source/components/data-grid/react/keyboard.ts +110 -0
- package/source/components/data-grid/react/locale.ts +130 -0
- package/source/components/data-grid/styles.css +874 -0
- package/source/components/date-picker/README.md +141 -0
- package/source/components/date-picker/core/calendar.ts +266 -0
- package/source/components/date-picker/core/format.ts +170 -0
- package/source/components/date-picker/core/index.ts +41 -0
- package/source/components/date-picker/core/today.ts +72 -0
- package/source/components/date-picker/core/types.ts +68 -0
- package/source/components/date-picker/index.ts +16 -0
- package/source/components/date-picker/passport.json +407 -0
- package/source/components/date-picker/react/Calendar.tsx +160 -0
- package/source/components/date-picker/react/CalendarPanel.tsx +170 -0
- package/source/components/date-picker/react/DatePicker.tsx +249 -0
- package/source/components/date-picker/react/DateRangePicker.tsx +291 -0
- package/source/components/date-picker/react/MonthYearPanels.tsx +123 -0
- package/source/components/date-picker/react/Popover.tsx +45 -0
- package/source/components/date-picker/react/icons.tsx +15 -0
- package/source/components/date-picker/react/locale.ts +23 -0
- package/source/components/date-picker/react/props.ts +76 -0
- package/source/components/date-picker/react/useDatePicker.ts +557 -0
- package/source/components/date-picker/styles.css +445 -0
- package/source/components/dialog/README.md +39 -0
- package/source/components/dialog/core/api.ts +34 -0
- package/source/components/dialog/core/dialog.ts +11 -0
- package/source/components/dialog/core/index.ts +7 -0
- package/source/components/dialog/core/store.ts +81 -0
- package/source/components/dialog/core/types.ts +59 -0
- package/source/components/dialog/index.ts +7 -0
- package/source/components/dialog/passport.json +344 -0
- package/source/components/dialog/react/Dialog.tsx +279 -0
- package/source/components/dialog/react/DialogHost.tsx +45 -0
- package/source/components/dialog/react/icons.tsx +20 -0
- package/source/components/dialog/react/locale.ts +8 -0
- package/source/components/dialog/react/props.ts +13 -0
- package/source/components/dialog/styles.css +280 -0
- package/source/components/file-uploader/README.md +99 -0
- package/source/components/file-uploader/core/format.ts +79 -0
- package/source/components/file-uploader/core/index.ts +26 -0
- package/source/components/file-uploader/core/store.ts +432 -0
- package/source/components/file-uploader/core/transport.ts +154 -0
- package/source/components/file-uploader/core/types.ts +126 -0
- package/source/components/file-uploader/core/validate.ts +83 -0
- package/source/components/file-uploader/index.ts +7 -0
- package/source/components/file-uploader/passport.json +518 -0
- package/source/components/file-uploader/react/FileRow.tsx +268 -0
- package/source/components/file-uploader/react/FileUploader.tsx +511 -0
- package/source/components/file-uploader/react/icons.tsx +60 -0
- package/source/components/file-uploader/react/locale.ts +33 -0
- package/source/components/file-uploader/react/props.ts +31 -0
- package/source/components/file-uploader/react/useFileUploader.ts +32 -0
- package/source/components/file-uploader/styles.css +394 -0
- package/source/components/input/README.md +44 -0
- package/source/components/input/core/count.ts +5 -0
- package/source/components/input/core/index.ts +11 -0
- package/source/components/input/core/otp.ts +64 -0
- package/source/components/input/core/types.ts +14 -0
- package/source/components/input/index.ts +8 -0
- package/source/components/input/passport.json +536 -0
- package/source/components/input/react/Input.tsx +219 -0
- package/source/components/input/react/OtpInput.tsx +256 -0
- package/source/components/input/react/dom.ts +10 -0
- package/source/components/input/react/icons.tsx +17 -0
- package/source/components/input/react/locale.ts +9 -0
- package/source/components/input/react/props.ts +6 -0
- package/source/components/input/styles.css +295 -0
- package/source/components/menu/README.md +68 -0
- package/source/components/menu/core/index.ts +11 -0
- package/source/components/menu/core/navigation.ts +84 -0
- package/source/components/menu/core/types.ts +32 -0
- package/source/components/menu/index.ts +19 -0
- package/source/components/menu/passport.json +322 -0
- package/source/components/menu/react/Menu.tsx +371 -0
- package/source/components/menu/react/context.ts +31 -0
- package/source/components/menu/react/items.tsx +268 -0
- package/source/components/menu/react/locale.ts +6 -0
- package/source/components/menu/react/props.ts +4 -0
- package/source/components/menu/react/useMenuKeys.ts +124 -0
- package/source/components/menu/styles.css +224 -0
- package/source/components/modal/README.md +51 -0
- package/source/components/modal/core/dismiss.ts +29 -0
- package/source/components/modal/core/index.ts +3 -0
- package/source/components/modal/core/types.ts +27 -0
- package/source/components/modal/index.ts +5 -0
- package/source/components/modal/passport.json +289 -0
- package/source/components/modal/react/Modal.tsx +242 -0
- package/source/components/modal/react/icons.tsx +1 -0
- package/source/components/modal/react/locale.ts +5 -0
- package/source/components/modal/react/props.ts +17 -0
- package/source/components/modal/styles.css +241 -0
- package/source/components/number-input/README.md +53 -0
- package/source/components/number-input/core/format.ts +37 -0
- package/source/components/number-input/core/index.ts +4 -0
- package/source/components/number-input/core/parse.ts +118 -0
- package/source/components/number-input/core/step.ts +73 -0
- package/source/components/number-input/core/types.ts +32 -0
- package/source/components/number-input/index.ts +5 -0
- package/source/components/number-input/passport.json +541 -0
- package/source/components/number-input/react/NumberInput.tsx +465 -0
- package/source/components/number-input/react/icons.tsx +17 -0
- package/source/components/number-input/react/locale.ts +7 -0
- package/source/components/number-input/react/props.ts +11 -0
- package/source/components/number-input/styles.css +230 -0
- package/source/components/popover/README.md +44 -0
- package/source/components/popover/core/index.ts +2 -0
- package/source/components/popover/core/types.ts +12 -0
- package/source/components/popover/index.ts +4 -0
- package/source/components/popover/passport.json +238 -0
- package/source/components/popover/react/Popover.tsx +202 -0
- package/source/components/popover/react/locale.ts +5 -0
- package/source/components/popover/react/props.ts +4 -0
- package/source/components/popover/styles.css +148 -0
- package/source/components/progress/README.md +38 -0
- package/source/components/progress/core/index.ts +2 -0
- package/source/components/progress/core/progress.ts +40 -0
- package/source/components/progress/core/types.ts +23 -0
- package/source/components/progress/index.ts +7 -0
- package/source/components/progress/passport.json +270 -0
- package/source/components/progress/react/CircularProgress.tsx +106 -0
- package/source/components/progress/react/Progress.tsx +113 -0
- package/source/components/progress/react/locale.ts +7 -0
- package/source/components/progress/react/props.ts +6 -0
- package/source/components/progress/styles.css +200 -0
- package/source/components/radio-group/README.md +58 -0
- package/source/components/radio-group/core/group.ts +36 -0
- package/source/components/radio-group/core/index.ts +2 -0
- package/source/components/radio-group/core/types.ts +20 -0
- package/source/components/radio-group/index.ts +9 -0
- package/source/components/radio-group/passport.json +477 -0
- package/source/components/radio-group/react/Radio.tsx +144 -0
- package/source/components/radio-group/react/RadioGroup.tsx +179 -0
- package/source/components/radio-group/react/context.ts +18 -0
- package/source/components/radio-group/react/locale.ts +5 -0
- package/source/components/radio-group/react/props.ts +12 -0
- package/source/components/radio-group/styles.css +269 -0
- package/source/components/shared/README.md +54 -0
- package/source/components/shared/core/agent/adapter.ts +65 -0
- package/source/components/shared/core/agent/history.ts +120 -0
- package/source/components/shared/core/agent/index.ts +46 -0
- package/source/components/shared/core/agent/numbers.ts +217 -0
- package/source/components/shared/core/agent/schema.ts +180 -0
- package/source/components/shared/core/agent/types.ts +169 -0
- package/source/components/shared/core/agent/webmcp.ts +328 -0
- package/source/components/shared/core/cx.ts +9 -0
- package/source/components/shared/core/field.ts +35 -0
- package/source/components/shared/core/position.ts +124 -0
- package/source/components/shared/core/text.ts +14 -0
- package/source/components/shared/index.ts +17 -0
- package/source/components/shared/react/GramproAIProvider.tsx +50 -0
- package/source/components/shared/react/Popover.tsx +189 -0
- package/source/components/shared/react/icons.tsx +74 -0
- package/source/components/shared/react/useAskAgent.ts +217 -0
- package/source/components/shared/react/useControllableState.ts +42 -0
- package/source/components/shared/styles.css +79 -0
- package/source/components/shared/version.json +4 -0
- package/source/components/shared/version.ts +6 -0
- package/source/components/skeleton/README.md +58 -0
- package/source/components/skeleton/core/index.ts +3 -0
- package/source/components/skeleton/core/lines.ts +25 -0
- package/source/components/skeleton/core/types.ts +5 -0
- package/source/components/skeleton/index.ts +5 -0
- package/source/components/skeleton/passport.json +251 -0
- package/source/components/skeleton/react/Empty.tsx +51 -0
- package/source/components/skeleton/react/Skeleton.tsx +92 -0
- package/source/components/skeleton/react/props.ts +4 -0
- package/source/components/skeleton/styles.css +185 -0
- package/source/components/spinner/README.md +29 -0
- package/source/components/spinner/core/index.ts +3 -0
- package/source/components/spinner/core/types.ts +28 -0
- package/source/components/spinner/core/visibility.ts +34 -0
- package/source/components/spinner/index.ts +6 -0
- package/source/components/spinner/passport.json +245 -0
- package/source/components/spinner/react/Spinner.tsx +96 -0
- package/source/components/spinner/react/locale.ts +5 -0
- package/source/components/spinner/react/props.ts +4 -0
- package/source/components/spinner/react/useDelayedLoading.ts +39 -0
- package/source/components/spinner/styles.css +173 -0
- package/source/components/switch/README.md +46 -0
- package/source/components/switch/core/index.ts +2 -0
- package/source/components/switch/core/toggle.ts +54 -0
- package/source/components/switch/core/types.ts +9 -0
- package/source/components/switch/index.ts +5 -0
- package/source/components/switch/passport.json +421 -0
- package/source/components/switch/react/Switch.tsx +219 -0
- package/source/components/switch/react/locale.ts +5 -0
- package/source/components/switch/react/props.ts +12 -0
- package/source/components/switch/styles.css +227 -0
- package/source/components/tabs/README.md +32 -0
- package/source/components/tabs/core/index.ts +3 -0
- package/source/components/tabs/core/navigation.ts +54 -0
- package/source/components/tabs/core/types.ts +16 -0
- package/source/components/tabs/index.ts +5 -0
- package/source/components/tabs/passport.json +315 -0
- package/source/components/tabs/react/Tabs.tsx +306 -0
- package/source/components/tabs/react/context.ts +21 -0
- package/source/components/tabs/styles.css +263 -0
- package/source/components/textarea/README.md +39 -0
- package/source/components/textarea/core/count.ts +5 -0
- package/source/components/textarea/core/index.ts +4 -0
- package/source/components/textarea/core/size.ts +23 -0
- package/source/components/textarea/core/types.ts +17 -0
- package/source/components/textarea/index.ts +5 -0
- package/source/components/textarea/passport.json +382 -0
- package/source/components/textarea/react/Textarea.tsx +208 -0
- package/source/components/textarea/react/locale.ts +5 -0
- package/source/components/textarea/react/props.ts +4 -0
- package/source/components/textarea/styles.css +158 -0
- package/source/components/toaster/README.md +128 -0
- package/source/components/toaster/core/api.ts +87 -0
- package/source/components/toaster/core/index.ts +14 -0
- package/source/components/toaster/core/store.ts +211 -0
- package/source/components/toaster/core/toast.ts +12 -0
- package/source/components/toaster/core/types.ts +78 -0
- package/source/components/toaster/index.ts +6 -0
- package/source/components/toaster/passport.json +221 -0
- package/source/components/toaster/react/ToastItem.tsx +164 -0
- package/source/components/toaster/react/Toaster.tsx +187 -0
- package/source/components/toaster/react/icons.tsx +23 -0
- package/source/components/toaster/react/locale.ts +6 -0
- package/source/components/toaster/react/props.ts +15 -0
- package/source/components/toaster/react/useToasts.ts +11 -0
- package/source/components/toaster/styles.css +282 -0
- package/source/components/tooltip/README.md +43 -0
- package/source/components/tooltip/core/index.ts +4 -0
- package/source/components/tooltip/core/types.ts +4 -0
- package/source/components/tooltip/core/visibility.ts +34 -0
- package/source/components/tooltip/index.ts +3 -0
- package/source/components/tooltip/passport.json +170 -0
- package/source/components/tooltip/react/Tooltip.tsx +196 -0
- package/source/components/tooltip/react/props.ts +4 -0
- package/source/components/tooltip/styles.css +71 -0
- package/source/fallback/index.tsx +11 -0
- package/source/globalStyle.ts +74 -0
- package/source/hooks/SelectionHooks/type.ts +4 -0
- package/source/hooks/SelectionHooks/useClickOutside.ts +22 -0
- package/source/hooks/SelectionHooks/useMultiSelectState.ts +64 -0
- package/source/hooks/SelectionHooks/useSelectData.ts +58 -0
- package/source/hooks/SelectionHooks/useSelectState.ts +36 -0
- package/source/icon/Icon.tsx +102 -0
- package/source/icon/iconPaths.ts +206 -0
- package/source/legacy-components/bargraph/index.tsx +95 -0
- package/source/legacy-components/bargraph/type.ts +12 -0
- package/source/legacy-components/breadcrumb/index.tsx +66 -0
- package/source/legacy-components/breadcrumb/types.ts +10 -0
- package/source/legacy-components/button/index.tsx +42 -0
- package/source/legacy-components/card/index.tsx +31 -0
- package/source/legacy-components/checkbox/index.tsx +25 -0
- package/source/legacy-components/contextmenu/ContextMenu.tsx +87 -0
- package/source/legacy-components/contextmenu/ContextMenuItem.tsx +30 -0
- package/source/legacy-components/contextmenu/index.ts +2 -0
- package/source/legacy-components/contextmenu/types.ts +19 -0
- package/source/legacy-components/darkmode/index.tsx +55 -0
- package/source/legacy-components/datagrid/context/GridContext.tsx +215 -0
- package/source/legacy-components/datagrid/createTemplate.tsx +82 -0
- package/source/legacy-components/datagrid/hooks/index.ts +6 -0
- package/source/legacy-components/datagrid/hooks/useColumns.ts +33 -0
- package/source/legacy-components/datagrid/hooks/useDataSource.ts +54 -0
- package/source/legacy-components/datagrid/hooks/useFiltering.ts +169 -0
- package/source/legacy-components/datagrid/hooks/usePagination.ts +112 -0
- package/source/legacy-components/datagrid/hooks/useRowSelection.ts +48 -0
- package/source/legacy-components/datagrid/hooks/useSearch.ts +90 -0
- package/source/legacy-components/datagrid/index.tsx +28 -0
- package/source/legacy-components/datagrid/layout/FilterPopup.tsx +92 -0
- package/source/legacy-components/datagrid/layout/GridBody.tsx +184 -0
- package/source/legacy-components/datagrid/layout/GridPagination.tsx +119 -0
- package/source/legacy-components/datagrid/layout/GridToolbar.tsx +109 -0
- package/source/legacy-components/datagrid/layout/index.tsx +68 -0
- package/source/legacy-components/datagrid/type.ts +198 -0
- package/source/legacy-components/datepicker/index.tsx +365 -0
- package/source/legacy-components/datepicker/types.ts +12 -0
- package/source/legacy-components/dialog/index.tsx +44 -0
- package/source/legacy-components/dialog/types.ts +12 -0
- package/source/legacy-components/formrenderer/componentHandles/CheckBoxHandles.tsx +80 -0
- package/source/legacy-components/formrenderer/componentHandles/DatePickerHandles.tsx +93 -0
- package/source/legacy-components/formrenderer/componentHandles/InputHandles.tsx +106 -0
- package/source/legacy-components/formrenderer/componentHandles/MultiHandles.tsx +93 -0
- package/source/legacy-components/formrenderer/componentHandles/SelectHandles.tsx +95 -0
- package/source/legacy-components/formrenderer/helperFunctions.ts +72 -0
- package/source/legacy-components/formrenderer/index.tsx +93 -0
- package/source/legacy-components/formrenderer/types.ts +127 -0
- package/source/legacy-components/input/index.tsx +142 -0
- package/source/legacy-components/input/types.ts +10 -0
- package/source/legacy-components/materialInput/index.tsx +138 -0
- package/source/legacy-components/materialInput/types.ts +16 -0
- package/source/legacy-components/modal/index.tsx +152 -0
- package/source/legacy-components/modal/types.ts +19 -0
- package/source/legacy-components/multiselect/PortalDropDown.tsx +44 -0
- package/source/legacy-components/multiselect/index.tsx +269 -0
- package/source/legacy-components/multiselect/types.ts +26 -0
- package/source/legacy-components/navbar/AuthSection.tsx +98 -0
- package/source/legacy-components/navbar/MobileToggle.tsx +31 -0
- package/source/legacy-components/navbar/NavItemComponent.tsx +119 -0
- package/source/legacy-components/navbar/SearchBar.tsx +55 -0
- package/source/legacy-components/navbar/helperFunctions.ts +27 -0
- package/source/legacy-components/navbar/index.tsx +156 -0
- package/source/legacy-components/navbar/types.ts +69 -0
- package/source/legacy-components/select/PortalDropDown.tsx +59 -0
- package/source/legacy-components/select/index.tsx +249 -0
- package/source/legacy-components/select/types.ts +29 -0
- package/source/legacy-components/sidebar/components/MenuItem.tsx +101 -0
- package/source/legacy-components/sidebar/components/MenuSection.tsx +34 -0
- package/source/legacy-components/sidebar/components/SidebarFooter.tsx +50 -0
- package/source/legacy-components/sidebar/components/SidebarLogo.tsx +24 -0
- package/source/legacy-components/sidebar/components/SidebarMenu.tsx +29 -0
- package/source/legacy-components/sidebar/components/UserProfile.tsx +31 -0
- package/source/legacy-components/sidebar/hooks/useSidebarState.ts +41 -0
- package/source/legacy-components/sidebar/index.tsx +77 -0
- package/source/legacy-components/sidebar/type.ts +42 -0
- package/source/legacy-components/spinner/Stroke.tsx +89 -0
- package/source/legacy-components/spinner/index.tsx +54 -0
- package/source/legacy-components/spinner/types.ts +9 -0
- package/source/legacy-components/tabs/index.tsx +71 -0
- package/source/legacy-components/tabs/types.ts +16 -0
- package/source/legacy-components/textarea/index.tsx +123 -0
- package/source/legacy-components/textarea/types.ts +15 -0
- package/source/legacy-components/toast/ToastComponent.tsx +101 -0
- package/source/legacy-components/toast/ToastIcon.tsx +61 -0
- package/source/legacy-components/toast/index.tsx +42 -0
- package/source/legacy-components/toast/toastStore.ts +42 -0
- package/source/legacy-components/toast/types.ts +18 -0
- package/source/legacy-components/toast/useToast.ts +12 -0
- package/source/legacy-components/uploader/ProgressAnimation.tsx +50 -0
- package/source/legacy-components/uploader/UploadedFilePreview.tsx +130 -0
- package/source/legacy-components/uploader/index.tsx +311 -0
- package/source/legacy-components/uploader/types.ts +30 -0
- package/source/legacy-components/uploader/uploaderIcon.tsx +72 -0
- package/source/legacy-components/uploader/uploaderService.ts +134 -0
- package/source/legacy-components/useUploader/useUploader.tsx +310 -0
- package/source/legacy-components/usepaginateddata/usePaginatedData.ts +174 -0
- package/source/theme.ts +40 -0
- package/source/utils.ts +10 -0
- package/tools/env.cjs +61 -0
- package/tools/passport/cli.cjs +79 -0
- package/tools/passport/extract.cjs +493 -0
- package/tools/passport/index.cjs +185 -0
- package/tools/passport/merge.cjs +131 -0
- package/tools/passport/policy.cjs +65 -0
- package/tools/passport/validate.cjs +277 -0
- package/tools/ts-require.cjs +79 -0
package/index.cjs
ADDED
|
@@ -0,0 +1,1370 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require("fs-extra");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const crypto = require("crypto");
|
|
6
|
+
const yargs = require("yargs/yargs");
|
|
7
|
+
const { hideBin } = require("yargs/helpers");
|
|
8
|
+
const readline = require("readline");
|
|
9
|
+
|
|
10
|
+
// Configuration
|
|
11
|
+
const CONFIG = {
|
|
12
|
+
components: [
|
|
13
|
+
"Card",
|
|
14
|
+
"Select",
|
|
15
|
+
"SideBar",
|
|
16
|
+
"MultiSelect",
|
|
17
|
+
"Button",
|
|
18
|
+
"DatePicker",
|
|
19
|
+
"Checkbox",
|
|
20
|
+
"DarkMode",
|
|
21
|
+
"Dialog",
|
|
22
|
+
"Input",
|
|
23
|
+
"Modal",
|
|
24
|
+
"Spinner",
|
|
25
|
+
"Toast",
|
|
26
|
+
"Tabs",
|
|
27
|
+
"Uploader",
|
|
28
|
+
"FormRenderer",
|
|
29
|
+
"MaterialInput",
|
|
30
|
+
"TextArea",
|
|
31
|
+
"ContextMenu",
|
|
32
|
+
"Navbar",
|
|
33
|
+
"DataGrid",
|
|
34
|
+
"BreadCrumb",
|
|
35
|
+
"Bargraph",
|
|
36
|
+
"UsePaginatedData",
|
|
37
|
+
"UseUploader",
|
|
38
|
+
],
|
|
39
|
+
betaComponents: [
|
|
40
|
+
"DataGrid",
|
|
41
|
+
"Combobox",
|
|
42
|
+
"DatePicker",
|
|
43
|
+
"Toaster",
|
|
44
|
+
"FileUploader",
|
|
45
|
+
"Dialog",
|
|
46
|
+
"Input",
|
|
47
|
+
"Modal",
|
|
48
|
+
"Textarea",
|
|
49
|
+
"Button",
|
|
50
|
+
"Breadcrumb",
|
|
51
|
+
"Checkbox",
|
|
52
|
+
"Tabs",
|
|
53
|
+
"Spinner",
|
|
54
|
+
"Menu",
|
|
55
|
+
"Tooltip",
|
|
56
|
+
"Popover",
|
|
57
|
+
"Card",
|
|
58
|
+
"Skeleton",
|
|
59
|
+
"NumberInput",
|
|
60
|
+
"RadioGroup",
|
|
61
|
+
"Switch",
|
|
62
|
+
"Accordion",
|
|
63
|
+
"Alert",
|
|
64
|
+
"Avatar",
|
|
65
|
+
"Badge",
|
|
66
|
+
"Progress",
|
|
67
|
+
],
|
|
68
|
+
// Beta folder names that are not simply the lowercased component name.
|
|
69
|
+
betaFolders: {
|
|
70
|
+
DataGrid: "data-grid",
|
|
71
|
+
DatePicker: "date-picker",
|
|
72
|
+
FileUploader: "file-uploader",
|
|
73
|
+
NumberInput: "number-input",
|
|
74
|
+
RadioGroup: "radio-group",
|
|
75
|
+
},
|
|
76
|
+
// Define component dependencies
|
|
77
|
+
dependencies: {
|
|
78
|
+
FormRenderer: ["Select", "MultiSelect", "Input", "DatePicker"],
|
|
79
|
+
},
|
|
80
|
+
docs: "https://kit.gramproindia.com/",
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const { version: PACKAGE_VERSION } = require("./package.json");
|
|
84
|
+
|
|
85
|
+
const LEGACY_SOURCE_PATH = path.join(__dirname, "source", "legacy-components");
|
|
86
|
+
const SOURCE_PATH = path.join(__dirname, "source", "components");
|
|
87
|
+
|
|
88
|
+
/** The folder name components are copied into, inside the host project. */
|
|
89
|
+
const COMPONENT_DIR = "component-lib";
|
|
90
|
+
|
|
91
|
+
const SKIP_DIRS = new Set([
|
|
92
|
+
"node_modules", ".git", ".next", ".turbo", "dist", "build", "out", "coverage",
|
|
93
|
+
]);
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* A previous install, recognised by the barrel every install writes.
|
|
97
|
+
*
|
|
98
|
+
* This is what remembers `--dest`: rather than leaving a config file in
|
|
99
|
+
* someone's project, the components are found where they already are. An
|
|
100
|
+
* ambiguous answer is no answer, so two of them fall through to the default
|
|
101
|
+
* and the author can say which with `--dest`.
|
|
102
|
+
*/
|
|
103
|
+
const findExistingDest = () => {
|
|
104
|
+
const found = [];
|
|
105
|
+
|
|
106
|
+
const walk = (dir, depth) => {
|
|
107
|
+
if (depth > 3 || found.length > 1) return;
|
|
108
|
+
let entries;
|
|
109
|
+
try {
|
|
110
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
111
|
+
} catch {
|
|
112
|
+
return; // Unreadable directory: not somewhere we would have installed.
|
|
113
|
+
}
|
|
114
|
+
if (entries.some((entry) => entry.isFile() && entry.name === "gbs.css")) {
|
|
115
|
+
found.push(dir);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
for (const entry of entries) {
|
|
119
|
+
if (entry.isDirectory() && !SKIP_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
|
|
120
|
+
walk(path.join(dir, entry.name), depth + 1);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
walk(process.cwd(), 0);
|
|
126
|
+
return found.length === 1 ? found[0] : null;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Where to put the components in the project being worked on.
|
|
131
|
+
*
|
|
132
|
+
* `--dest` wins. Otherwise an install already here wins, so a second run
|
|
133
|
+
* lands beside the first wherever its author put it. Failing that the
|
|
134
|
+
* project's own layout decides: a project with `src/` gets `src/`, because
|
|
135
|
+
* dropping a sibling of `src/` at the root is nobody's convention.
|
|
136
|
+
*/
|
|
137
|
+
const resolveDestPath = (requested) => {
|
|
138
|
+
if (requested) return path.resolve(process.cwd(), requested);
|
|
139
|
+
|
|
140
|
+
const existing = findExistingDest();
|
|
141
|
+
if (existing) return existing;
|
|
142
|
+
|
|
143
|
+
return fs.existsSync(path.join(process.cwd(), "src"))
|
|
144
|
+
? path.join(process.cwd(), "src", COMPONENT_DIR)
|
|
145
|
+
: path.join(process.cwd(), COMPONENT_DIR);
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* The agent skill. It teaches a coding agent how to use these components, so it
|
|
150
|
+
* belongs at the root of the project being worked on, not in component-lib.
|
|
151
|
+
*/
|
|
152
|
+
const SKILL_SOURCE_PATH = path.join(__dirname, ".gbs");
|
|
153
|
+
const SKILL_DEST_DIR = ".gbs";
|
|
154
|
+
const SKILL_NAME = "gbs-components";
|
|
155
|
+
|
|
156
|
+
/** Where SKILL.md and references/ live inside the package. */
|
|
157
|
+
const SKILL_SRC_DIR = path.join(
|
|
158
|
+
SKILL_SOURCE_PATH,
|
|
159
|
+
"skills",
|
|
160
|
+
SKILL_NAME,
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Agents we can write a copy of the skill for. Each looks in its own place and
|
|
165
|
+
* none of them reads `.gbs`, which stays the canonical copy either way.
|
|
166
|
+
*/
|
|
167
|
+
const SKILL_TARGETS = ["claude", "codex", "antigravity"];
|
|
168
|
+
|
|
169
|
+
/** Codex reads one always-on file the project usually maintains by hand, so we
|
|
170
|
+
* own a marked block inside it and never touch a line outside. */
|
|
171
|
+
const AGENTS_FILE = "AGENTS.md";
|
|
172
|
+
const BLOCK_START = "<!-- @grampro/kit:start -->";
|
|
173
|
+
const BLOCK_END = "<!-- @grampro/kit:end -->";
|
|
174
|
+
|
|
175
|
+
/** Newline, named so long template literals stay readable. */
|
|
176
|
+
const NL = String.fromCharCode(10);
|
|
177
|
+
|
|
178
|
+
/** How this tool is invoked, for the messages it prints. */
|
|
179
|
+
const CLI_NAME = "@grampro/kit";
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Every beta component imports its shared helpers from `../../shared`, so this
|
|
183
|
+
* folder is installed alongside whatever the user picked. It is not a component
|
|
184
|
+
* and never appears in the component list.
|
|
185
|
+
*/
|
|
186
|
+
const SHARED_DIR = "shared";
|
|
187
|
+
const MANIFEST_FILE = ".install-manifest.json";
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Tests belong to this repo, not to the projects we install into: they import
|
|
191
|
+
* vitest, which the project has no reason to have, and the shared folder's
|
|
192
|
+
* boundary tests would run against whatever else is in the user's
|
|
193
|
+
* component-lib.
|
|
194
|
+
*/
|
|
195
|
+
const TESTS_DIR = "__tests__";
|
|
196
|
+
|
|
197
|
+
const isTestPath = (filePath) => filePath.split(/[\\/]/).includes(TESTS_DIR);
|
|
198
|
+
|
|
199
|
+
const normalizeComponent = (component, availableComponents) =>
|
|
200
|
+
availableComponents.find(
|
|
201
|
+
(available) => available.toLowerCase() === component.toLowerCase(),
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
const getAvailableComponents = (beta = false) =>
|
|
205
|
+
beta ? CONFIG.betaComponents : CONFIG.components;
|
|
206
|
+
|
|
207
|
+
const getSourcePath = (beta = false) => (beta ? SOURCE_PATH : LEGACY_SOURCE_PATH);
|
|
208
|
+
|
|
209
|
+
/** The folder a component lives in, which is not always its lowercased name. */
|
|
210
|
+
const folderFor = (component, beta = false) =>
|
|
211
|
+
(beta && CONFIG.betaFolders[component]) || component.toLowerCase();
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
/*
|
|
215
|
+
* The stylesheet list, written for the project rather than left to it.
|
|
216
|
+
*
|
|
217
|
+
* Component CSS ships unlayered, which is what makes this boring: unlayered
|
|
218
|
+
* rules beat a framework reset on ordinary specificity, so Tailwind v3,
|
|
219
|
+
* Bootstrap and a project with no framework all need nothing beyond importing
|
|
220
|
+
* the file. Tailwind v4 is the exception, because it puts its own utilities in
|
|
221
|
+
* a cascade layer, and an unlayered rule would outrank them - so a v4 project
|
|
222
|
+
* imports this inside Tailwind's `components` layer, after the framework,
|
|
223
|
+
* which is the standard way to place third-party CSS.
|
|
224
|
+
*/
|
|
225
|
+
const writeStyleBarrel = async (destPath, beta) => {
|
|
226
|
+
if (!beta) return null;
|
|
227
|
+
|
|
228
|
+
// Whatever is on disk now, not whatever this run happened to install: the
|
|
229
|
+
// barrel has to stay correct across repeated and partial installs.
|
|
230
|
+
const folders = fs
|
|
231
|
+
.readdirSync(destPath, { withFileTypes: true })
|
|
232
|
+
.filter((entry) => entry.isDirectory())
|
|
233
|
+
.map((entry) => entry.name)
|
|
234
|
+
.filter((folder) => fs.existsSync(path.join(destPath, folder, "styles.css")))
|
|
235
|
+
.sort();
|
|
236
|
+
|
|
237
|
+
// The path from the project root, not just the folder name: with the
|
|
238
|
+
// components under src/ the bare name would point somewhere they are not.
|
|
239
|
+
const dir =
|
|
240
|
+
path.relative(process.cwd(), destPath).split(path.sep).join("/") ||
|
|
241
|
+
path.basename(destPath);
|
|
242
|
+
const lines = [
|
|
243
|
+
"/*",
|
|
244
|
+
" * Generated by @grampro/kit. Rewritten on every install.",
|
|
245
|
+
" *",
|
|
246
|
+
" * Import this once in your global CSS. The paths below are written from",
|
|
247
|
+
" * the project root, so adjust them if your global CSS sits deeper.",
|
|
248
|
+
" *",
|
|
249
|
+
" * With Tailwind v4, import it after the framework and inside its",
|
|
250
|
+
" * components layer:",
|
|
251
|
+
" *",
|
|
252
|
+
' * @import "tailwindcss";',
|
|
253
|
+
' * @import "./' + dir + '/gbs.css" layer(components);',
|
|
254
|
+
" *",
|
|
255
|
+
" * With Tailwind v3, Bootstrap, or no framework, the plain import is all",
|
|
256
|
+
" * you need:",
|
|
257
|
+
" *",
|
|
258
|
+
' * @import "./' + dir + '/gbs.css";',
|
|
259
|
+
" *",
|
|
260
|
+
" * A component with no styles.css is not listed, which is normal - not",
|
|
261
|
+
" * every component ships CSS.",
|
|
262
|
+
" */",
|
|
263
|
+
...folders.map((folder) => '@import "./' + folder + '/styles.css";'),
|
|
264
|
+
"",
|
|
265
|
+
];
|
|
266
|
+
|
|
267
|
+
await fs.writeFile(path.join(destPath, "gbs.css"), lines.join(NL));
|
|
268
|
+
|
|
269
|
+
// Read the project's own Tailwind major, so the printed line is the one they
|
|
270
|
+
// actually need rather than both of them with a decision attached.
|
|
271
|
+
let tailwindMajor = null;
|
|
272
|
+
try {
|
|
273
|
+
const pkg = readJson(path.join(process.cwd(), "package.json"));
|
|
274
|
+
const range =
|
|
275
|
+
(pkg && pkg.dependencies && pkg.dependencies.tailwindcss) ||
|
|
276
|
+
(pkg && pkg.devDependencies && pkg.devDependencies.tailwindcss) ||
|
|
277
|
+
null;
|
|
278
|
+
const found = range && String(range).match(/(\d+)/);
|
|
279
|
+
if (found) tailwindMajor = Number(found[1]);
|
|
280
|
+
} catch {
|
|
281
|
+
/* No package.json, or unreadable: fall through to showing both. */
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return { count: folders.length, dir, tailwindMajor };
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
/** Printed after an install, so the one manual step is impossible to miss. */
|
|
288
|
+
const reportStyleBarrel = (barrel) => {
|
|
289
|
+
if (!barrel) return;
|
|
290
|
+
console.log(
|
|
291
|
+
NL + "Styles: wrote gbs.css, importing " + barrel.count + " stylesheet(s).",
|
|
292
|
+
);
|
|
293
|
+
console.log("Add this to your global CSS (paths from the project root):");
|
|
294
|
+
|
|
295
|
+
if (barrel.tailwindMajor === null) {
|
|
296
|
+
console.log(NL + " @import \"./" + barrel.dir + "/gbs.css\";");
|
|
297
|
+
console.log(
|
|
298
|
+
NL + " With Tailwind v4, put it after the framework and in its layer:",
|
|
299
|
+
);
|
|
300
|
+
console.log(" @import \"tailwindcss\";");
|
|
301
|
+
console.log(
|
|
302
|
+
" @import \"./" + barrel.dir + "/gbs.css\" layer(components);",
|
|
303
|
+
);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (barrel.tailwindMajor >= 4) {
|
|
308
|
+
console.log(NL + " @import \"tailwindcss\";");
|
|
309
|
+
console.log(
|
|
310
|
+
" @import \"./" + barrel.dir + "/gbs.css\" layer(components);",
|
|
311
|
+
);
|
|
312
|
+
console.log(
|
|
313
|
+
NL + " (after the framework, and in its components layer - Tailwind v4" +
|
|
314
|
+
" layers its utilities)",
|
|
315
|
+
);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
console.log(NL + " @import \"./" + barrel.dir + "/gbs.css\";");
|
|
320
|
+
console.log(NL + " (Tailwind v" + barrel.tailwindMajor + ": nothing else to do)");
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
const copyCommonFiles = async (destPath) => {
|
|
324
|
+
const commonFiles = [
|
|
325
|
+
{ src: ["..", "utils.ts"], dest: "utils.ts" },
|
|
326
|
+
{ src: ["..", "globalStyle.ts"], dest: "globalStyle.ts" },
|
|
327
|
+
{ src: ["..", "theme.ts"], dest: "theme.ts" },
|
|
328
|
+
{ src: ["..", "icon"], dest: "icon" },
|
|
329
|
+
];
|
|
330
|
+
|
|
331
|
+
for (const file of commonFiles) {
|
|
332
|
+
const src = path.join(LEGACY_SOURCE_PATH, ...file.src);
|
|
333
|
+
const dest = path.join(destPath, file.dest);
|
|
334
|
+
await fs.copy(src, dest, { overwrite: true });
|
|
335
|
+
console.log(`✓ ${file.dest} copied successfully`);
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
/* ------------------------------------------------------------ shared folder */
|
|
340
|
+
|
|
341
|
+
const sha256 = (filePath) =>
|
|
342
|
+
crypto.createHash("sha256").update(fs.readFileSync(filePath)).digest("hex");
|
|
343
|
+
|
|
344
|
+
/** Every file under a directory, relative to it, with "/" separators. */
|
|
345
|
+
const listFiles = (dir, prefix = "") =>
|
|
346
|
+
fs
|
|
347
|
+
.readdirSync(path.join(dir, prefix), { withFileTypes: true })
|
|
348
|
+
.flatMap((entry) => {
|
|
349
|
+
const relative = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
350
|
+
if (entry.name === MANIFEST_FILE || entry.name === TESTS_DIR) return [];
|
|
351
|
+
return entry.isDirectory() ? listFiles(dir, relative) : [relative];
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
/** -1, 0 or 1, comparing dotted numeric versions such as "1.2.0". */
|
|
355
|
+
const compareVersions = (a, b) => {
|
|
356
|
+
const left = String(a).split(".").map(Number);
|
|
357
|
+
const right = String(b).split(".").map(Number);
|
|
358
|
+
for (let i = 0; i < Math.max(left.length, right.length); i += 1) {
|
|
359
|
+
const diff = (left[i] || 0) - (right[i] || 0);
|
|
360
|
+
if (diff !== 0) return diff > 0 ? 1 : -1;
|
|
361
|
+
}
|
|
362
|
+
return 0;
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
const readJson = (filePath, fallback = null) => {
|
|
366
|
+
try {
|
|
367
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
368
|
+
} catch {
|
|
369
|
+
return fallback;
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
/** Files the manifest says we wrote, whose contents have since changed. */
|
|
374
|
+
const locallyEdited = (dest, manifest) =>
|
|
375
|
+
!manifest
|
|
376
|
+
? []
|
|
377
|
+
: Object.keys(manifest.files).filter((file) => {
|
|
378
|
+
const target = path.join(dest, file);
|
|
379
|
+
return fs.existsSync(target) && manifest.files[file] !== sha256(target);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Installs or updates `shared/`.
|
|
384
|
+
*
|
|
385
|
+
* Because components are copied into a project one at a time, a project can end
|
|
386
|
+
* up with components built against different versions of this folder. The rules
|
|
387
|
+
* are that `shared/` only ever moves forward, and that a file the user has
|
|
388
|
+
* edited since we wrote it is never replaced without `--force`.
|
|
389
|
+
*/
|
|
390
|
+
const installShared = async (destPath, { force = false } = {}) => {
|
|
391
|
+
const src = path.join(SOURCE_PATH, SHARED_DIR);
|
|
392
|
+
const dest = path.join(destPath, SHARED_DIR);
|
|
393
|
+
|
|
394
|
+
if (!fs.existsSync(src)) {
|
|
395
|
+
throw new Error(
|
|
396
|
+
`The shared folder is missing from ${SOURCE_PATH}. Beta components cannot be installed without it.`,
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const srcVersion =
|
|
401
|
+
(readJson(path.join(src, "version.json"), {}) || {}).version || "0.0.0";
|
|
402
|
+
const manifest = readJson(path.join(dest, MANIFEST_FILE));
|
|
403
|
+
const installedVersion = manifest ? manifest.version : null;
|
|
404
|
+
const edited = locallyEdited(dest, manifest);
|
|
405
|
+
|
|
406
|
+
if (installedVersion && !force) {
|
|
407
|
+
const order = compareVersions(installedVersion, srcVersion);
|
|
408
|
+
|
|
409
|
+
if (order > 0) {
|
|
410
|
+
console.error(
|
|
411
|
+
`\n✗ This project has shared v${installedVersion}, newer than the v${srcVersion} in this installer.`,
|
|
412
|
+
);
|
|
413
|
+
console.error(
|
|
414
|
+
" Installing it would downgrade the folder and break the components already here.",
|
|
415
|
+
);
|
|
416
|
+
console.error(
|
|
417
|
+
" Update the CLI, or re-run with --force to overwrite it anyway.",
|
|
418
|
+
);
|
|
419
|
+
process.exit(1);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (order === 0) {
|
|
423
|
+
console.log(
|
|
424
|
+
edited.length > 0
|
|
425
|
+
? `✓ shared v${installedVersion} is already installed (keeping your ${edited.length} edited file(s))`
|
|
426
|
+
: `✓ shared v${installedVersion} is already installed`,
|
|
427
|
+
);
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (edited.length > 0 && !force) {
|
|
433
|
+
console.error(
|
|
434
|
+
`\n✗ These files in component-lib/${SHARED_DIR} have local changes:`,
|
|
435
|
+
);
|
|
436
|
+
edited.forEach((file) => console.error(` - ${file}`));
|
|
437
|
+
console.error(
|
|
438
|
+
"\n Updating shared/ would replace them. Re-run with --force to do that, or move",
|
|
439
|
+
);
|
|
440
|
+
console.error(
|
|
441
|
+
" your changes into your own module first: components are yours to edit,",
|
|
442
|
+
);
|
|
443
|
+
console.error(" shared/ is replaced on every update.");
|
|
444
|
+
process.exit(1);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const files = listFiles(src);
|
|
448
|
+
for (const file of files) {
|
|
449
|
+
await fs.copy(path.join(src, file), path.join(dest, file), {
|
|
450
|
+
overwrite: true,
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Remove what an older version left behind, but only files we installed and
|
|
455
|
+
// the user has not since changed.
|
|
456
|
+
if (manifest) {
|
|
457
|
+
const removed = Object.keys(manifest.files).filter(
|
|
458
|
+
(file) => !files.includes(file),
|
|
459
|
+
);
|
|
460
|
+
for (const file of removed) {
|
|
461
|
+
const target = path.join(dest, file);
|
|
462
|
+
if (
|
|
463
|
+
fs.existsSync(target) &&
|
|
464
|
+
(force || manifest.files[file] === sha256(target))
|
|
465
|
+
) {
|
|
466
|
+
await fs.remove(target);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
fs.writeJsonSync(
|
|
472
|
+
path.join(dest, MANIFEST_FILE),
|
|
473
|
+
{
|
|
474
|
+
version: srcVersion,
|
|
475
|
+
installedAt: new Date().toISOString(),
|
|
476
|
+
files: Object.fromEntries(
|
|
477
|
+
files.map((file) => [file, sha256(path.join(src, file))]),
|
|
478
|
+
),
|
|
479
|
+
},
|
|
480
|
+
{ spaces: 2 },
|
|
481
|
+
);
|
|
482
|
+
|
|
483
|
+
console.log(
|
|
484
|
+
installedVersion
|
|
485
|
+
? `✓ shared updated ${installedVersion} → ${srcVersion}`
|
|
486
|
+
: `✓ shared v${srcVersion} installed`,
|
|
487
|
+
);
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
/* --------------------------------------------------------------- the skill */
|
|
491
|
+
|
|
492
|
+
/** Posix-style path, so manifest keys read the same on every platform. */
|
|
493
|
+
const rel = (...parts) => parts.join("/");
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Removes directories a removal left empty, up to but not including `root`.
|
|
497
|
+
*
|
|
498
|
+
* Only ever removes an empty directory, so a `.claude` folder that also holds
|
|
499
|
+
* the project's own settings survives having our skill taken out of it.
|
|
500
|
+
*/
|
|
501
|
+
const pruneEmptyDirs = async (root, from) => {
|
|
502
|
+
const stop = path.resolve(root);
|
|
503
|
+
let dir = path.dirname(path.resolve(root, from));
|
|
504
|
+
|
|
505
|
+
while (dir !== stop && dir.startsWith(stop)) {
|
|
506
|
+
if (!fs.existsSync(dir) || fs.readdirSync(dir).length > 0) return;
|
|
507
|
+
await fs.remove(dir);
|
|
508
|
+
dir = path.dirname(dir);
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
/** Splits `---` front matter from the body of a markdown document. */
|
|
513
|
+
const parseSkillDoc = (file) => {
|
|
514
|
+
const text = fs.readFileSync(file, "utf8").split("\r\n").join(NL);
|
|
515
|
+
const opener = "---" + NL;
|
|
516
|
+
if (!text.startsWith(opener)) return { frontMatter: "", body: text };
|
|
517
|
+
|
|
518
|
+
const marker = NL + "---" + NL;
|
|
519
|
+
const close = text.indexOf(marker, opener.length);
|
|
520
|
+
if (close === -1) return { frontMatter: "", body: text };
|
|
521
|
+
|
|
522
|
+
return {
|
|
523
|
+
frontMatter: text.slice(opener.length, close),
|
|
524
|
+
body: text.slice(close + marker.length),
|
|
525
|
+
};
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
/** One `key: value` line out of front matter we wrote ourselves. */
|
|
529
|
+
const frontMatterField = (frontMatter, key) => {
|
|
530
|
+
const prefix = key + ":";
|
|
531
|
+
const line = frontMatter.split(NL).find((item) => item.startsWith(prefix));
|
|
532
|
+
return line ? line.slice(prefix.length).trim() : "";
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Claude Code: a full skill folder, references included, since it reads files
|
|
537
|
+
* bundled beside SKILL.md. Only the front matter changes — it spells the glob
|
|
538
|
+
* list `paths` where the GBS agent spells it `autoAttach`.
|
|
539
|
+
*/
|
|
540
|
+
const writeClaudeSkill = async (root) => {
|
|
541
|
+
const base = rel(".claude", "skills", SKILL_NAME);
|
|
542
|
+
const written = [];
|
|
543
|
+
|
|
544
|
+
for (const file of listFiles(SKILL_SRC_DIR)) {
|
|
545
|
+
const from = path.join(SKILL_SRC_DIR, file);
|
|
546
|
+
const to = path.join(root, base, file);
|
|
547
|
+
|
|
548
|
+
if (file === "SKILL.md") {
|
|
549
|
+
const { frontMatter, body } = parseSkillDoc(from);
|
|
550
|
+
const globs = frontMatterField(frontMatter, "autoAttach");
|
|
551
|
+
const lines = [
|
|
552
|
+
"---",
|
|
553
|
+
"name: " + (frontMatterField(frontMatter, "name") || SKILL_NAME),
|
|
554
|
+
"description: " + frontMatterField(frontMatter, "description"),
|
|
555
|
+
];
|
|
556
|
+
// Claude Code spells the glob list `paths`; the GBS agent spells it `autoAttach`.
|
|
557
|
+
if (globs) lines.push("paths: " + globs);
|
|
558
|
+
lines.push("---");
|
|
559
|
+
|
|
560
|
+
// `body` keeps the blank line that followed the original front matter.
|
|
561
|
+
await fs.outputFile(to, lines.join(NL) + NL + body);
|
|
562
|
+
} else {
|
|
563
|
+
await fs.copy(from, to, { overwrite: true });
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
written.push(rel(base, file));
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
return written;
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Antigravity: one markdown rule, well inside its 12,000 character limit. Its
|
|
574
|
+
* glob activation is set on the rule in the IDE rather than in the file, so the
|
|
575
|
+
* globs are stated in the text for whoever configures it.
|
|
576
|
+
*/
|
|
577
|
+
const writeAntigravityRule = async (root) => {
|
|
578
|
+
const { frontMatter, body } = parseSkillDoc(
|
|
579
|
+
path.join(SKILL_SRC_DIR, "SKILL.md"),
|
|
580
|
+
);
|
|
581
|
+
const target = rel(".agents", "rules", SKILL_NAME + ".md");
|
|
582
|
+
|
|
583
|
+
const preamble = [
|
|
584
|
+
"> Applies to React UI files: " +
|
|
585
|
+
(frontMatterField(frontMatter, "autoAttach") || "src/**/*.tsx") +
|
|
586
|
+
".",
|
|
587
|
+
"> Set that as this rule's glob if you want it attached automatically.",
|
|
588
|
+
"> Deeper reference files live in " +
|
|
589
|
+
rel(SKILL_DEST_DIR, "skills", SKILL_NAME, "references") +
|
|
590
|
+
"/.",
|
|
591
|
+
"",
|
|
592
|
+
].join(NL);
|
|
593
|
+
|
|
594
|
+
await fs.outputFile(path.join(root, target), preamble + body);
|
|
595
|
+
return [target];
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Codex: a pointer in AGENTS.md rather than the skill itself, because that file
|
|
600
|
+
* is always in context and the skill is 5 KB. Everything outside our markers is
|
|
601
|
+
* left exactly as it was.
|
|
602
|
+
*/
|
|
603
|
+
const writeAgentsPointer = async (root) => {
|
|
604
|
+
const file = path.join(root, AGENTS_FILE);
|
|
605
|
+
const skillPath = rel(SKILL_DEST_DIR, "skills", SKILL_NAME, "SKILL.md");
|
|
606
|
+
|
|
607
|
+
const block = [
|
|
608
|
+
BLOCK_START,
|
|
609
|
+
"",
|
|
610
|
+
"## GramproKit components",
|
|
611
|
+
"",
|
|
612
|
+
"This project uses GramproKit components (@grampro/kit). They are copied",
|
|
613
|
+
"into the project with their styles, so you own them. Before adding or",
|
|
614
|
+
"changing UI, read `" + skillPath + "` and follow it.",
|
|
615
|
+
"",
|
|
616
|
+
"It covers the import rule (components are copied into `component-lib/`, not",
|
|
617
|
+
"imported from a package), the change-handler name each component uses, and",
|
|
618
|
+
"the props that do not exist. Deeper detail is in the `references/` folder",
|
|
619
|
+
"beside it — open the one the task needs.",
|
|
620
|
+
"",
|
|
621
|
+
BLOCK_END,
|
|
622
|
+
].join(NL);
|
|
623
|
+
|
|
624
|
+
const existing = fs.existsSync(file) ? fs.readFileSync(file, "utf8") : "";
|
|
625
|
+
let next;
|
|
626
|
+
|
|
627
|
+
if (existing.includes(BLOCK_START) && existing.includes(BLOCK_END)) {
|
|
628
|
+
next =
|
|
629
|
+
existing.slice(0, existing.indexOf(BLOCK_START)) +
|
|
630
|
+
block +
|
|
631
|
+
existing.slice(existing.indexOf(BLOCK_END) + BLOCK_END.length);
|
|
632
|
+
} else if (existing.trim()) {
|
|
633
|
+
next = existing.replace(/\s+$/, "") + NL + NL + block + NL;
|
|
634
|
+
} else {
|
|
635
|
+
next = "# AGENTS.md" + NL + NL + block + NL;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
await fs.outputFile(file, next);
|
|
639
|
+
return existing.includes(BLOCK_START) ? "updated" : "added";
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Installs or updates the skill at the project root.
|
|
644
|
+
*
|
|
645
|
+
* `.gbs/` is always written: it is the canonical copy and the one the GBS agent
|
|
646
|
+
* reads. The other agents each look somewhere else, so they get an adapter
|
|
647
|
+
* generated from the same source rather than a second copy to maintain.
|
|
648
|
+
*
|
|
649
|
+
* Everything we write is tracked by sha256 in one manifest, so a file the user
|
|
650
|
+
* has edited is never replaced without `--force`. AGENTS.md is the exception:
|
|
651
|
+
* the project owns that file, and we only rewrite our own marked block.
|
|
652
|
+
*/
|
|
653
|
+
const installSkill = async (
|
|
654
|
+
destRoot,
|
|
655
|
+
{ force = false, targets = SKILL_TARGETS } = {},
|
|
656
|
+
) => {
|
|
657
|
+
const src = SKILL_SOURCE_PATH;
|
|
658
|
+
|
|
659
|
+
if (!fs.existsSync(src)) {
|
|
660
|
+
throw new Error(
|
|
661
|
+
`The skill is missing from ${src}. Update the CLI and try again.`,
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
const manifestPath = path.join(destRoot, SKILL_DEST_DIR, MANIFEST_FILE);
|
|
666
|
+
const manifest = readJson(manifestPath);
|
|
667
|
+
const installedVersion = manifest ? manifest.version : null;
|
|
668
|
+
const edited = locallyEdited(destRoot, manifest);
|
|
669
|
+
|
|
670
|
+
if (edited.length > 0 && !force) {
|
|
671
|
+
console.error(`${NL}✗ These skill files have local changes:`);
|
|
672
|
+
edited.forEach((file) => console.error(` - ${file}`));
|
|
673
|
+
console.error(
|
|
674
|
+
NL +
|
|
675
|
+
" Updating the skill would replace them. Re-run with --force to do that,",
|
|
676
|
+
);
|
|
677
|
+
console.error(
|
|
678
|
+
" or move your own guidance into a separate skill folder first.",
|
|
679
|
+
);
|
|
680
|
+
process.exit(1);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// The canonical copy.
|
|
684
|
+
const written = [];
|
|
685
|
+
for (const file of listFiles(src)) {
|
|
686
|
+
await fs.copy(path.join(src, file), path.join(destRoot, SKILL_DEST_DIR, file), {
|
|
687
|
+
overwrite: true,
|
|
688
|
+
});
|
|
689
|
+
written.push(rel(SKILL_DEST_DIR, file));
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// One adapter per agent that looks somewhere else.
|
|
693
|
+
if (targets.includes("claude")) {
|
|
694
|
+
written.push(...(await writeClaudeSkill(destRoot)));
|
|
695
|
+
}
|
|
696
|
+
if (targets.includes("antigravity")) {
|
|
697
|
+
written.push(...(await writeAntigravityRule(destRoot)));
|
|
698
|
+
}
|
|
699
|
+
const agents = targets.includes("codex")
|
|
700
|
+
? await writeAgentsPointer(destRoot)
|
|
701
|
+
: null;
|
|
702
|
+
|
|
703
|
+
// Drop what an older version left behind, but only files we installed and
|
|
704
|
+
// the user has not since changed.
|
|
705
|
+
if (manifest) {
|
|
706
|
+
const removed = Object.keys(manifest.files).filter(
|
|
707
|
+
(file) => !written.includes(file),
|
|
708
|
+
);
|
|
709
|
+
for (const file of removed) {
|
|
710
|
+
const target = path.join(destRoot, file);
|
|
711
|
+
if (
|
|
712
|
+
fs.existsSync(target) &&
|
|
713
|
+
(force || manifest.files[file] === sha256(target))
|
|
714
|
+
) {
|
|
715
|
+
await fs.remove(target);
|
|
716
|
+
await pruneEmptyDirs(destRoot, file);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
fs.writeJsonSync(
|
|
722
|
+
manifestPath,
|
|
723
|
+
{
|
|
724
|
+
version: PACKAGE_VERSION,
|
|
725
|
+
installedAt: new Date().toISOString(),
|
|
726
|
+
targets,
|
|
727
|
+
// Hashed after writing, because the adapters are generated rather than copied.
|
|
728
|
+
files: Object.fromEntries(
|
|
729
|
+
written.map((file) => [file, sha256(path.join(destRoot, file))]),
|
|
730
|
+
),
|
|
731
|
+
},
|
|
732
|
+
{ spaces: 2 },
|
|
733
|
+
);
|
|
734
|
+
|
|
735
|
+
console.log(
|
|
736
|
+
installedVersion
|
|
737
|
+
? `✓ skill updated ${installedVersion} → ${PACKAGE_VERSION}`
|
|
738
|
+
: `✓ skill v${PACKAGE_VERSION} installed`,
|
|
739
|
+
);
|
|
740
|
+
console.log(` ${SKILL_DEST_DIR}/ (canonical, ${listFiles(src).length} files)`);
|
|
741
|
+
if (targets.includes("claude")) {
|
|
742
|
+
console.log(` .claude/skills/${SKILL_NAME}/ for Claude Code`);
|
|
743
|
+
}
|
|
744
|
+
if (targets.includes("antigravity")) {
|
|
745
|
+
console.log(` .agents/rules/${SKILL_NAME}.md for Antigravity`);
|
|
746
|
+
}
|
|
747
|
+
if (agents) {
|
|
748
|
+
console.log(` ${AGENTS_FILE} block ${agents}, for Codex`);
|
|
749
|
+
}
|
|
750
|
+
console.log(" Commit them so the whole team's agents pick this up.");
|
|
751
|
+
};
|
|
752
|
+
|
|
753
|
+
/** `--for claude,codex` picks the adapters; `none` writes only `.gbs/`. */
|
|
754
|
+
const parseSkillTargets = (value) => {
|
|
755
|
+
if (value === undefined || value === null || value === "") {
|
|
756
|
+
return SKILL_TARGETS;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
const wanted = String(value)
|
|
760
|
+
.split(",")
|
|
761
|
+
.map((item) => item.trim().toLowerCase())
|
|
762
|
+
.filter(Boolean);
|
|
763
|
+
|
|
764
|
+
if (wanted.includes("none")) return [];
|
|
765
|
+
|
|
766
|
+
const unknown = wanted.filter((item) => !SKILL_TARGETS.includes(item));
|
|
767
|
+
if (unknown.length > 0) {
|
|
768
|
+
console.error(
|
|
769
|
+
`Unknown --for target(s): ${unknown.join(", ")}. Valid: ${SKILL_TARGETS.join(", ")}, none.`,
|
|
770
|
+
);
|
|
771
|
+
process.exit(1);
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
return wanted;
|
|
775
|
+
};
|
|
776
|
+
|
|
777
|
+
/* ------------------------------------------------------------- passports */
|
|
778
|
+
|
|
779
|
+
/**
|
|
780
|
+
* Regenerate or verify passports for the components copied into this project.
|
|
781
|
+
*
|
|
782
|
+
* Reads the consumer's own source, so a modified component gets a passport that
|
|
783
|
+
* describes the modification. `passport.local.json` is read as an overlay and
|
|
784
|
+
* never written.
|
|
785
|
+
*/
|
|
786
|
+
const runPassport = async ({ check: checkOnly, dest: requestedDest }) => {
|
|
787
|
+
const dest = resolveDestPath(requestedDest);
|
|
788
|
+
|
|
789
|
+
if (!fs.existsSync(dest)) {
|
|
790
|
+
console.error(
|
|
791
|
+
`No ${path.basename(dest)}/ folder here. Install a component first:` +
|
|
792
|
+
`${NL} npx @grampro/kit add Button`,
|
|
793
|
+
);
|
|
794
|
+
process.exit(1);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
let passport;
|
|
798
|
+
try {
|
|
799
|
+
passport = require("./tools/passport/index.cjs");
|
|
800
|
+
} catch {
|
|
801
|
+
console.error(
|
|
802
|
+
"Passport tooling is not available in this installation of the CLI.",
|
|
803
|
+
);
|
|
804
|
+
process.exit(1);
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
const pkgPath = path.join(process.cwd(), "package.json");
|
|
808
|
+
const libraryVersion = fs.existsSync(pkgPath)
|
|
809
|
+
? readJson(pkgPath, {}).version || "unknown"
|
|
810
|
+
: "unknown";
|
|
811
|
+
|
|
812
|
+
const options = {
|
|
813
|
+
libRoot: dest,
|
|
814
|
+
repoRoot: process.cwd(),
|
|
815
|
+
libraryVersion,
|
|
816
|
+
includeLocal: true,
|
|
817
|
+
coverageThreshold: undefined,
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
try {
|
|
821
|
+
if (checkOnly) {
|
|
822
|
+
const { report, errors, warnings } = passport.check(options);
|
|
823
|
+
for (const row of report) {
|
|
824
|
+
if (row.problems.length === 0) {
|
|
825
|
+
console.log(` ${row.component}: OK`);
|
|
826
|
+
continue;
|
|
827
|
+
}
|
|
828
|
+
for (const issue of row.problems) {
|
|
829
|
+
console.log(` ${issue.level === "error" ? "✗" : "!"} ${issue.component}: ${issue.message}`);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
console.log(`${NL}${report.length} component(s) · ${errors} error(s) · ${warnings} warning(s)`);
|
|
833
|
+
if (errors > 0) process.exit(1);
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
const { results, written } = passport.generate(options);
|
|
838
|
+
const errors = results.flatMap((r) => r.issues.filter((i) => i.level === "error"));
|
|
839
|
+
for (const issue of errors) console.error(` ✗ ${issue.component}: ${issue.message}`);
|
|
840
|
+
console.log(
|
|
841
|
+
`${NL}✓ ${results.length} passport(s) regenerated · ${written.length} changed`,
|
|
842
|
+
);
|
|
843
|
+
if (errors.length > 0) process.exit(1);
|
|
844
|
+
} catch (error) {
|
|
845
|
+
if (error.code === "ETYPESCRIPT_MISSING" || error.code === "EBADJSON") {
|
|
846
|
+
console.error(error.message);
|
|
847
|
+
process.exit(1);
|
|
848
|
+
}
|
|
849
|
+
throw error;
|
|
850
|
+
}
|
|
851
|
+
};
|
|
852
|
+
|
|
853
|
+
/** `-a skill` is accepted as well as `--skill`, since both read naturally. */
|
|
854
|
+
const isSkillWord = (value) =>
|
|
855
|
+
typeof value === "string" && value.trim().toLowerCase() === "skill";
|
|
856
|
+
|
|
857
|
+
/** The files a component needs beside it, which differ between the two sets. */
|
|
858
|
+
const copySupportFiles = async (destPath, beta, options) => {
|
|
859
|
+
if (beta) {
|
|
860
|
+
await installShared(destPath, options);
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
// The legacy components' common files. Beta components use none of them.
|
|
864
|
+
if (!fs.existsSync(path.join(destPath, "utils.ts"))) {
|
|
865
|
+
await copyCommonFiles(destPath);
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
|
|
869
|
+
/* -------------------------------------------------------------- components */
|
|
870
|
+
|
|
871
|
+
const checkComponentExists = (component, destPath, beta = false) => {
|
|
872
|
+
const componentPath = path.join(destPath, folderFor(component, beta));
|
|
873
|
+
return fs.existsSync(componentPath);
|
|
874
|
+
};
|
|
875
|
+
|
|
876
|
+
const copyComponent = async (component, destPath, beta = false) => {
|
|
877
|
+
try {
|
|
878
|
+
const componentSrc = path.join(
|
|
879
|
+
getSourcePath(beta),
|
|
880
|
+
folderFor(component, beta),
|
|
881
|
+
);
|
|
882
|
+
const componentDest = path.join(destPath, folderFor(component, beta));
|
|
883
|
+
|
|
884
|
+
if (!fs.existsSync(componentSrc)) {
|
|
885
|
+
throw new Error(
|
|
886
|
+
`Component ${component} not found in ${beta ? "beta " : ""}source directory.`,
|
|
887
|
+
);
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
// The whole folder travels, which includes passport.json and
|
|
891
|
+
// passport.manual.json. The passport is part of the component contract,
|
|
892
|
+
// not an add-on — a contract you opt into is not a contract.
|
|
893
|
+
await fs.copy(componentSrc, componentDest, {
|
|
894
|
+
overwrite: true,
|
|
895
|
+
filter: (source) => !isTestPath(path.relative(componentSrc, source)),
|
|
896
|
+
});
|
|
897
|
+
console.log(
|
|
898
|
+
`✓ Component ${component} installed successfully ${
|
|
899
|
+
component === "Grid"
|
|
900
|
+
? "This Version of Grid will be deprecated soon. Please Install The New Data Grid Component"
|
|
901
|
+
: ""
|
|
902
|
+
}`,
|
|
903
|
+
);
|
|
904
|
+
} catch (error) {
|
|
905
|
+
console.error(`Error installing component ${component}:`, error.message);
|
|
906
|
+
process.exit(1);
|
|
907
|
+
}
|
|
908
|
+
};
|
|
909
|
+
|
|
910
|
+
const installComponentWithDependencies = async (
|
|
911
|
+
component,
|
|
912
|
+
destPath,
|
|
913
|
+
beta = false,
|
|
914
|
+
) => {
|
|
915
|
+
// Get dependencies for the component
|
|
916
|
+
const dependencies = CONFIG.dependencies[component] || [];
|
|
917
|
+
const componentsToInstall = new Set([component, ...dependencies]);
|
|
918
|
+
|
|
919
|
+
// Check which components need to be installed
|
|
920
|
+
const pendingInstalls = beta
|
|
921
|
+
? Array.from(componentsToInstall)
|
|
922
|
+
: Array.from(componentsToInstall).filter(
|
|
923
|
+
(comp) => !checkComponentExists(comp, destPath, beta),
|
|
924
|
+
);
|
|
925
|
+
|
|
926
|
+
if (pendingInstalls.length === 0) {
|
|
927
|
+
console.log(
|
|
928
|
+
`✓ ${component} and all its dependencies are already installed.`,
|
|
929
|
+
);
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
// Install all pending components
|
|
934
|
+
for (const comp of pendingInstalls) {
|
|
935
|
+
await copyComponent(comp, destPath, beta);
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
if (dependencies.length > 0) {
|
|
939
|
+
console.log(`\nInstalled dependencies for ${component}:`);
|
|
940
|
+
dependencies.forEach((dep) => {
|
|
941
|
+
console.log(`- ${dep}`);
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
reportStyleBarrel(await writeStyleBarrel(destPath, beta));
|
|
946
|
+
|
|
947
|
+
console.log(`\nFor documentation visit: ${CONFIG.docs}`);
|
|
948
|
+
};
|
|
949
|
+
|
|
950
|
+
const installMultipleComponents = async (
|
|
951
|
+
components,
|
|
952
|
+
destPath,
|
|
953
|
+
beta = false,
|
|
954
|
+
) => {
|
|
955
|
+
const allComponentsToInstall = new Set();
|
|
956
|
+
|
|
957
|
+
// Collect all components and their dependencies
|
|
958
|
+
components.forEach((component) => {
|
|
959
|
+
const dependencies = CONFIG.dependencies[component] || [];
|
|
960
|
+
allComponentsToInstall.add(component);
|
|
961
|
+
dependencies.forEach((dep) => allComponentsToInstall.add(dep));
|
|
962
|
+
});
|
|
963
|
+
|
|
964
|
+
// Filter out already installed components
|
|
965
|
+
const pendingInstalls = beta
|
|
966
|
+
? Array.from(allComponentsToInstall)
|
|
967
|
+
: Array.from(allComponentsToInstall).filter(
|
|
968
|
+
(comp) => !checkComponentExists(comp, destPath, beta),
|
|
969
|
+
);
|
|
970
|
+
|
|
971
|
+
if (pendingInstalls.length === 0) {
|
|
972
|
+
console.log(
|
|
973
|
+
"✓ All selected components and their dependencies are already installed.",
|
|
974
|
+
);
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
console.log(`\nInstalling ${pendingInstalls.length} components...`);
|
|
979
|
+
|
|
980
|
+
// Install all pending components
|
|
981
|
+
for (const comp of pendingInstalls) {
|
|
982
|
+
await copyComponent(comp, destPath, beta);
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
// Show dependency information
|
|
986
|
+
const allDependencies = new Set();
|
|
987
|
+
components.forEach((component) => {
|
|
988
|
+
const dependencies = CONFIG.dependencies[component] || [];
|
|
989
|
+
dependencies.forEach((dep) => allDependencies.add(dep));
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
if (allDependencies.size > 0) {
|
|
993
|
+
console.log(`\nDependencies installed:`);
|
|
994
|
+
Array.from(allDependencies).forEach((dep) => {
|
|
995
|
+
console.log(`- ${dep}`);
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
reportStyleBarrel(await writeStyleBarrel(destPath, beta));
|
|
1000
|
+
|
|
1001
|
+
console.log(`\nFor documentation visit: ${CONFIG.docs}`);
|
|
1002
|
+
};
|
|
1003
|
+
|
|
1004
|
+
const interactiveComponentSelector = async (beta = false) => {
|
|
1005
|
+
return new Promise((resolve) => {
|
|
1006
|
+
const rl = readline.createInterface({
|
|
1007
|
+
input: process.stdin,
|
|
1008
|
+
output: process.stdout,
|
|
1009
|
+
});
|
|
1010
|
+
|
|
1011
|
+
let selectedComponents = new Set();
|
|
1012
|
+
let currentIndex = 0;
|
|
1013
|
+
|
|
1014
|
+
const renderMenu = () => {
|
|
1015
|
+
console.clear();
|
|
1016
|
+
console.log("🚀 Component Installer - Interactive Mode");
|
|
1017
|
+
console.log(
|
|
1018
|
+
"Use ↑/↓ arrow keys to navigate, SPACE to select/deselect, ENTER to install\n",
|
|
1019
|
+
);
|
|
1020
|
+
|
|
1021
|
+
const availableComponents = getAvailableComponents(beta);
|
|
1022
|
+
|
|
1023
|
+
availableComponents.forEach((component, index) => {
|
|
1024
|
+
const isSelected = selectedComponents.has(component);
|
|
1025
|
+
const isCurrentIndex = index === currentIndex;
|
|
1026
|
+
const deps = CONFIG.dependencies[component]
|
|
1027
|
+
? ` (requires: ${CONFIG.dependencies[component].join(", ")})`
|
|
1028
|
+
: "";
|
|
1029
|
+
|
|
1030
|
+
const prefix = isCurrentIndex ? ">" : " ";
|
|
1031
|
+
const checkbox = isSelected ? "☑" : "☐";
|
|
1032
|
+
const line = `${prefix} ${checkbox} ${component}${deps}`;
|
|
1033
|
+
|
|
1034
|
+
if (isCurrentIndex) {
|
|
1035
|
+
console.log(`\x1b[36m${line}\x1b[0m`); // Cyan highlight
|
|
1036
|
+
} else {
|
|
1037
|
+
console.log(line);
|
|
1038
|
+
}
|
|
1039
|
+
});
|
|
1040
|
+
|
|
1041
|
+
console.log(`\nSelected: ${selectedComponents.size} components`);
|
|
1042
|
+
console.log("Press ENTER to install selected components, or 'q' to quit");
|
|
1043
|
+
};
|
|
1044
|
+
|
|
1045
|
+
const handleKeyPress = (key) => {
|
|
1046
|
+
switch (key) {
|
|
1047
|
+
case "[A": // Up arrow
|
|
1048
|
+
currentIndex = Math.max(0, currentIndex - 1);
|
|
1049
|
+
renderMenu();
|
|
1050
|
+
break;
|
|
1051
|
+
case "[B": // Down arrow
|
|
1052
|
+
currentIndex = Math.min(
|
|
1053
|
+
getAvailableComponents(beta).length - 1,
|
|
1054
|
+
currentIndex + 1,
|
|
1055
|
+
);
|
|
1056
|
+
renderMenu();
|
|
1057
|
+
break;
|
|
1058
|
+
case " ": // Space bar
|
|
1059
|
+
const component = getAvailableComponents(beta)[currentIndex];
|
|
1060
|
+
if (selectedComponents.has(component)) {
|
|
1061
|
+
selectedComponents.delete(component);
|
|
1062
|
+
} else {
|
|
1063
|
+
selectedComponents.add(component);
|
|
1064
|
+
}
|
|
1065
|
+
renderMenu();
|
|
1066
|
+
break;
|
|
1067
|
+
case "\r": // Enter
|
|
1068
|
+
if (selectedComponents.size > 0) {
|
|
1069
|
+
rl.close();
|
|
1070
|
+
resolve(Array.from(selectedComponents));
|
|
1071
|
+
}
|
|
1072
|
+
break;
|
|
1073
|
+
case "q":
|
|
1074
|
+
rl.close();
|
|
1075
|
+
resolve([]);
|
|
1076
|
+
break;
|
|
1077
|
+
}
|
|
1078
|
+
};
|
|
1079
|
+
|
|
1080
|
+
// Enable raw mode to capture arrow keys
|
|
1081
|
+
process.stdin.setRawMode(true);
|
|
1082
|
+
process.stdin.resume();
|
|
1083
|
+
process.stdin.setEncoding("utf8");
|
|
1084
|
+
process.stdin.on("data", handleKeyPress);
|
|
1085
|
+
|
|
1086
|
+
renderMenu();
|
|
1087
|
+
|
|
1088
|
+
rl.on("close", () => {
|
|
1089
|
+
process.stdin.setRawMode(false);
|
|
1090
|
+
process.stdin.pause();
|
|
1091
|
+
});
|
|
1092
|
+
});
|
|
1093
|
+
};
|
|
1094
|
+
|
|
1095
|
+
const parseMultipleComponents = (componentString) => {
|
|
1096
|
+
return componentString
|
|
1097
|
+
.split(",")
|
|
1098
|
+
.map((comp) => comp.trim())
|
|
1099
|
+
.filter((comp) => comp.length > 0);
|
|
1100
|
+
};
|
|
1101
|
+
|
|
1102
|
+
const validateComponents = (components, beta = false) => {
|
|
1103
|
+
const availableComponents = getAvailableComponents(beta);
|
|
1104
|
+
const invalidComponents = components.filter(
|
|
1105
|
+
(comp) => !availableComponents.includes(comp),
|
|
1106
|
+
);
|
|
1107
|
+
if (invalidComponents.length > 0) {
|
|
1108
|
+
if (invalidComponents.some((comp) => comp.toLowerCase() === SHARED_DIR)) {
|
|
1109
|
+
console.error(
|
|
1110
|
+
"`shared` is not a component: it is installed automatically with any beta component.",
|
|
1111
|
+
);
|
|
1112
|
+
}
|
|
1113
|
+
console.error(`Invalid components: ${invalidComponents.join(", ")}`);
|
|
1114
|
+
console.log(`\nAvailable ${beta ? "" : "1.x "}components:`);
|
|
1115
|
+
availableComponents.forEach((comp) => console.log(`- ${comp}`));
|
|
1116
|
+
if (!beta) {
|
|
1117
|
+
console.log("\nRedesigned beta components (install with -beta):");
|
|
1118
|
+
CONFIG.betaComponents.forEach((comp) => console.log(`- ${comp}`));
|
|
1119
|
+
}
|
|
1120
|
+
return false;
|
|
1121
|
+
}
|
|
1122
|
+
return true;
|
|
1123
|
+
};
|
|
1124
|
+
|
|
1125
|
+
/*
|
|
1126
|
+
* The verb-first grammar, and the old flag grammar it replaced.
|
|
1127
|
+
*
|
|
1128
|
+
* `add`, `list`, `skill` and `passport` are positional commands. The previous
|
|
1129
|
+
* flags (`-a Button -beta`, `-skill`, `-passport`) are still accepted and still
|
|
1130
|
+
* do the right thing — they just print the new spelling once. Hundreds of
|
|
1131
|
+
* projects a week install this; breaking their scripts to tidy an argument
|
|
1132
|
+
* parser is not a trade worth making.
|
|
1133
|
+
*/
|
|
1134
|
+
const COMMANDS = new Set(["add", "list", "skill", "passport"]);
|
|
1135
|
+
|
|
1136
|
+
/**
|
|
1137
|
+
* Flags that take a separate value, so the token after one is that value and
|
|
1138
|
+
* not a component name. `--flag=value` is a single token and needs no entry.
|
|
1139
|
+
*/
|
|
1140
|
+
const VALUE_FLAGS = new Set(["--dest", "--for", "--add", "-a"]);
|
|
1141
|
+
|
|
1142
|
+
/** Reads a verb-first invocation, or null when this is the old flag style. */
|
|
1143
|
+
const readCommand = (argv) => {
|
|
1144
|
+
const [first, ...rest] = argv;
|
|
1145
|
+
if (!first || !COMMANDS.has(first)) return null;
|
|
1146
|
+
|
|
1147
|
+
const operands = [];
|
|
1148
|
+
for (let i = 0; i < rest.length; i += 1) {
|
|
1149
|
+
if (VALUE_FLAGS.has(rest[i])) {
|
|
1150
|
+
i += 1;
|
|
1151
|
+
continue;
|
|
1152
|
+
}
|
|
1153
|
+
if (!rest[i].startsWith("-")) operands.push(rest[i]);
|
|
1154
|
+
}
|
|
1155
|
+
return { command: first, operands };
|
|
1156
|
+
};
|
|
1157
|
+
|
|
1158
|
+
const main = async () => {
|
|
1159
|
+
const rawArgs = hideBin(process.argv);
|
|
1160
|
+
const parsed = readCommand(rawArgs);
|
|
1161
|
+
|
|
1162
|
+
const args = rawArgs.map((arg) => {
|
|
1163
|
+
// The old single-dash spellings, which yargs would otherwise read as a
|
|
1164
|
+
// bundle of short flags (`-beta` as -b -e -t -a).
|
|
1165
|
+
if (arg === "-beta") return "--beta";
|
|
1166
|
+
if (arg === "-skill") return "--skill";
|
|
1167
|
+
if (arg === "-passport") return "--passport";
|
|
1168
|
+
if (arg === "-legacy") return "--legacy";
|
|
1169
|
+
return arg;
|
|
1170
|
+
});
|
|
1171
|
+
|
|
1172
|
+
const argv = yargs(args)
|
|
1173
|
+
.usage("$0 <command> [options]")
|
|
1174
|
+
.option("legacy", {
|
|
1175
|
+
describe: "Install from the 1.x component set instead of the current one",
|
|
1176
|
+
type: "boolean",
|
|
1177
|
+
default: false,
|
|
1178
|
+
})
|
|
1179
|
+
.option("add", {
|
|
1180
|
+
alias: "a",
|
|
1181
|
+
describe: "Deprecated. Use: add <Component> [more...]",
|
|
1182
|
+
type: "string",
|
|
1183
|
+
})
|
|
1184
|
+
.option("interactive", {
|
|
1185
|
+
alias: "i",
|
|
1186
|
+
describe: "Interactive component selection mode",
|
|
1187
|
+
type: "boolean",
|
|
1188
|
+
})
|
|
1189
|
+
.option("list", {
|
|
1190
|
+
alias: "l",
|
|
1191
|
+
describe: "Deprecated. Use: list",
|
|
1192
|
+
type: "boolean",
|
|
1193
|
+
})
|
|
1194
|
+
.option("beta", {
|
|
1195
|
+
describe: "Deprecated and ignored: these components are the default now",
|
|
1196
|
+
type: "boolean",
|
|
1197
|
+
default: false,
|
|
1198
|
+
hidden: true,
|
|
1199
|
+
})
|
|
1200
|
+
.option("skill", {
|
|
1201
|
+
describe: "Deprecated. Use: skill",
|
|
1202
|
+
type: "boolean",
|
|
1203
|
+
default: false,
|
|
1204
|
+
})
|
|
1205
|
+
.option("for", {
|
|
1206
|
+
describe:
|
|
1207
|
+
"With skill: which agents to write adapters for (claude, codex, antigravity, or none)",
|
|
1208
|
+
type: "string",
|
|
1209
|
+
})
|
|
1210
|
+
.option("passport", {
|
|
1211
|
+
describe: "Deprecated. Use: passport",
|
|
1212
|
+
type: "boolean",
|
|
1213
|
+
default: false,
|
|
1214
|
+
})
|
|
1215
|
+
.option("check", {
|
|
1216
|
+
describe: "With passport: verify passports match the source; writes nothing",
|
|
1217
|
+
type: "boolean",
|
|
1218
|
+
default: false,
|
|
1219
|
+
})
|
|
1220
|
+
.option("dest", {
|
|
1221
|
+
describe: `Where to put the components (default: ${COMPONENT_DIR}/, or src/${COMPONENT_DIR}/ in a project with src/)`,
|
|
1222
|
+
type: "string",
|
|
1223
|
+
})
|
|
1224
|
+
.option("force", {
|
|
1225
|
+
describe: "Replace files you have edited locally in shared/ or the skill",
|
|
1226
|
+
type: "boolean",
|
|
1227
|
+
default: false,
|
|
1228
|
+
})
|
|
1229
|
+
.example("$0 add Button", "Install one component")
|
|
1230
|
+
.example("$0 add DataGrid Button Modal", "Install several")
|
|
1231
|
+
.example("$0 list", "List available components")
|
|
1232
|
+
.example("$0 skill", "Install the agent skill for every supported agent")
|
|
1233
|
+
.example("$0 skill --for claude", "Only the Claude Code copy")
|
|
1234
|
+
.example("$0 passport --check", "Fail if a passport is stale (CI)")
|
|
1235
|
+
.example("$0 add Select --legacy", "Install from the 1.x set")
|
|
1236
|
+
.example("$0 add Button --dest app/ui", "Choose where they go")
|
|
1237
|
+
.example("$0 -i", "Interactive selection")
|
|
1238
|
+
.help().argv;
|
|
1239
|
+
|
|
1240
|
+
/*
|
|
1241
|
+
* The 2.x components are what `add` installs. `--legacy` reaches the 1.x set,
|
|
1242
|
+
* which is where the old `--beta` flag's meaning went: it used to opt *in* to
|
|
1243
|
+
* these, and now there is nothing to opt into.
|
|
1244
|
+
*/
|
|
1245
|
+
const beta = !argv.legacy;
|
|
1246
|
+
|
|
1247
|
+
if (argv["beta"] === true) {
|
|
1248
|
+
console.log(
|
|
1249
|
+
"Note: --beta is no longer needed; these components are the default. " +
|
|
1250
|
+
"Drop the flag.",
|
|
1251
|
+
);
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
if (parsed) {
|
|
1255
|
+
if (parsed.command === "list") argv.list = true;
|
|
1256
|
+
if (parsed.command === "skill") argv.skill = true;
|
|
1257
|
+
if (parsed.command === "passport") argv.passport = true;
|
|
1258
|
+
if (parsed.command === "add") {
|
|
1259
|
+
if (parsed.operands.length === 0) {
|
|
1260
|
+
console.log(`Which component? Try: ${CLI_NAME} add Button (or: ${CLI_NAME} list)`);
|
|
1261
|
+
return;
|
|
1262
|
+
}
|
|
1263
|
+
argv.add = parsed.operands.join(",");
|
|
1264
|
+
}
|
|
1265
|
+
} else if (argv.add || argv.list || argv.skill || argv.passport) {
|
|
1266
|
+
console.log(`Tip: the shorter form is \`${CLI_NAME} add <Component>\`.`);
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
// List components if requested
|
|
1270
|
+
if (argv.list) {
|
|
1271
|
+
const availableComponents = getAvailableComponents(beta);
|
|
1272
|
+
console.log(`\nAvailable ${beta ? "" : "1.x "}components:`);
|
|
1273
|
+
availableComponents.forEach((comp) => {
|
|
1274
|
+
const deps = CONFIG.dependencies[comp]
|
|
1275
|
+
? ` (requires: ${CONFIG.dependencies[comp].join(", ")})`
|
|
1276
|
+
: "";
|
|
1277
|
+
console.log(`- ${comp}${deps}`);
|
|
1278
|
+
});
|
|
1279
|
+
if (beta) {
|
|
1280
|
+
console.log(
|
|
1281
|
+
`\nEvery beta component also installs component-lib/${SHARED_DIR}, which they all import.`,
|
|
1282
|
+
);
|
|
1283
|
+
console.log(
|
|
1284
|
+
`${NL}The agent skill installs separately, at the project root, for ${SKILL_TARGETS.join(", ")} and the GBS agent:${NL} npx @grampro/kit skill`,
|
|
1285
|
+
);
|
|
1286
|
+
} else {
|
|
1287
|
+
console.log("\nRedesigned beta components (install with -beta):");
|
|
1288
|
+
CONFIG.betaComponents.forEach((comp) => console.log(`- ${comp}`));
|
|
1289
|
+
}
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
// Interactive mode
|
|
1294
|
+
if (argv.interactive) {
|
|
1295
|
+
console.log("Starting interactive component selector...\n");
|
|
1296
|
+
const selectedComponents = await interactiveComponentSelector(beta);
|
|
1297
|
+
|
|
1298
|
+
if (selectedComponents.length === 0) {
|
|
1299
|
+
console.log("No components selected. Exiting...");
|
|
1300
|
+
return;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
const destPath = resolveDestPath(argv.dest);
|
|
1304
|
+
await fs.ensureDir(destPath);
|
|
1305
|
+
|
|
1306
|
+
await copySupportFiles(destPath, beta, { force: argv.force });
|
|
1307
|
+
|
|
1308
|
+
// Install selected components
|
|
1309
|
+
await installMultipleComponents(selectedComponents, destPath, beta);
|
|
1310
|
+
return;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
// Passports are regenerated in place against the project's own copied
|
|
1314
|
+
// source, so a fork's passport describes the fork.
|
|
1315
|
+
if (argv.passport) {
|
|
1316
|
+
await runPassport({ check: argv.check, dest: argv.dest });
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
// The skill goes to the project root, not component-lib, so it is handled
|
|
1321
|
+
// before anything reaches the component installer.
|
|
1322
|
+
if (argv.skill || isSkillWord(argv.add)) {
|
|
1323
|
+
await installSkill(process.cwd(), {
|
|
1324
|
+
force: argv.force,
|
|
1325
|
+
targets: parseSkillTargets(argv.for),
|
|
1326
|
+
});
|
|
1327
|
+
|
|
1328
|
+
// `-a --skill` leaves `add` empty, and `-a skill` names no component.
|
|
1329
|
+
if (!argv.add || isSkillWord(argv.add)) return;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
if (!argv.add) {
|
|
1333
|
+
console.error(
|
|
1334
|
+
"Please specify a component to install using -a/--add, install the agent skill with --skill, use -i/--interactive for interactive mode, or -l/--list to see available components",
|
|
1335
|
+
);
|
|
1336
|
+
process.exit(1);
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
// Parse components (single or multiple)
|
|
1340
|
+
const componentInput = argv.add;
|
|
1341
|
+
const components = parseMultipleComponents(componentInput).map(
|
|
1342
|
+
(component) =>
|
|
1343
|
+
normalizeComponent(component, getAvailableComponents(beta)) ||
|
|
1344
|
+
component,
|
|
1345
|
+
);
|
|
1346
|
+
|
|
1347
|
+
// Validate all components
|
|
1348
|
+
if (!validateComponents(components, beta)) {
|
|
1349
|
+
process.exit(1);
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
const destPath = resolveDestPath(argv.dest);
|
|
1353
|
+
await fs.ensureDir(destPath);
|
|
1354
|
+
|
|
1355
|
+
await copySupportFiles(destPath, beta, { force: argv.force });
|
|
1356
|
+
|
|
1357
|
+
// Install components
|
|
1358
|
+
if (components.length === 1) {
|
|
1359
|
+
// Single component installation (existing behavior)
|
|
1360
|
+
await installComponentWithDependencies(components[0], destPath, beta);
|
|
1361
|
+
} else {
|
|
1362
|
+
// Multiple components installation
|
|
1363
|
+
await installMultipleComponents(components, destPath, beta);
|
|
1364
|
+
}
|
|
1365
|
+
};
|
|
1366
|
+
|
|
1367
|
+
main().catch((error) => {
|
|
1368
|
+
console.error("Error:", error.message);
|
|
1369
|
+
process.exit(1);
|
|
1370
|
+
});
|