---
name: frontend-extension-config
description: >
  Generates ExtensionConfig (@atlashub/smartstack) with slot definitions that
  mirror the <Slot name="..."> calls emitted by frontend/component.
phase: development/frontend
cli: cli/scaffold-extension-config
allowed-tools: [Read, Glob, Grep, Bash]  # Bash: CLI invocation
---

# Extension Config — Slots + Columns + Form Fields

Generates the `ExtensionConfig` that publishes the module's extension points
(slots, table columns, form fields) to SmartStack's extension system.

`ExtensionConfig` type comes from `@atlashub/smartstack` (npm).

## Slot catalog contract

The slot catalog emitted here MUST match exactly the `<Slot name="...">` calls
emitted by `frontend/component/scaffold-component`. Renaming in one place
without the other breaks composition silently (fills will never render).

Per entity, six slots are registered:

| Slot name | Rendered by `{Entity}ListPage`/`DetailPage`/`FormPage` | Context shape |
|-----------|--------------------------------------------------------|---------------|
| `{module}.{entityLower}.list.before` | ListPage, above the table | `{}` |
| `{module}.{entityLower}.list.after` | ListPage, below the table | `{}` |
| `{module}.{entityLower}.detail.before` | DetailPage, top | `{ data: {Entity} }` |
| `{module}.{entityLower}.detail.after` | DetailPage, bottom | `{ data: {Entity} }` |
| `{module}.{entityLower}.form.fields.before` | FormPage, before scaffolded fields | `{ formData, onChange }` |
| `{module}.{entityLower}.form.fields.after` | FormPage, after scaffolded fields | `{ formData, onChange }` |

## Output pattern

```ts
import type { ExtensionConfig } from '@atlashub/smartstack';

/**
 * Extension configuration for the hrm module.
 *
 * Slot catalog (each <Slot name="..."> call must match exactly):
 *   - 'hrm.employee.list.before': Rendered above the Employee list table.
 *       context: {}
 *   - 'hrm.employee.detail.before': Rendered at the top of the Employee detail page.
 *       context: { data: Employee }
 *   - ...
 *
 * Add Fill components in your app entrypoint to inject content into any slot:
 *   <Fill slot="hrm.employee.list.before">{yourJsx}</Fill>
 */
export const hrmConfig: ExtensionConfig = {
  slots: {
    'hrm.employee.list.before': null,
    'hrm.employee.list.after': null,
    'hrm.employee.detail.before': null,
    'hrm.employee.detail.after': null,
    'hrm.employee.form.fields.before': null,
    'hrm.employee.form.fields.after': null,
    // ... one group per entity
  },
  tableColumns: {},
  formFields: {},
  hooks: {},
};
```

The `tableColumns` and `formFields` are left empty for the client to populate
with domain-specific column extensions and custom `ext.*` form fields. `hooks`
is reserved for future behavioural extension points.

## ⚠ BLOCKING — Key Rules

1. **Module name**: kebab-case, starts with a letter (regex `/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/`).
2. **Entity names**: PascalCase (regex `/^[A-Z][A-Za-z0-9]*$/`).
3. **Slot naming**: must match the pattern `{module}.{entityLower}.{view}.{position}` (five parts for form fields). The component generator emits exactly this shape — any deviation breaks the contract.

## Invocation

```bash
npx tsx skills/development/frontend/extension-config/cli/scaffold-extension-config/index.ts \
  --spec '{"module":"hrm","appCode":"myapp","entities":["Employee","Contract"],"projectPath":"/path"}'
```

## Output

- `src/extensions/{module}Extensions.ts` — one file per module listing all entities' slots.

## Client usage

Register the config with the SmartStack extension system (usually in `main.tsx`
after the registry imports):

```ts
import { registerExtensionConfig } from '@atlashub/smartstack';
import { hrmConfig } from './extensions/hrmExtensions';

registerExtensionConfig('hrm', hrmConfig);
```

Then fills can be provided anywhere in the app:

```tsx
<Fill slot="hrm.employee.list.before">
  <MyCustomBanner />
</Fill>
```
