Skip to content

Commit 1c4eead

Browse files
rohitkrai03claude
andcommitted
feat: add rhdh-coding skill for Backstage/RHDH plugin development
Adds a coding skill that teaches agents non-obvious Backstage and RHDH patterns — the knowledge you'd gain after six months of getting burned by conventions that aren't in the docs or training data. Covers: BUI-first styling, NFS Blueprints, RHDH dynamic plugin gotchas, plugin type decisions, Backstage-specific testing, dev app setup (both plugin dev mode and full Backstage app), and frontend spec guidance for PRDs/OpenSpec workflows. References are verified against real plugin code in rhdh-plugins and community-plugins. Designed to be the daily-driver skill for agents working on existing plugins — complementary to create-plugin (new plugins), nfs-migration (migration), and official backstage.io skills. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Rohit Rai <rohitkrai03@gmail.com>
1 parent a80cb33 commit 1c4eead

12 files changed

Lines changed: 1301 additions & 4 deletions

File tree

.claude-plugin/marketplace.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
},
66
"metadata": {
77
"description": "Orchestrator skill for RHDH plugin development - onboard, update, and maintain plugins in the Extensions Catalog",
8-
"version": "0.6.0"
8+
"version": "0.7.0"
99
},
1010
"plugins": [
1111
{
1212
"name": "rhdh",
1313
"source": "./",
1414
"description": "Skills for RHDH plugin lifecycle management",
15-
"version": "0.6.0",
15+
"version": "0.7.0",
1616
"strict": true
1717
}
1818
]

.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "rhdh",
33
"description": "All-in-one toolkit for Red Hat Developer Hub (RHDH). Covers plugin development, overlay management, environment setup, version compatibility, CI/CD, and RHDH ecosystem navigation.",
4-
"version": "0.6.0",
4+
"version": "0.7.0",
55
"author": {
66
"name": "RHDH Store Manager"
77
},

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "rhdh-skill"
3-
version = "0.6.0"
3+
version = "0.7.0"
44
description = "Claude Code skill for RHDH plugin development"
55
readme = "README.md"
66
license = "Apache-2.0"

