@hiver/skills 1.0.2 → 1.0.4

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.
@@ -36,8 +36,8 @@ Help the user to grill down on a problem to its core discussing all sorts of the
36
36
 
37
37
  1. Take input from the user related to the problem they want to discuss or grill down
38
38
  2. If the problem is related to building a new feature, check with the user if prototype designs are available
39
- 3. If Figma designs are available, use the Figma MCP tool to get the prototype details and analyze them
40
- 4. Keep asking if the user has more Figma designs available until all designs are provided; keep using the Figma MCP tool to load them into memory
39
+ 3. If Figma designs are available, use the claude Figma desktop MCP tool to get the prototype details and analyze them
40
+ 4. Keep asking if the user has more Figma designs available until all designs are provided; keep using the claude Figma desktop MCP tool to load them into memory
41
41
  5. Analyze the problem statement in context of the current codebase — use read and search tools to explore, never write tools
42
42
  6. Ask the right questions until you are clear on the problem statement
43
43
  7. Go through the codebase and figure out how the problem can be solved — discussion only
@@ -1,8 +1,81 @@
1
1
  # Icons
2
2
 
3
- - **Use MUI icons only** - import from `@mui/icons-material` or `@hiver/hiver-ui-kit` icons
4
- - **For SVG icons**: Import SVG files directly and use them in `<img>` tags
5
- - Import SVG files: `import iconName from 'path/to/icon.svg'`
6
- - Use in component: `<img src={iconName} alt="description" />`
7
- - Example: `import AILogo from '../assets/ai_logo.svg'` then `<img src={AILogo} alt="AI Copilot" />`
8
- - **Do not import icons from other packages** (no FontAwesome, Heroicons, etc.)
3
+ How to import from `@hiver/hiver-ui-kit`:
4
+
5
+ ```js
6
+ import { brandIcons, icons } from '@hiver/hiver-ui-kit';
7
+ // <brandIcons.Star />
8
+ // <icons.AccountCircle />
9
+ ```
10
+
11
+ ## Resolution priority
12
+
13
+ When a Figma design contains an icon, resolve it in this order:
14
+
15
+ 1. **User-provided SVG asset** — if the user supplied an `.svg` file, import it directly:
16
+ ```js
17
+ import iconName from 'path/to/icon.svg';
18
+ <img src={iconName} alt="description" />
19
+ ```
20
+ 2. **`brandIcons`** — Hiver-designed icons (see "Brand icon catalog" below).
21
+ 3. **`icons`** — curated MUI icons re-exported from hui-kit (see "MUI icon catalog" below).
22
+ 4. **`@mui/icons-material`** — **last resort only**, when the icon is not in the design system **and** the user has not provided an asset.
23
+ 5. **Never** import icons from FontAwesome, Heroicons, or any other third-party icon library.
24
+
25
+ ## How to match a Figma icon to a code name
26
+
27
+ 1. **Prefer the Figma layer/component name** if the design exposes one (e.g. a layer named `Brand/Star` → `brandIcons.Star`).
28
+ 2. **Otherwise, match visually** against the catalog sheets embedded below. Each icon is rendered with its exact export name underneath.
29
+ 3. The export name is what you use in code — including any typos preserved from the design system (see Notes).
30
+
31
+ ## Brand icon catalog (`brandIcons`)
32
+
33
+ ![Brand icons catalog](./icons/brand-icons.png)
34
+
35
+ **Hiver Logo**
36
+ - `Logo`
37
+
38
+ **Brand icons** (box 20×20, live area 18×18, stroke 1.4)
39
+ - `SharedMailbox`, `Analytics`, `EmailTemplates`, `Settings`, `HiverAlerts`, `Activity`, `AutoAssignment`, `Workload`, `Lable`, `Permalink`
40
+ - `Star`, `Edit`, `Filters`, `HiverChat`, `AircallPhone`, `Aavatar`, `Unassigned`, `Mine`, `Team`, `Pending`
41
+ - `Closed`, `Tags`, `Crown`, `Dashboard`, `DashboardInactive`, `SharedInboxNav`, `SharedInboxNavInactive`, `SharedLabel`, `SharedLabelInactive`, `KnowledgeBase`
42
+ - `KnowledgeBaseInactive`, `Connector`, `ConnectorInactive`, `SettingsNav`, `SettingsNavInactive`, `UserRole`, `UserRoleInactive`
43
+
44
+ **Activity icons** (box 20×20, live area 16×16, stroke 1.4)
45
+ - `Status`, `Tag`, `Inbox`, `AlertTriangle`, `EnvelopeDownload`, `ActivityStar`, `EnvelopeUpload`, `Envelope`, `ActivityEdit`, `CloseCircle`, `CheckAction`, `UserCheck`
46
+
47
+ **Settings icons** (box 24×24, live area 22×22, stroke 1.4)
48
+ - `AdminPanel`, `Sliders`, `Compass`, `SwitchAccount`, `Gift`, `MessageCircle`, `LifeBuoy`
49
+
50
+ ## MUI icon catalog (`icons`)
51
+
52
+ ![MUI icons catalog](./icons/mui-icons.png)
53
+
54
+ - `AccountCircle`, `Add`, `Alarm`, `Close`, `Phone`, `ArchiveOutlined`, `ArrowBack`, `ArrowBackIos`, `ArrowDropDown`, `ArrowDropUp`, `ArrowForward`
55
+ - `ArrowOutward`, `CalendarToday`, `CalendarTodayOutlined`, `CalendarTodayOutlinedIcon`, `Cancel`, `CancelOutlined`, `CheckCircle`, `ChevronRightOutlined`, `CodeSharp`, `DeleteOutline`, `DownloadOutlined`
56
+ - `DragIndicator`, `EditOutlined`, `Error`, `ErrorOutline`, `GroupsOutlined`, `InfoOutlined`, `LabelOutlined`, `Link`, `LockOutlined`, `MailOutlined`, `MicNone`
57
+ - `MoreVert`, `NotificationsOutlined`, `OpenInNew`, `Pause`, `PersonOutline`, `PersonOutlined`, `PlayArrow`, `Public`, `Search`, `Settings`, `SettingsOutlined`
58
+ - `StickyNote2Outlined`, `ThumbDownAlt`, `ThumbDownOffAlt`, `ThumbUpAlt`, `ThumbUpOffAlt`, `TimerOutlined`, `TrackChanges`, `UnarchiveOutlined`, `Voicemail`, `Warning`, `WarningAmber`
59
+ - `ArrowRightAlt`, `Delete`, `Email`, `Favorite`
60
+
61
+ ## Notes — Figma → code gotchas
62
+
63
+ - **Typos preserved in exports** — these match the Figma names, so map literally; do not auto-correct:
64
+ - `Lable` (not `Label`)
65
+ - `Aavatar` (not `Avatar`)
66
+ - **`Edit` collision** — three similar icons:
67
+ - `brandIcons.Edit` — pen-on-paper brand icon (yellow accent)
68
+ - `brandIcons.ActivityEdit` — green envelope with a pen, activity style
69
+ - `icons.EditOutlined` — plain MUI outlined pencil
70
+ - **`Settings` collision** — five similar names:
71
+ - `brandIcons.Settings`, `brandIcons.SettingsNav`, `brandIcons.SettingsNavInactive`
72
+ - `icons.Settings`, `icons.SettingsOutlined`
73
+ Pick by Figma category section and visual.
74
+ - **`Star` collision**:
75
+ - `brandIcons.Star` — yellow filled brand star
76
+ - `brandIcons.ActivityStar` — purple activity star
77
+ - **`Tag` vs `Tags`**:
78
+ - `brandIcons.Tags` — brand (plural, box 20×20 live 18×18)
79
+ - `brandIcons.Tag` — activity (singular, box 20×20 live 16×16)
80
+ - **Inactive variants** — many brand icons have `*Inactive` siblings (`DashboardInactive`, `SharedInboxNavInactive`, `SharedLabelInactive`, `KnowledgeBaseInactive`, `ConnectorInactive`, `SettingsNavInactive`, `UserRoleInactive`). Use the inactive variant for the unselected/disabled state of a nav item.
81
+ - **`UserCheck`** — present in `brandIcons` exports but not visible on the current Figma sheet. Use it if a design clearly needs it; otherwise prefer the activity-style icons documented above.
@@ -1,6 +1,10 @@
1
1
  # Typography reference (`@hiver/hiver-ui-kit`)
