| name | add-extension |
| description | Scaffold a new Shell extension with hexagonal layers and TDD |
| user_invocable | true |
/add-extension
Scaffold a new Alder Shell extension with hexagonal architecture layers and
test-driven development.
Workflow
Step 1: Gather Requirements
Ask the user (or extract from $ARGUMENTS) for:
| Field | Required | Default | Description |
|---|
| name | yes | — | Extension name (kebab-case, e.g. settings) |
| scope | no | v1 | Release scope: v1 or v2 |
| description | yes | — | One-line description |
| contributions | no | — | ActivityBar icon, panels, routes, commands |
Step 2: Create Feature Directory
Create the hexagonal layer structure:
src/features/<name>/
├── domain/types.ts # Pure types — no React, no framework deps
├── domain/types.test.ts # Domain type tests (written FIRST)
├── application/ # Hooks, Zustand stores, orchestration
├── adapters/ # API clients, Tauri invoke wrappers
├── ui/ # React components
└── extension.tsx # Shell extension registration
Create empty directories for application/, adapters/, and ui/ with
.gitkeep files so they are tracked.
Step 3: Write Domain Types Test FIRST (TDD Red)
Create src/features/<name>/domain/types.test.ts:
import { describe, it, expect } from 'vitest';
describe('<Name> domain types', () => {
it('should define <Name> type with required fields', () => {
});
});
The test must compile and FAIL (RED) before proceeding.
Step 4: Create Domain Types (TDD Green)
Create src/features/<name>/domain/types.ts:
- Pure TypeScript types and interfaces only
- No React imports, no framework dependencies
- No imports from other features
- Export all public types
Step 5: Verify GREEN
pnpm test
All tests must pass before proceeding.
Step 6: Create Extension Registration
Create src/features/<name>/extension.tsx:
import type { Extension } from '@paulbreuler/shell';
export const <camelName>Extension: Extension = {
id: 'grove.<name>',
activate(ctx) {
},
deactivate() {
},
};
Follow the naming convention:
- Extension variable:
<camelName>Extension (e.g., settingsExtension)
- Extension ID:
grove.<name> (e.g., grove.settings)
Step 7: Wire into App.tsx
NOTE: Extension bootstrapping is not yet implemented in App.tsx. When
creating the first extension, you will need to add the extension registration
mechanism. Until then, document the extension in this file and wire it when
the Shell integration is built out.
Once extension bootstrapping exists in src/App.tsx:
- Import the extension:
import { <camelName>Extension } from './features/<name>/extension';
- Add to the extensions array passed to Shell
Step 8: Verify
pnpm check
pnpm test
Checklist
Before marking complete, verify:
Rules
- TDD is mandatory — write the test file BEFORE the implementation
- Domain layer has zero framework dependencies (no React, no Zustand, no Axios)
- No cross-feature imports — features are isolated modules
- All styling via
--grove-* design tokens, never raw CSS values
- Extension ID must be
grove.<name> to avoid collisions
- If the extension needs a Zustand store, it goes in
application/, not domain/
- This skill establishes the convention for
extension.tsx — no prior examples exist in the codebase