skills/rhdh-coding/SKILL.md

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
---
2+
name: rhdh-coding
3+
description: >-
4+
Backstage and RHDH plugin development patterns. Use when writing, modifying,
5+
or reviewing code in a Backstage or RHDH plugin — frontend components, backend
6+
services, API clients, styling, testing, entity pages, scaffolder actions,
7+
catalog processors, NFS Blueprints, dynamic plugin configuration. Also use
8+
when asked to "add a feature to a plugin", "implement a Backstage component",
9+
"create an API client", "write plugin tests", "add a backend route", "create
10+
a scaffolder action", "what plugin type should I use", or any coding task in
11+
a Backstage or RHDH codebase.
12+
---
13+
14+
# RHDH Coding
15+
16+
Patterns for Backstage and RHDH plugin development that agents need but can't
17+
reliably get from training data or codebase discovery alone. This covers what
18+
you'd learn after six months of getting burned by non-obvious conventions.
19+
20+
## Before You Write Code
21+
22+
### 1. Check for existing specs
23+
24+
Look for a spec, PRD, or OpenSpec design for this work — in `docs/plans/**/`,
25+
`specifications/`, `openspec/changes/*/`, or linked from the issue. If found,
26+
use the component list and acceptance criteria as your implementation blueprint.
27+
Read `references/frontend-specs.md` for what good frontend specs include.
28+
29+
### 2. Discover the plugin context
30+
31+
Run the detection script to understand what you're working with:
32+
33+
```bash
34+
python scripts/detect-rhdh-context.py --path <plugin-dir>
35+
```
36+
37+
This reports: Backstage role, frontend system (legacy/NFS/dual), existing
38+
extensions, MUI version, dynamic plugin status, plugin ID, scalprum name.
39+
40+
### 3. Read workspace instructions
41+
42+
```bash
43+
test -f AGENTS.md && cat AGENTS.md
44+
test -f CLAUDE.md && cat CLAUDE.md
45+
```
46+
47+
These contain repo-specific rules that override general patterns.
48+
49+
### 4. Check version compatibility
50+
51+
Consult `../rhdh/references/versions.md` for the RHDH → Backstage version
52+
matrix. Your `@backstage/*` dependency versions must match the target RHDH
53+
version. Mismatched versions cause runtime errors — most commonly "Cannot
54+
read properties of undefined."
55+
56+
## Styling: BUI First
57+
58+
**For new plugins,** use Backstage UI (`@backstage/ui`) with CSS Modules and
59+
BUI CSS variables. **In existing plugins,** match whatever the workspace already
60+
uses — if it's MUI v4, stay consistent rather than mixing libraries. Only
61+
introduce BUI in a workspace that has already adopted it or is actively migrating.
62+
63+
**Priority for new plugins:**
64+
1. **BUI** (`@backstage/ui`) — default for new plugins and new workspaces
65+
2. **MUI v5** (`@mui/material`) — when BUI lacks the component you need
66+
3. **MUI v4** (`@material-ui/core`) — legacy maintenance only
67+
68+
When using MUI v5 alongside BUI, add the class name generator to prevent
69+
collisions in dynamic plugin bundles:
70+
71+
```typescript
72+
// src/index.ts
73+
import { unstable_ClassNameGenerator as ClassNameGenerator } from '@mui/material/className';
74+
ClassNameGenerator.configure(name => name.startsWith('v5-') ? name : `v5-${name}`);
75+
```
76+
77+
**Icons:** Use `@remixicon/react` (not `@material-ui/icons`).
78+
79+
Read `references/bui.md` for the component mapping table and CSS variable reference.
80+
81+
## Frontend System: NFS for New Plugins
82+
83+
New plugins targeting RHDH 1.5+ should use the **new frontend system (NFS)**
84+
with Blueprints (`createFrontendPlugin`, `PageBlueprint`, `EntityCardBlueprint`,
85+
etc.).
86+
87+
Legacy system (`createPlugin` + `createRoutableExtension`) is for existing
88+
plugins not yet migrated. For migration, use the `nfs-migration` sibling skill.
89+
90+
Read `references/nfs.md` for Blueprint patterns, alpha export structure, and
91+
compatWrapper decisions.
92+
93+
## Plugin Types
94+
95+
Not sure whether to build a page, a card, an entity tab, or a backend module?
96+
Read `references/plugin-types.md` for the decision guide.
97+
98+
## Backend: New System Only
99+
100+
All backend code MUST use:
101+
- `createBackendPlugin` — new standalone backend capabilities
102+
- `createBackendModule` — extensions to existing plugins (catalog, scaffolder, auth)
103+
104+
From `@backstage/backend-plugin-api`. Never the legacy backend system.
105+
106+
Core services: `httpRouter`, `logger`, `rootConfig`, `httpAuth`, `database`,
107+
`scheduler`, `permissions`, `discovery`.
108+
109+
**Default export is required** from the entry point (`src/index.ts`). Missing
110+
default export is the #1 cause of "plugin not loading" in RHDH.
111+
112+
## RHDH Dynamic Plugins
113+
114+
Key gotchas (read `references/rhdh.md` for full details):
115+
116+
- **Default export required** from `src/index.ts` — missing this is the #1 cause of "plugin not loading"
117+
- **Scalprum name** must match the key in `dynamic-plugins.yaml` wiring (derived from package name)
118+
- **MUI v5 class name generator** required when using `@mui/material` in dynamic bundles
119+
- **Auth:** use `fetchApi` — it includes auth headers automatically. Don't implement custom auth.
120+
- **RHDH-only Blueprints:** `AppDrawerContentBlueprint`, `GlobalHeaderMenuItemBlueprint` — not upstream
121+
122+
`references/rhdh.md` covers all RHDH-specific patterns including backend modules,
123+
extension points, theming, i18n, and the common package pattern.
124+
125+
## Analytics
126+
127+
BUI components (`Link`, `ButtonLink`, `Tab`, `MenuItem`, `Tag`, `Table` rows)
128+
have built-in click analytics via the Backstage Analytics API. Don't add manual
129+
`captureEvent('click', ...)` for these — it produces duplicates. Use `noTrack`
130+
prop to suppress the built-in event only when replacing it with a domain-specific
131+
verb (e.g., `deploy`, `approve`). For detailed instrumentation guidance, install
132+
the official `plugin-analytics-instrumentation` skill from backstage.io.
133+
134+
## Testing
135+
136+
Backstage has its own test infrastructure that differs from standard React testing.
137+
Read `references/testing.md` for: TestApiProvider setup, renderInTestApp,
138+
entity context mocking, async component testing, accessibility testing, and
139+
common gotchas.
140+
141+
## Reference Index
142+
143+
| Reference | Load when... |
144+
|-----------|-------------|
145+
| `references/frontend-specs.md` | Writing specs, PRDs, or OpenSpec proposals for frontend features |
146+
| `references/bui.md` | Using BUI components — mapping table, CSS variables, icons |
147+
| `references/plugin-types.md` | Deciding what type of plugin or extension to build |
148+
| `references/nfs.md` | Writing NFS code — Blueprints, package exports, compatWrapper |
149+
| `references/dev-app.md` | Plugin dev mode, full Backstage app setup, sidebar, app-config |
150+
| `references/testing.md` | Writing tests — TestApiProvider, entity mocking, a11y |
151+
| `references/rhdh.md` | RHDH-specific patterns — dynamic plugins, backend modules, i18n |
152+
153+
## Sibling Skills (rhdh-skill)
154+
155+
| Skill | Use when... |
156+
|-------|------------|
157+
| `create-plugin` | Scaffolding a new plugin from scratch |
158+
| `nfs-migration` | Migrating an existing plugin from legacy to NFS |
159+
| `overlay` | Managing overlay packaging for the Extensions Catalog |
160+
| `backstage-upgrade` | Upgrading Backstage dependency versions |
161+
| `rhdh-local` | Running and testing plugins locally |
162+
| `rhdh` | RHDH version matrix, repo navigation, ecosystem context |
163+
164+
## Official Backstage Skills (backstage.io)
165+
166+
Install via `npx skills add https://backstage.io`. These are complementary —
167+
they cover migration and instrumentation workflows that this skill does not.
168+
169+
| Skill | Use when... |
170+
|-------|------------|
171+
| `mui-to-bui-migration` | Migrating a plugin from MUI to BUI (component-by-component guide) |
172+
| `plugin-new-frontend-system-support` | Adding NFS support while keeping legacy working (dual entry point) |
173+
| `plugin-full-frontend-system-migration` | Fully migrating a plugin to NFS, dropping legacy |
174+
| `app-frontend-system-migration` | Migrating an entire Backstage app to the new frontend system |
175+
| `plugin-analytics-instrumentation` | Adding analytics events via Backstage Analytics API |
176+
| `onboard-to-openapi-server` | Migrating backend router to typed OpenAPI tooling |
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# Backstage UI (BUI) Reference
2+
3+
Setup: `yarn add @backstage/ui @remixicon/react` and add `import '@backstage/ui/css/styles.css'` to `src/index.ts`.
4+
5+
BUI uses **CSS Modules** with CSS custom properties — not makeStyles or CSS-in-JS.
6+
7+
```css
8+
/* MyComponent.module.css */
9+
@layer components {
10+
.container {
11+
padding: var(--bui-space-4);
12+
background-color: var(--bui-bg-surface-1);
13+
border-radius: var(--bui-radius-2);
14+
}
15+
}
16+
```
17+
18+
```tsx
19+
import { Box, Text } from '@backstage/ui';
20+
import styles from './MyComponent.module.css';
21+
22+
export const MyComponent = () => (
23+
<Box className={styles.container}>
24+
<Text variant="title-small">Title</Text>
25+
</Box>
26+
);
27+
```
28+
29+
## Component Mapping
30+
31+
| BUI | Replaces MUI | Key differences |
32+
|-----|-------------|-----------------|
33+
| `Text` | `Typography` | `variant`: title-large/medium/small/x-small, body-large/medium/small/x-small. `weight`, `truncate` props. |
34+
| `Button` | `Button` | `variant="primary"/"secondary"/"tertiary"`, `isDisabled`, `destructive`, `loading` |
35+
| `ButtonIcon` | `IconButton` | `icon={<RiIcon />}`, `onPress` (not `onClick`), needs `aria-label` |
36+
| `Card` + `CardHeader/Body/Footer` | `Paper`, `Card` | Composition pattern |
37+
| `Flex` | `Box display="flex"` | `direction`, `align`, `justify="between"` (not `"space-between"`) |
38+
| `Grid.Root` + `Grid.Item` | `Grid container/item` | `columns={{ sm: '12' }}`, `colSpan={{ sm: '12', md: '6' }}` |
39+
| `TextField` | `TextField` | `isRequired`, `onChange` receives string directly (not event!) |
40+
| `PasswordField` || Password input with show/hide toggle |
41+
| `Dialog` + `DialogTrigger` | `Dialog` | Trigger pattern |
42+
| `Tabs` + `TabList/Tab/TabPanel` | `Tabs/TabList/TabPanel` | `defaultSelectedKey`, id-based |
43+
| `Menu` + `MenuTrigger/MenuItem` | `Menu/Popover` | Trigger pattern. Also: `MenuSection`, `MenuSeparator`, `SubmenuTrigger` |
44+
| `Tooltip` + `TooltipTrigger` | `Tooltip` | Both imported from `@backstage/ui` |
45+
| `Tag` | `Chip` | Direct replacement |
46+
| `TagGroup` || Grouped tags |
47+
| `Select` | `Select` | Single and multiple selection modes |
48+
| `Switch` | `Switch` | Toggle |
49+
| `Checkbox` | `Checkbox` | Checkbox input |
50+
| `CheckboxGroup` || Grouped checkboxes with shared label, `orientation`, `isRequired` |
51+
| `RadioGroup` + `Radio` | `RadioGroup` | BUI pattern |
52+
| `SearchField` | `InputBase` | Search input |
53+
| `SearchAutocomplete` || Search with autocomplete popover (`SearchAutocompleteItem`) |
54+
| `Skeleton` | `Skeleton` | Loading placeholder |
55+
| `Accordion` + `AccordionTrigger/Panel` | `Accordion` | Trigger pattern. `AccordionGroup` for multiple. |
56+
| `Alert` | `@material-ui/lab Alert` | `status`, `title`, `description` props |
57+
| `Badge` || Inline badge/label with optional icon |
58+
| `DateRangePicker` || Date range input field |
59+
| `FieldLabel` || Form field label with description and secondary label |
60+
| `Header` || Page header with breadcrumbs and tabs |
61+
| `PluginHeader` || Plugin-level header (used by NFS PageLayout automatically) |
62+
| `List` + `ListRow` | `List/ListItem` | BUI list pattern |
63+
| `Slider` || Range slider input |
64+
| `Table` + `useTable` | `Table` | Data tables with `useTable` hook (supports `complete`, `offset`, `cursor` pagination) |
65+
| `TablePagination` || Standalone pagination component |
66+
| `FullPage` || Full-page layout wrapper |
67+
| `Container` || Centered content container with max-width |
68+
| `VisuallyHidden` || Accessibility helper |
69+
70+
## Icons
71+
72+
Use `@remixicon/react` — not `@material-ui/icons`.
73+
74+
```tsx
75+
import { RiSearchLine, RiCloseLine } from '@remixicon/react';
76+
<RiSearchLine size={16} />
77+
```
78+
79+
| MUI Icon | Remix Icon |
80+
|----------|------------|
81+
| Close | RiCloseLine |
82+
| Search | RiSearchLine |
83+
| Settings | RiSettingsLine |
84+
| Add | RiAddLine |
85+
| Delete | RiDeleteBinLine |
86+
| Edit | RiEditLine |
87+
| Check | RiCheckLine |
88+
| Error | RiErrorWarningLine |
89+
| Warning | RiAlertLine |
90+
| Info | RiInformationLine |
91+
| ExpandMore | RiArrowDownSLine |
92+
| ChevronRight | RiArrowRightSLine |
93+
| Menu | RiMenuLine |
94+
| MoreVert | RiMore2Line |
95+
| Visibility | RiEyeLine |
96+
97+
Full catalog: https://remixicon.com/
98+
99+
## CSS Variables
100+
101+
| Category | Variables |
102+
|----------|----------|
103+
| Spacing | `--bui-space-1` (4px) … `--bui-space-8` (32px) |
104+
| Foreground | `--bui-fg-primary`, `--bui-fg-secondary`, `--bui-fg-link`, `--bui-fg-danger` |
105+
| Background | `--bui-bg-surface-0` (page), `--bui-bg-surface-1` (card), `--bui-bg-hover`, `--bui-bg-solid` |
106+
| Border | `--bui-border`, `--bui-ring` |
107+
| Radius | `--bui-radius-2`, `--bui-radius-3`, `--bui-radius-full` |
108+
| Typography | `--bui-font-regular`, `--bui-font-size-1/2/3`, `--bui-font-weight-regular/bold` |
109+
110+
## MUI Spacing → BUI Spacing
111+
112+
| `theme.spacing(n)` | BUI variable |
113+
|--------------------|-------------|
114+
| `theme.spacing(0.5)` | `var(--bui-space-1)` |
115+
| `theme.spacing(1)` | `var(--bui-space-2)` |
116+
| `theme.spacing(2)` | `var(--bui-space-4)` |
117+
| `theme.spacing(3)` | `var(--bui-space-6)` |
118+
| `theme.spacing(4)` | `var(--bui-space-8)` |
119+
120+
## MUI Colors → BUI Colors
121+
122+
| `theme.palette.*` | BUI variable |
123+
|-------------------|-------------|
124+
| `text.primary` | `var(--bui-fg-primary)` |
125+
| `text.secondary` | `var(--bui-fg-secondary)` |
126+
| `background.paper` | `var(--bui-bg-surface-1)` |
127+
| `background.default` | `var(--bui-bg-surface-0)` |
128+
| `error.main` | `var(--bui-fg-danger)` |
129+
| `divider` | `var(--bui-border)` |
130+
131+
## Responsive
132+
133+
```tsx
134+
import { useBreakpoint } from '@backstage/ui';
135+
const breakpoint = useBreakpoint(); // 'sm' | 'md' | 'lg' | 'xl'
136+
```
137+
138+
## Known Limitations
139+
140+
BUI does not yet have: Timeline, Pagination, Alert, Autocomplete.
141+
Use MUI v5 (`@mui/material`) for these — add the class name generator when mixing.
142+
143+
Some Backstage APIs (e.g., NavItemBlueprint `icon` prop) expect MUI `IconComponent`
144+
type. Remix icons aren't type-compatible — use MUI icons for these specific cases.
145+
146+
## Further Reference
147+
148+
- BUI docs: https://ui.backstage.io
149+
- Full migration guide: `mui-to-bui-migration` skill in community-plugins

0 commit comments

Comments
 (0)