2
2
 
3
- Map Figma text styles to `<Typography variant="...">`. Font family is **Open Sans**. Match by fontSize + fontWeight (regular 400, medium 500, semiBold 600, bold 700).
3
+ Map Figma text styles to `<Typography variant="...">`. Match by fontSize + fontWeight (regular 400, medium 500, semiBold 600, bold 700).
4
+
5
+ **Font families:**
6
+ - Legacy variants (`h1_*`–`h5_*`, `body0_5_*`–`body4_*`, `caption1_*`, `caption2_*`, `overline_medium`, `overline_semiBold`, `buttonLarge`/`buttonMedium`/`buttonSmall`, `chip`) use **Open Sans**.
7
+ - 2026 tokens (`body_lg_*`–`body_xs_*`, `heading_lg_*`–`heading_sm_*`, `display_lg_*`/`display_md_*`, `caption_regular`/`caption_medium`, `overline_regular`) use **Inter**.
4
8
 
5
9
  ## Heading variants
6
10
 
@@ -68,12 +72,68 @@ Map Figma text styles to `<Typography variant="...">`. Font family is **Open San
68
72
  | `buttonSmall` | 12 | 500 | 20px | 0.4px |
69
73
  | `chip` | 12 | 500 | 12px | 0.4px |
70
74
 
75
+ ## Typography 2026 tokens (Inter)
76
+
77
+ Canonical 2026 design tokens. Use these for new UI built against the 2026 design system. Font family is **Inter**.
78
+
79
+ ### Display
80
+
81
+ | Variant | fontSize | fontWeight | lineHeight | letterSpacing |
82
+ |---------|----------|------------|------------|---------------|
83
+ | `display_lg_semiBold` | 28 | 600 | 36px | 0.01em |
84
+ | `display_lg_bold` | 28 | 700 | 36px | 0.01em |
85
+ | `display_md_semiBold` | 24 | 600 | 32px | 0.01em |
86
+ | `display_md_bold` | 24 | 700 | 32px | 0.01em |
87
+
88
+ ### Heading
89
+
90
+ | Variant | fontSize | fontWeight | lineHeight | letterSpacing |
91
+ |---------|----------|------------|------------|---------------|
92
+ | `heading_lg_medium` | 20 | 500 | 28px | -0.005em |
93
+ | `heading_lg_semiBold` | 20 | 600 | 28px | -0.005em |
94
+ | `heading_md_medium` | 18 | 500 | 28px | -0.005em |
95
+ | `heading_md_semiBold` | 18 | 600 | 28px | -0.005em |
96
+ | `heading_sm_medium` | 16 | 500 | 22px | -0.005em |
97
+ | `heading_sm_semiBold` | 16 | 600 | 22px | -0.005em |
98
+
99
+ ### Body
100
+
101
+ | Variant | fontSize | fontWeight | lineHeight | letterSpacing |
102
+ |---------|----------|------------|------------|---------------|
103
+ | `body_lg_regular` | 16 | 400 | 24px | -0.005em |
104
+ | `body_lg_medium` | 16 | 500 | 24px | -0.005em |
105
+ | `body_lg_semiBold` | 16 | 600 | 24px | -0.005em |
106
+ | `body_md_regular` | 14 | 400 | 20px | -0.005em |
107
+ | `body_md_medium` | 14 | 500 | 20px | -0.005em |
108
+ | `body_md_semiBold` | 14 | 600 | 20px | -0.005em |
109
+ | `body_sm_regular` | 13 | 400 | 19px | -0.005em |
110
+ | `body_sm_medium` | 13 | 500 | 19px | -0.005em |
111
+ | `body_sm_semiBold` | 13 | 600 | 19px | -0.005em |
112
+ | `body_xs_regular` | 12 | 400 | 18px | -0.005em |
113
+ | `body_xs_medium` | 12 | 500 | 18px | -0.005em |
114
+ | `body_xs_semiBold` | 12 | 600 | 18px | -0.005em |
115
+
116
+ ### Caption & overline (2026)
117
+
118
+ | Variant | fontSize | fontWeight | lineHeight | letterSpacing | Notes |
119
+ |---------|----------|------------|------------|---------------|-------|
120
+ | `caption_regular` | 12 | 400 | 14px | 0.01em | |
121
+ | `caption_medium` | 12 | 500 | 14px | 0.01em | |
122
+ | `overline_regular` | 12 | 400 | 14px | 0.01em | UPPERCASE |
123
+
124
+ > Note: a 2026 `overline_medium` token also exists in the theme (Inter, 12 / 500 / 14px / 0.01em / UPPERCASE) and shares its name with the legacy Open Sans `overline_medium` (11 / 500 / 18px). The 2026 spread is applied after the legacy block, so the Inter version wins at runtime — use accordingly.
125
+
71
126
  ## Usage
72
127
 
73
128
  ```jsx
74
129
  <Typography variant="h2_medium">Section title</Typography>
75
130
  <Typography variant="body1_regular">Body text</Typography>
76
131
  <Typography variant="caption1_medium" color="text.secondary">Caption</Typography>
132
+
133
+ // 2026 tokens
134
+ <Typography variant="heading_lg_semiBold">Page title</Typography>
135
+ <Typography variant="body_md_regular">Body text</Typography>
136
+ <Typography variant="display_lg_bold">Hero</Typography>
77
137
  ```
78
138
 
79
- **Figma → variant:** Compare Figma text style fontSize and weight (Regular/Medium/Semi Bold/Bold) to the tables above and pick the closest match.
139
+ **Figma → variant:** Compare Figma text style fontSize and weight (Regular/Medium/Semi Bold/Bold) to the tables above and pick the closest match. For new 2026 designs, prefer the Inter tokens.
@@ -1,8 +1,81 @@
1
1
  # Icons
2
2
 
3
- - **Use MUI icons only** - import from `@mui/icons-material` or `@hiver/hiver-ui-kit` icons
4
- - **For SVG icons**: Import SVG files directly and use them in `<img>` tags
5
- - Import SVG files: `import iconName from 'path/to/icon.svg'`
6
- - Use in component: `<img src={iconName} alt="description" />`
7
- - Example: `import AILogo from '../assets/ai_logo.svg'` then `<img src={AILogo} alt="AI Copilot" />`
8
- - **Do not import icons from other packages** (no FontAwesome, Heroicons, etc.)
3
+ How to import from `@hiver/hiver-ui-kit`:
4
+
5
+ ```js
6
+ import { brandIcons, icons } from '@hiver/hiver-ui-kit';
7
+ // <brandIcons.Star />
8
+ // <icons.AccountCircle />
9
+ ```
10
+
11
+ ## Resolution priority
12
+
13
+ When a Figma design contains an icon, resolve it in this order:
14
+
15
+ 1. **User-provided SVG asset** — if the user supplied an `.svg` file, import it directly:
16
+ ```js
17
+ import iconName from 'path/to/icon.svg';
18
+ <img src={iconName} alt="description" />
19
+ ```
20
+ 2. **`brandIcons`** — Hiver-designed icons (see "Brand icon catalog" below).
21
+ 3. **`icons`** — curated MUI icons re-exported from hui-kit (see "MUI icon catalog" below).
22
+ 4. **`@mui/icons-material`** — **last resort only**, when the icon is not in the design system **and** the user has not provided an asset.
23
+ 5. **Never** import icons from FontAwesome, Heroicons, or any other third-party icon library.
24
+
25
+ ## How to match a Figma icon to a code name
26
+
27
+ 1. **Prefer the Figma layer/component name** if the design exposes one (e.g. a layer named `Brand/Star` → `brandIcons.Star`).
28
+ 2. **Otherwise, match visually** against the catalog sheets embedded below. Each icon is rendered with its exact export name underneath.
29
+ 3. The export name is what you use in code — including any typos preserved from the design system (see Notes).
30
+
31
+ ## Brand icon catalog (`brandIcons`)
32
+
33
+ ![Brand icons catalog](./icons/brand-icons.png)
34
+
35
+ **Hiver Logo**
36
+ - `Logo`
37
+
38
+ **Brand icons** (box 20×20, live area 18×18, stroke 1.4)
39
+ - `SharedMailbox`, `Analytics`, `EmailTemplates`, `Settings`, `HiverAlerts`, `Activity`, `AutoAssignment`, `Workload`, `Lable`, `Permalink`
40
+ - `Star`, `Edit`, `Filters`, `HiverChat`, `AircallPhone`, `Aavatar`, `Unassigned`, `Mine`, `Team`, `Pending`
41
+ - `Closed`, `Tags`, `Crown`, `Dashboard`, `DashboardInactive`, `SharedInboxNav`, `SharedInboxNavInactive`, `SharedLabel`, `SharedLabelInactive`, `KnowledgeBase`
42
+ - `KnowledgeBaseInactive`, `Connector`, `ConnectorInactive`, `SettingsNav`, `SettingsNavInactive`, `UserRole`, `UserRoleInactive`
43
+
44
+ **Activity icons** (box 20×20, live area 16×16, stroke 1.4)
45
+ - `Status`, `Tag`, `Inbox`, `AlertTriangle`, `EnvelopeDownload`, `ActivityStar`, `EnvelopeUpload`, `Envelope`, `ActivityEdit`, `CloseCircle`, `CheckAction`, `UserCheck`
46
+
47
+ **Settings icons** (box 24×24, live area 22×22, stroke 1.4)
48
+ - `AdminPanel`, `Sliders`, `Compass`, `SwitchAccount`, `Gift`, `MessageCircle`, `LifeBuoy`
49
+
50
+ ## MUI icon catalog (`icons`)
51
+
52
+ ![MUI icons catalog](./icons/mui-icons.png)
53
+
54
+ - `AccountCircle`, `Add`, `Alarm`, `Close`, `Phone`, `ArchiveOutlined`, `ArrowBack`, `ArrowBackIos`, `ArrowDropDown`, `ArrowDropUp`, `ArrowForward`
55
+ - `ArrowOutward`, `CalendarToday`, `CalendarTodayOutlined`, `CalendarTodayOutlinedIcon`, `Cancel`, `CancelOutlined`, `CheckCircle`, `ChevronRightOutlined`, `CodeSharp`, `DeleteOutline`, `DownloadOutlined`
56
+ - `DragIndicator`, `EditOutlined`, `Error`, `ErrorOutline`, `GroupsOutlined`, `InfoOutlined`, `LabelOutlined`, `Link`, `LockOutlined`, `MailOutlined`, `MicNone`
57
+ - `MoreVert`, `NotificationsOutlined`, `OpenInNew`, `Pause`, `PersonOutline`, `PersonOutlined`, `PlayArrow`, `Public`, `Search`, `Settings`, `SettingsOutlined`
58
+ - `StickyNote2Outlined`, `ThumbDownAlt`, `ThumbDownOffAlt`, `ThumbUpAlt`, `ThumbUpOffAlt`, `TimerOutlined`, `TrackChanges`, `UnarchiveOutlined`, `Voicemail`, `Warning`, `WarningAmber`
59
+ - `ArrowRightAlt`, `Delete`, `Email`, `Favorite`
60
+
61
+ ## Notes — Figma → code gotchas
62
+
63
+ - **Typos preserved in exports** — these match the Figma names, so map literally; do not auto-correct:
64
+ - `Lable` (not `Label`)
65
+ - `Aavatar` (not `Avatar`)
66
+ - **`Edit` collision** — three similar icons:
67
+ - `brandIcons.Edit` — pen-on-paper brand icon (yellow accent)
68
+ - `brandIcons.ActivityEdit` — green envelope with a pen, activity style
69
+ - `icons.EditOutlined` — plain MUI outlined pencil
70
+ - **`Settings` collision** — five similar names:
71
+ - `brandIcons.Settings`, `brandIcons.SettingsNav`, `brandIcons.SettingsNavInactive`
72
+ - `icons.Settings`, `icons.SettingsOutlined`
73
+ Pick by Figma category section and visual.
74
+ - **`Star` collision**:
75
+ - `brandIcons.Star` — yellow filled brand star
76
+ - `brandIcons.ActivityStar` — purple activity star
77
+ - **`Tag` vs `Tags`**:
78
+ - `brandIcons.Tags` — brand (plural, box 20×20 live 18×18)
79
+ - `brandIcons.Tag` — activity (singular, box 20×20 live 16×16)
80
+ - **Inactive variants** — many brand icons have `*Inactive` siblings (`DashboardInactive`, `SharedInboxNavInactive`, `SharedLabelInactive`, `KnowledgeBaseInactive`, `ConnectorInactive`, `SettingsNavInactive`, `UserRoleInactive`). Use the inactive variant for the unselected/disabled state of a nav item.
81
+ - **`UserCheck`** — present in `brandIcons` exports but not visible on the current Figma sheet. Use it if a design clearly needs it; otherwise prefer the activity-style icons documented above.
@@ -1,6 +1,10 @@
1
1
  # Typography reference (`@hiver/hiver-ui-kit`)
2
2
 
3
- Map Figma text styles to `<Typography variant="...">`. Font family is **Open Sans**. Match by fontSize + fontWeight (regular 400, medium 500, semiBold 600, bold 700).
3
+ Map Figma text styles to `<Typography variant="...">`. Match by fontSize + fontWeight (regular 400, medium 500, semiBold 600, bold 700).
4
+
5
+ **Font families:**
6
+ - Legacy variants (`h1_*`–`h5_*`, `body0_5_*`–`body4_*`, `caption1_*`, `caption2_*`, `overline_medium`, `overline_semiBold`, `buttonLarge`/`buttonMedium`/`buttonSmall`, `chip`) use **Open Sans**.
7
+ - 2026 tokens (`body_lg_*`–`body_xs_*`, `heading_lg_*`–`heading_sm_*`, `display_lg_*`/`display_md_*`, `caption_regular`/`caption_medium`, `overline_regular`) use **Inter**.
4
8
 
5
9
  ## Heading variants
6
10
 
@@ -68,12 +72,68 @@ Map Figma text styles to `<Typography variant="...">`. Font family is **Open San
68
72
  | `buttonSmall` | 12 | 500 | 20px | 0.4px |
69
73
  | `chip` | 12 | 500 | 12px | 0.4px |
70
74
 
75
+ ## Typography 2026 tokens (Inter)
76
+
77
+ Canonical 2026 design tokens. Use these for new UI built against the 2026 design system. Font family is **Inter**.
78
+
79
+ ### Display
80
+
81
+ | Variant | fontSize | fontWeight | lineHeight | letterSpacing |
82
+ |---------|----------|------------|------------|---------------|
83
+ | `display_lg_semiBold` | 28 | 600 | 36px | 0.01em |
84
+ | `display_lg_bold` | 28 | 700 | 36px | 0.01em |
85
+ | `display_md_semiBold` | 24 | 600 | 32px | 0.01em |
86
+ | `display_md_bold` | 24 | 700 | 32px | 0.01em |
87
+
88
+ ### Heading
89
+
90
+ | Variant | fontSize | fontWeight | lineHeight | letterSpacing |
91
+ |---------|----------|------------|------------|---------------|
92
+ | `heading_lg_medium` | 20 | 500 | 28px | -0.005em |
93
+ | `heading_lg_semiBold` | 20 | 600 | 28px | -0.005em |
94
+ | `heading_md_medium` | 18 | 500 | 28px | -0.005em |
95
+ | `heading_md_semiBold` | 18 | 600 | 28px | -0.005em |
96
+ | `heading_sm_medium` | 16 | 500 | 22px | -0.005em |
97
+ | `heading_sm_semiBold` | 16 | 600 | 22px | -0.005em |
98
+
99
+ ### Body
100
+
101
+ | Variant | fontSize | fontWeight | lineHeight | letterSpacing |
102
+ |---------|----------|------------|------------|---------------|
103
+ | `body_lg_regular` | 16 | 400 | 24px | -0.005em |
104
+ | `body_lg_medium` | 16 | 500 | 24px | -0.005em |
105
+ | `body_lg_semiBold` | 16 | 600 | 24px | -0.005em |
106
+ | `body_md_regular` | 14 | 400 | 20px | -0.005em |
107
+ | `body_md_medium` | 14 | 500 | 20px | -0.005em |
108
+ | `body_md_semiBold` | 14 | 600 | 20px | -0.005em |
109
+ | `body_sm_regular` | 13 | 400 | 19px | -0.005em |
110
+ | `body_sm_medium` | 13 | 500 | 19px | -0.005em |
111
+ | `body_sm_semiBold` | 13 | 600 | 19px | -0.005em |
112
+ | `body_xs_regular` | 12 | 400 | 18px | -0.005em |
113
+ | `body_xs_medium` | 12 | 500 | 18px | -0.005em |
114
+ | `body_xs_semiBold` | 12 | 600 | 18px | -0.005em |
115
+
116
+ ### Caption & overline (2026)
117
+
118
+ | Variant | fontSize | fontWeight | lineHeight | letterSpacing | Notes |
119
+ |---------|----------|------------|------------|---------------|-------|
120
+ | `caption_regular` | 12 | 400 | 14px | 0.01em | |
121
+ | `caption_medium` | 12 | 500 | 14px | 0.01em | |
122
+ | `overline_regular` | 12 | 400 | 14px | 0.01em | UPPERCASE |
123
+
124
+ > Note: a 2026 `overline_medium` token also exists in the theme (Inter, 12 / 500 / 14px / 0.01em / UPPERCASE) and shares its name with the legacy Open Sans `overline_medium` (11 / 500 / 18px). The 2026 spread is applied after the legacy block, so the Inter version wins at runtime — use accordingly.
125
+
71
126
  ## Usage
72
127
 
73
128
  ```jsx
74
129
  <Typography variant="h2_medium">Section title</Typography>
75
130
  <Typography variant="body1_regular">Body text</Typography>
76
131
  <Typography variant="caption1_medium" color="text.secondary">Caption</Typography>
132
+
133
+ // 2026 tokens
134
+ <Typography variant="heading_lg_semiBold">Page title</Typography>
135
+ <Typography variant="body_md_regular">Body text</Typography>
136
+ <Typography variant="display_lg_bold">Hero</Typography>
77
137
  ```
78
138
 
79
- **Figma → variant:** Compare Figma text style fontSize and weight (Regular/Medium/Semi Bold/Bold) to the tables above and pick the closest match.
139
+ **Figma → variant:** Compare Figma text style fontSize and weight (Regular/Medium/Semi Bold/Bold) to the tables above and pick the closest match. For new 2026 designs, prefer the Inter tokens.
package/dist/cli.js CHANGED
@@ -239,6 +239,15 @@ function getAgentMeta(collectionName, agentName) {
239
239
  dependencies: Array.isArray(fm.dependencies) ? fm.dependencies : []
240
240
  };
241
241
  }
242
+ function findCollectionsContaining(name, type) {
243
+ const matches = [];
244
+ for (const col of listCollections()) {
245
+ const { skills, agents } = scanCollection(col);
246
+ if (type === "skill" && skills.includes(name)) matches.push(col);
247
+ if (type === "agent" && agents.includes(name)) matches.push(col);
248
+ }
249
+ return matches;
250
+ }
242
251
  function resolveDependencies(selected, selectedCollections) {
243
252
  const installedNames = /* @__PURE__ */ new Set([
244
253
  ...selected.skills.map((s) => s.name),
@@ -341,34 +350,8 @@ function Add({ directSkills }) {
341
350
  const [selectedType, setSelectedType] = useState3(null);
342
351
  const [toInstall, setToInstall] = useState3({ skills: [], agents: [] });
343
352
  const [results, setResults] = useState3({ skills: 0, agents: 0, deps: [] });
353
+ const [directMissing, setDirectMissing] = useState3([]);
344
354
  const destRoot = process.cwd();
345
- useEffect(() => {
346
- if (directSkills && directSkills.length > 0 && targetKey) {
347
- installByName(directSkills, targets[targetKey], destRoot).then((res) => {
348
- setResults(res);
349
- setStep(STEPS.DONE);
350
- });
351
- }
352
- }, [targetKey]);
353
- async function installByName(names, targetConfig, dest) {
354
- const collections = listCollections();
355
- let skillCount = 0;
356
- let agentCount = 0;
357
- for (const name of names) {
358
- for (const col of collections) {
359
- const { skills, agents } = scanCollection(col);
360
- if (skills.includes(name)) {
361
- if (await installSkill(col, name, targetConfig, dest)) skillCount++;
362
- break;
363
- }
364
- if (agents.includes(name)) {
365
- if (await installAgent(col, name, targetConfig, dest)) agentCount++;
366
- break;
367
- }
368
- }
369
- }
370
- return { skills: skillCount, agents: agentCount, deps: [] };
371
- }
372
355
  function getAvailableTypes(col) {
373
356
  const { skills, agents } = scanCollection(col);
374
357
  const types = [];
@@ -378,11 +361,29 @@ function Add({ directSkills }) {
378
361
  }
379
362
  function handleTargetSelect(key) {
380
363
  setTargetKey(key);
381
- if (directSkills && directSkills.length > 0) return;
382
364
  setStep(STEPS.COLLECTIONS);
383
365
  }
384
366
  function handleCollectionSelect(col) {
385
367
  setSelectedCollection(col);
368
+ if (directSkills && directSkills.length > 0) {
369
+ const { skills, agents } = scanCollection(col);
370
+ const skillItems = [];
371
+ const agentItems = [];
372
+ const missing = [];
373
+ for (const name of directSkills) {
374
+ if (skills.includes(name)) skillItems.push({ collection: col, name });
375
+ else if (agents.includes(name)) agentItems.push({ collection: col, name });
376
+ else missing.push(`${name} (not in ${col})`);
377
+ }
378
+ setDirectMissing(missing);
379
+ setToInstall({ skills: skillItems, agents: agentItems });
380
+ if (skillItems.length === 0 && agentItems.length === 0) {
381
+ setStep(STEPS.DONE);
382
+ } else {
383
+ setStep(STEPS.INSTALLING);
384
+ }
385
+ return;
386
+ }
386
387
  setToInstall({ skills: [], agents: [] });
387
388
  const types = getAvailableTypes(col);
388
389
  if (types.length === 1) {
@@ -466,8 +467,24 @@ function Add({ directSkills }) {
466
467
  );
467
468
  }
468
469
  if (step === STEPS.COLLECTIONS) {
469
- const collections = listCollections();
470
+ let collections = listCollections();
471
+ if (directSkills && directSkills.length > 0) {
472
+ const union = /* @__PURE__ */ new Set();
473
+ for (const name of directSkills) {
474
+ for (const c of findCollectionsContaining(name, "skill")) union.add(c);
475
+ for (const c of findCollectionsContaining(name, "agent")) union.add(c);
476
+ }
477
+ collections = collections.filter((c) => union.has(c));
478
+ }
470
479
  const items = collections.map((c) => ({ label: c, value: c }));
480
+ if (items.length === 0) {
481
+ setTimeout(() => exit(), 100);
482
+ return /* @__PURE__ */ jsxs4(ErrorMsg, { children: [
483
+ "None of: ",
484
+ directSkills.join(", "),
485
+ " found in any collection."
486
+ ] });
487
+ }
471
488
  return /* @__PURE__ */ jsx3(
472
489
  Select,
473
490
  {
@@ -530,6 +547,10 @@ function Add({ directSkills }) {
530
547
  if (step === STEPS.DONE) {
531
548
  setTimeout(() => exit(), 100);
532
549
  return /* @__PURE__ */ jsxs4(Box3, { flexDirection: "column", marginTop: 1, children: [
550
+ directMissing.length > 0 && /* @__PURE__ */ jsx3(Box3, { marginBottom: 1, children: /* @__PURE__ */ jsxs4(ErrorMsg, { children: [
551
+ "Not found in any collection: ",
552
+ directMissing.join(", ")
553
+ ] }) }),
533
554
  results.deps.length > 0 && /* @__PURE__ */ jsxs4(Box3, { flexDirection: "column", marginBottom: 1, children: [
534
555
  /* @__PURE__ */ jsx3(Info, { children: "Auto-installed dependencies:" }),
535
556
  results.deps.map((d) => /* @__PURE__ */ jsxs4(Text4, { color: "cyan", children: [
@@ -690,88 +711,161 @@ function hasChanges(localContent, latestContent) {
690
711
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
691
712
  var STEPS2 = {
692
713
  TARGET: "target",
693
- CHECKING: "checking",
714
+ COLLECTION: "collection",
715
+ ITEMS: "items",
716
+ RESOLVE: "resolve",
694
717
  CONFIRM: "confirm",
695
718
  DONE: "done"
696
719
  };
697
- function Update() {
720
+ function detectType(name, targetConfig, destRoot) {
721
+ const skillPath = path3.join(destRoot, targetConfig.skillsDir, name, "SKILL.md");
722
+ if (fs3.existsSync(skillPath)) return "skill";
723
+ const agentPath = path3.join(destRoot, targetConfig.agentsDir, `${name}.md`);
724
+ if (fs3.existsSync(agentPath)) return "agent";
725
+ return null;
726
+ }
727
+ function Update({ directSkills }) {
698
728
  const { exit } = useApp3();
699
729
  const [step, setStep] = useState4(STEPS2.TARGET);
700
730
  const [targetKey, setTargetKey] = useState4(null);
701
- const [changedItems, setChangedItems] = useState4([]);
731
+ const [selectedCollection, setSelectedCollection] = useState4(null);
732
+ const [queue, setQueue] = useState4([]);
702
733
  const [currentIdx, setCurrentIdx] = useState4(0);
734
+ const [currentPatch, setCurrentPatch] = useState4(null);
703
735
  const [updatedCount, setUpdatedCount] = useState4(0);
704
736
  const [skippedCount, setSkippedCount] = useState4(0);
737
+ const [installedCount, setInstalledCount] = useState4(0);
738
+ const [missingNames, setMissingNames] = useState4([]);
739
+ const [directInfo, setDirectInfo] = useState4([]);
705
740
  const destRoot = process.cwd();
706
- function findSource(name, type) {
707
- for (const col of listCollections()) {
708
- const { skills, agents } = scanCollection(col);
709
- if (type === "skill" && skills.includes(name)) return { collection: col };
710
- if (type === "agent" && agents.includes(name)) return { collection: col };
741
+ function handleTargetSelect(key) {
742
+ setTargetKey(key);
743
+ if (directSkills && directSkills.length > 0) {
744
+ const targetConfig = targets[key];
745
+ const info = [];
746
+ const missing = [];
747
+ for (const name of directSkills) {
748
+ let type = detectType(name, targetConfig, destRoot);
749
+ let installed = type !== null;
750
+ if (!type) {
751
+ if (findCollectionsContaining(name, "skill").length > 0) type = "skill";
752
+ else if (findCollectionsContaining(name, "agent").length > 0) type = "agent";
753
+ }
754
+ if (!type) {
755
+ missing.push(name);
756
+ continue;
757
+ }
758
+ const cols = findCollectionsContaining(name, type);
759
+ if (cols.length === 0) {
760
+ missing.push(name);
761
+ continue;
762
+ }
763
+ info.push({ name, type, installed, collections: cols });
764
+ }
765
+ setMissingNames(missing);
766
+ setDirectInfo(info);
767
+ if (info.length === 0) {
768
+ setStep(STEPS2.DONE);
769
+ return;
770
+ }
711
771
  }
712
- return null;
772
+ setStep(STEPS2.COLLECTION);
713
773
  }
714
- useEffect2(() => {
715
- if (step !== STEPS2.CHECKING) return;
716
- const targetConfig = targets[targetKey];
717
- const installed = listInstalled(targetConfig, destRoot);
718
- const changed = [];
719
- for (const skillName of installed.skills) {
720
- const source = findSource(skillName, "skill");
721
- if (!source) continue;
722
- const localFile = path3.join(destRoot, targetConfig.skillsDir, skillName, "SKILL.md");
723
- const latestFile = path3.join(getCollectionsDir(), source.collection, "skills", skillName, "SKILL.md");
724
- if (!fs3.existsSync(localFile) || !fs3.existsSync(latestFile)) continue;
725
- const localContent = fs3.readFileSync(localFile, "utf-8");
726
- const latestContent = fs3.readFileSync(latestFile, "utf-8");
727
- if (hasChanges(localContent, latestContent)) {
728
- changed.push({
729
- name: skillName,
730
- type: "skill",
731
- collection: source.collection,
732
- patch: computeDiff("SKILL.md", localContent, latestContent)
733
- });
774
+ function handleCollectionSelect(collection) {
775
+ setSelectedCollection(collection);
776
+ if (directSkills && directSkills.length > 0) {
777
+ const items = [];
778
+ const skipped = [];
779
+ for (const entry of directInfo) {
780
+ if (entry.collections.includes(collection)) {
781
+ items.push({ name: entry.name, type: entry.type, installed: entry.installed });
782
+ } else {
783
+ skipped.push(`${entry.name} (not in ${collection})`);
784
+ }
734
785
  }
735
- }
736
- for (const agentName of installed.agents) {
737
- const source = findSource(agentName, "agent");
738
- if (!source) continue;
739
- const localFile = path3.join(destRoot, targetConfig.agentsDir, `${agentName}.md`);
740
- const latestFile = path3.join(getCollectionsDir(), source.collection, "agents", `${agentName}.md`);
741
- if (!fs3.existsSync(localFile) || !fs3.existsSync(latestFile)) continue;
742
- const localContent = fs3.readFileSync(localFile, "utf-8");
743
- const latestContent = fs3.readFileSync(latestFile, "utf-8");
744
- if (hasChanges(localContent, latestContent)) {
745
- changed.push({
746
- name: agentName,
747
- type: "agent",
748
- collection: source.collection,
749
- patch: computeDiff(`${agentName}.md`, localContent, latestContent)
750
- });
786
+ if (skipped.length > 0) {
787
+ setMissingNames((prev) => [...prev, ...skipped]);
788
+ }
789
+ setQueue(items);
790
+ setCurrentIdx(0);
791
+ if (items.length === 0) {
792
+ setStep(STEPS2.DONE);
793
+ } else {
794
+ setStep(STEPS2.RESOLVE);
751
795
  }
796
+ } else {
797
+ setStep(STEPS2.ITEMS);
752
798
  }
753
- setChangedItems(changed);
754
- if (changed.length === 0) {
799
+ }
800
+ function handleItemsSubmit(values) {
801
+ const items = values.map((v) => {
802
+ const [type, name] = v.split(":");
803
+ return { name, type, installed: true };
804
+ });
805
+ setQueue(items);
806
+ setCurrentIdx(0);
807
+ if (items.length === 0) {
755
808
  setStep(STEPS2.DONE);
756
809
  } else {
757
- setCurrentIdx(0);
758
- setStep(STEPS2.CONFIRM);
810
+ setStep(STEPS2.RESOLVE);
759
811
  }
760
- }, [step]);
761
- function handleTargetSelect(key) {
762
- setTargetKey(key);
763
- setStep(STEPS2.CHECKING);
812
+ }
813
+ useEffect2(() => {
814
+ if (step !== STEPS2.RESOLVE) return;
815
+ const item = queue[currentIdx];
816
+ if (!item) {
817
+ setStep(STEPS2.DONE);
818
+ return;
819
+ }
820
+ processItem(item);
821
+ }, [step, currentIdx]);
822
+ async function processItem(item) {
823
+ const targetConfig = targets[targetKey];
824
+ const collection = selectedCollection;
825
+ if (!item.installed) {
826
+ if (item.type === "skill") {
827
+ await installSkill(collection, item.name, targetConfig, destRoot);
828
+ } else {
829
+ await installAgent(collection, item.name, targetConfig, destRoot);
830
+ }
831
+ setInstalledCount((c) => c + 1);
832
+ advance();
833
+ return;
834
+ }
835
+ let localFile;
836
+ let latestFile;
837
+ if (item.type === "skill") {
838
+ localFile = path3.join(destRoot, targetConfig.skillsDir, item.name, "SKILL.md");
839
+ latestFile = path3.join(getCollectionsDir(), collection, "skills", item.name, "SKILL.md");
840
+ } else {
841
+ localFile = path3.join(destRoot, targetConfig.agentsDir, `${item.name}.md`);
842
+ latestFile = path3.join(getCollectionsDir(), collection, "agents", `${item.name}.md`);
843
+ }
844
+ if (!fs3.existsSync(localFile) || !fs3.existsSync(latestFile)) {
845
+ advance();
846
+ return;
847
+ }
848
+ const localContent = fs3.readFileSync(localFile, "utf-8");
849
+ const latestContent = fs3.readFileSync(latestFile, "utf-8");
850
+ if (!hasChanges(localContent, latestContent)) {
851
+ setSkippedCount((c) => c + 1);
852
+ advance();
853
+ return;
854
+ }
855
+ const filename = item.type === "skill" ? "SKILL.md" : `${item.name}.md`;
856
+ setCurrentPatch(computeDiff(filename, localContent, latestContent));
857
+ setStep(STEPS2.CONFIRM);
764
858
  }
765
859
  function handleConfirm(yes) {
766
- const item = changedItems[currentIdx];
860
+ const item = queue[currentIdx];
767
861
  const targetConfig = targets[targetKey];
768
862
  if (yes) {
769
863
  if (item.type === "skill") {
770
- const src = path3.join(getCollectionsDir(), item.collection, "skills", item.name);
864
+ const src = path3.join(getCollectionsDir(), selectedCollection, "skills", item.name);
771
865
  const dest = path3.join(destRoot, targetConfig.skillsDir, item.name);
772
866
  fs3.copySync(src, dest, { overwrite: true });
773
867
  } else {
774
- const src = path3.join(getCollectionsDir(), item.collection, "agents", `${item.name}.md`);
868
+ const src = path3.join(getCollectionsDir(), selectedCollection, "agents", `${item.name}.md`);
775
869
  const dest = path3.join(destRoot, targetConfig.agentsDir, `${item.name}.md`);
776
870
  fs3.copySync(src, dest, { overwrite: true });
777
871
  }
@@ -779,39 +873,119 @@ function Update() {
779
873
  } else {
780
874
  setSkippedCount((c) => c + 1);
781
875
  }
782
- if (currentIdx + 1 < changedItems.length) {
876
+ advance();
877
+ }
878
+ function advance() {
879
+ setCurrentPatch(null);
880
+ if (currentIdx + 1 < queue.length) {
783
881
  setCurrentIdx((i) => i + 1);
882
+ setStep(STEPS2.RESOLVE);
784
883
  } else {
785
884
  setStep(STEPS2.DONE);
786
885
  }
787
886
  }
788
887
  if (step === STEPS2.TARGET) {
789
- return /* @__PURE__ */ jsx7(Select, { items: targetList, onSelect: handleTargetSelect, onBack: () => exit(), message: "Which target to update?" });
888
+ return /* @__PURE__ */ jsx7(
889
+ Select,
890
+ {
891
+ items: targetList,
892
+ onSelect: handleTargetSelect,
893
+ onBack: () => exit(),
894
+ message: "Which target to update?"
895
+ }
896
+ );
897
+ }
898
+ if (step === STEPS2.COLLECTION) {
899
+ let collectionNames;
900
+ if (directSkills && directSkills.length > 0) {
901
+ const union = /* @__PURE__ */ new Set();
902
+ for (const entry of directInfo) {
903
+ for (const c of entry.collections) union.add(c);
904
+ }
905
+ collectionNames = listCollections().filter((c) => union.has(c));
906
+ } else {
907
+ collectionNames = listCollections();
908
+ }
909
+ const items = collectionNames.map((c) => ({ label: c, value: c }));
910
+ return /* @__PURE__ */ jsx7(
911
+ Select,
912
+ {
913
+ items,
914
+ onSelect: handleCollectionSelect,
915
+ onBack: () => setStep(STEPS2.TARGET),
916
+ message: "Pick collection to update from:"
917
+ }
918
+ );
919
+ }
920
+ if (step === STEPS2.ITEMS) {
921
+ const targetConfig = targets[targetKey];
922
+ const installed = listInstalled(targetConfig, destRoot);
923
+ const { skills, agents } = scanCollection(selectedCollection);
924
+ const items = [
925
+ ...installed.skills.filter((name) => skills.includes(name)).map((name) => ({
926
+ label: `${name} (skill)`,
927
+ value: `skill:${name}`,
928
+ checked: false
929
+ })),
930
+ ...installed.agents.filter((name) => agents.includes(name)).map((name) => ({
931
+ label: `${name} (agent)`,
932
+ value: `agent:${name}`,
933
+ checked: false
934
+ }))
935
+ ];
936
+ if (items.length === 0) {
937
+ setTimeout(() => exit(), 100);
938
+ return /* @__PURE__ */ jsxs7(Info, { children: [
939
+ 'No installed skills/agents from "',
940
+ selectedCollection,
941
+ '" under ',
942
+ targetConfig.skillsDir,
943
+ "/."
944
+ ] });
945
+ }
946
+ return /* @__PURE__ */ jsx7(
947
+ MultiSelect,
948
+ {
949
+ items,
950
+ onSubmit: handleItemsSubmit,
951
+ onBack: () => setStep(STEPS2.COLLECTION),
952
+ message: `Select skills/agents from "${selectedCollection}" to update:`
953
+ }
954
+ );
790
955
  }
791
- if (step === STEPS2.CHECKING) {
792
- return /* @__PURE__ */ jsx7(Text8, { color: "yellow", children: "Checking for updates..." });
956
+ if (step === STEPS2.RESOLVE) {
957
+ return /* @__PURE__ */ jsx7(Text8, { color: "yellow", children: "Checking..." });
793
958
  }
794
959
  if (step === STEPS2.CONFIRM) {
795
- const item = changedItems[currentIdx];
960
+ const item = queue[currentIdx];
796
961
  return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
797
962
  /* @__PURE__ */ jsxs7(Warning, { children: [
798
963
  item.name,
799
- ": local differs from latest"
964
+ ": local differs from ",
965
+ selectedCollection
800
966
  ] }),
801
- /* @__PURE__ */ jsx7(DiffView, { patch: item.patch }),
967
+ /* @__PURE__ */ jsx7(DiffView, { patch: currentPatch }),
802
968
  /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(Confirm, { message: `Update ${item.name}?`, onConfirm: handleConfirm }) })
803
969
  ] });
804
970
  }
805
971
  if (step === STEPS2.DONE) {
806
972
  setTimeout(() => exit(), 100);
807
- if (changedItems.length === 0) {
808
- return /* @__PURE__ */ jsx7(Success, { children: "All installed skills are up to date." });
809
- }
810
- return /* @__PURE__ */ jsxs7(Success, { children: [
811
- updatedCount,
812
- " updated, ",
813
- skippedCount,
814
- " skipped"
973
+ if (queue.length === 0 && missingNames.length === 0) {
974
+ return /* @__PURE__ */ jsx7(Success, { children: "Nothing to update." });
975
+ }
976
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
977
+ missingNames.length > 0 && /* @__PURE__ */ jsxs7(ErrorMsg, { children: [
978
+ "Skipped: ",
979
+ missingNames.join(", ")
980
+ ] }),
981
+ /* @__PURE__ */ jsxs7(Success, { children: [
982
+ updatedCount,
983
+ " updated, ",
984
+ installedCount,
985
+ " installed, ",
986
+ skippedCount,
987
+ " skipped"
988
+ ] })
815
989
  ] });
816
990
  }
817
991
  return null;
@@ -914,7 +1088,7 @@ function Help() {
914
1088
  const commands = [
915
1089
  { name: "add [skills...]", desc: "Install skills/agents (interactive if no args)" },
916
1090
  { name: "list", desc: "List all available skills and agents" },
917
- { name: "update", desc: "Update installed skills to latest version" },
1091
+ { name: "update [skills...]", desc: "Update installed skills (interactive if no args)" },
918
1092
  { name: "remove <skills>", desc: "Remove installed skills/agents" },
919
1093
  { name: "help", desc: "Show this help message" }
920
1094
  ];
@@ -923,6 +1097,7 @@ function Help() {
923
1097
  "npx @hiver/skills add discuss-problem create-prd",
924
1098
  "npx @hiver/skills list",
925
1099
  "npx @hiver/skills update",
1100
+ "npx @hiver/skills update build-component",
926
1101
  "npx @hiver/skills remove discuss-problem"
927
1102
  ];
928
1103
  return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginTop: 1, marginBottom: 1, children: [
@@ -959,7 +1134,7 @@ function App() {
959
1134
  case "list":
960
1135
  return /* @__PURE__ */ jsx9(List, {});
961
1136
  case "update":
962
- return /* @__PURE__ */ jsx9(Update, {});
1137
+ return /* @__PURE__ */ jsx9(Update, { directSkills: restArgs.length > 0 ? restArgs : null });
963
1138
  case "remove":
964
1139
  if (restArgs.length === 0) {
965
1140
  return /* @__PURE__ */ jsxs9(Text10, { color: "red", children: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hiver/skills",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Shared Claude Code skills & agents for Hiver teams",
5
5
  "type": "module",
6
6
  "bin": {