From doover-development
Add a Doover widget to an existing app. Use when users want to add a remote UI component/widget to their Doover application, scaffold a new widget from the widget template, or need help setting up Module Federation widgets.
npx claudepluginhub getdoover/doover-skills --plugin doover-developmentThis skill uses the workspace's default tool permissions.
Add a widget (remote UI component) to an existing Doover application. This skill scaffolds a widget from the template and optionally customizes it through a phase-based workflow.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Checks Next.js compilation errors using a running Turbopack dev server after code edits. Fixes actionable issues before reporting complete. Replaces `next build`.
Add a widget (remote UI component) to an existing Doover application. This skill scaffolds a widget from the template and optionally customizes it through a phase-based workflow.
This skill guides the process of adding a widget to a Doover app step-by-step. Each phase handles a specific part of the process, with clear completion criteria before proceeding to the next.
Workflow:
Phases R, 3, and 4 run when the widget needs customization beyond the template boilerplate — either because a widget description was provided (describing what the widget should do) or because reference repos were supplied. When just adding a blank widget scaffold, only Phases 2 and 5 run.
skills/doover-add-widget/
├── SKILL.md # This file — orchestrator and routing
└── references/
├── phase-2-config.md # Phase 2: Scaffold widget boilerplate
├── phase-r-refs.md # Phase R: Extract JS/UI reference patterns (optional)
├── phase-3-plan.md # Phase 3: Plan widget JS implementation
├── phase-4-build.md # Phase 4: Build widget JS code
└── phase-5-check.md # Phase 5: Validate widget build
External dependency: Platform documentation lives in skills/doover-platform-docs/ (the doover-platform-docs skill). Subagents are given {platform-docs-path} to locate widget-specific docs (widget-architecture.md, widget-hooks.md).
Phase files are loaded on-demand. Do not read all phases upfront.
State is stored in the app directory, not the skill directory:
{app-dir}/
└── .appgen/
├── WIDGET_PHASE.md # Widget phase state, name variants, references
├── WIDGET_REFERENCES.md # JS/UI reference patterns (created in Phase R, optional)
└── WIDGET_PLAN.md # Widget build plan (created in Phase 3)
The .appgen/WIDGET_PHASE.md file tracks:
These state files are separate from the appgen state files (PHASE.md, PLAN.md, REFERENCES.md) to avoid conflicts when add-widget is invoked as part of the appgen workflow.
The widget name is derived in four formats:
| Format | Example | Used For |
|---|---|---|
| PascalCase | MyCustomWidget | MF scope, JS filename, component function |
| kebab-case | my-custom-widget | Folder name, npm package name |
| snake_case | my_custom_widget | doover componentUrl, file deployment name |
| Title Case | My Custom Widget | Display name |
The rename script in the widget template accepts any format and derives all four.
| Phase | Name | Description |
|---|---|---|
| 2 | Config | Scaffold widget boilerplate (clone template, rename, integrate) |
| R | References | Extract JS/UI patterns from reference repos (optional) |
| 3 | Plan | Analyze requirements, load widget platform docs, create WIDGET_PLAN.md |
| 4 | Build | Write widget JS component code from WIDGET_PLAN.md |
| 5 | Check | Validate widget build and configuration |
Rules:
.appgen/WIDGET_PHASE.md status before loading next phaseEach phase runs in an isolated subagent to manage context efficiently.
.appgen/WIDGET_PHASE.md with widget statereferences/phase-{N}-*.md.appgen/WIDGET_PHASE.md.appgen/WIDGET_PHASE.md with resultsdoover_config.jsongh) authenticatedWhen this skill is invoked:
From appgen: App directory is provided in the prompt → use it directly, skip the question.
Standalone: Check if the user specified a path in their prompt. If not, use AskUserQuestion:
Verify that doover_config.json exists in the resolved directory. If it does not exist, STOP and tell the user this must be run from within a Doover app directory that has a doover_config.json.
Store this as {app-dir}.
From appgen: Widget name variants are provided in the prompt → use them directly, skip the question.
Standalone: Check if the user provided a widget name in their prompt (e.g., "add a widget called MyDashboard"). If not, use AskUserQuestion:
Accept any naming format (PascalCase, kebab-case, snake_case, or space-separated words).
Derive all name variants using these rules:
Report the derived names to the user before proceeding:
Widget name:
PascalCase: {PascalCase}
kebab-case: {kebab-case}
snake_case: {snake_case}
Title Case: {Title Case}
This step always runs, regardless of whether the skill was invoked from appgen or standalone. Widgets are visually bespoke — the appgen app description alone is rarely detailed enough to drive the widget's UI design. This step gathers the specifics.
From appgen (app description available):
Present the existing app description to the user and ask for widget-specific detail using AskUserQuestion:
"The app description from the project setup is:
{app description from PHASE.md}
Widgets need more visual and behavioral detail than a general app description provides. Please describe how this widget should look and behave — for example:
- What data should be displayed and how (tables, charts, status cards, etc.)?
- What user controls are needed (buttons, forms, toggles, etc.)?
- What layout or visual arrangement do you have in mind?
- Any specific channels, data fields, or interactions?"
Options: "I'll describe it" / "Just use a blank scaffold for now"
If user selects "I'll describe it", they provide free-text input. If they select "blank scaffold", set needs_customization: false.
Standalone (no app description):
Use AskUserQuestion:
"Widgets are bespoke UI components — describe in detail how this widget should look and behave. For example:
- What data should it display and how (tables, charts, status indicators, etc.)?
- What user controls or interactions are needed?
- What layout or visual arrangement do you have in mind?
- What channels or data sources should it use?"
Options: "I'll describe it" / "Just add a blank scaffold"
If user selects "I'll describe it", they provide free-text input. If they select "blank scaffold", set needs_customization: false.
After this step:
widget_description, set needs_customization: truewidget_description to "blank scaffold", set needs_customization: falseFrom appgen: References are passed in the prompt (or "none") → skip this step.
Standalone: Use AskUserQuestion:
If yes → enter the reference-gathering loop:
Reference-gathering loop (repeat until user says no more):
Use AskUserQuestion with two questions in a single call:
Record the answers as a numbered reference (Reference 1, Reference 2, etc.) with the reference type:
repo — code repository (local dir or GitHub)image — design mockup, screenshot, or visual referenceasset — image file the user wants included in the widget itselfUse AskUserQuestion with one question:
If "Yes, add another" → go back to step 1
If "No, that's all" → exit loop, set has_references: true, proceed
The platform documentation lives in the doover-platform-docs skill, located relative to this skill:
{platform-docs-path} = {this-skill-path}/../doover-platform-docs{platform-docs-path}/references/index.md)Create {app-dir}/.appgen/ directory if it doesn't exist.
Create {app-dir}/.appgen/WIDGET_PHASE.md:
# Widget Phase State
## Widget Details
- PascalCase: {PascalCase}
- kebab-case: {kebab-case}
- snake_case: {snake_case}
- Title Case: {Title Case}
- App directory: {app-dir}
## App Description
{app description from appgen PHASE.md, or "N/A" if standalone}
## Widget Description
{detailed widget description from Step 3, or "blank scaffold"}
## Current Phase
- Phase: Phase 2 - Config
- Status: pending
## Completed Phases
(none yet)
## Customization
- has_references: {true/false}
- needs_customization: {true/false}
## References
{If has_references is true:}
### Reference List
1. Location: {path, github org/repo, or image URL}
Type: {repo|image|asset}
Extract: {what to extract, or how to use the image}
(Repeat for each reference collected.)
Execute phases sequentially using the Task tool. After each phase, check WIDGET_PHASE.md to confirm completion before proceeding.
Phase flow:
has_references: Phase R (References) — extract JS/UI patternsneeds_customization: Phase 3 (Plan) → Phase 4 (Build) — design and implementPhase 2 Subagent (Config):
Task tool with:
subagent_type: "general-purpose"
prompt: |
Execute Phase 2 (Config) of the add-widget skill.
App directory: {app-dir}
Skill location: {path-to-this-skill}
Widget PascalCase: {PascalCase}
Widget kebab-case: {kebab-case}
Widget snake_case: {snake_case}
Instructions:
1. Read {skill-path}/references/phase-2-config.md
2. Read {app-dir}/.appgen/WIDGET_PHASE.md for widget details
3. Follow the scaffolding steps:
- Clone getdoover/widget-template into {app-dir}/_widget-tmp
- Run rename.js with {PascalCase}
- Move widget/ folder to {app-dir}/{kebab-case}/
- Delete _widget-tmp
- Update doover_config.json with file_deployments + deployment_channel_messages
- Update .gitignore with widget patterns
- Run npm install in {app-dir}/{kebab-case}/
4. Update {app-dir}/.appgen/WIDGET_PHASE.md with completion status
5. Return a summary including:
- Widget directory created
- doover_config.json entries added
- npm install result
- Any errors
Phase R Subagent (References — Conditional):
After Phase 2, read has_references from WIDGET_PHASE.md:
true → spawn Phase R subagent belowfalse → skip to Phase 3 checkTask tool with:
subagent_type: "general-purpose"
prompt: |
Execute Phase R (References) of the add-widget skill.
App directory: {app-dir}
Skill location: {path-to-this-skill}
Instructions:
1. Read {app-dir}/.appgen/WIDGET_PHASE.md for the reference list
2. Read {skill-path}/references/phase-r-refs.md for full instructions
3. For each reference: resolve ambiguity, acquire source, extract JS/UI patterns only
4. Write {app-dir}/.appgen/WIDGET_REFERENCES.md with curated patterns
5. Cleanup any temp clone directories
6. Update {app-dir}/.appgen/WIDGET_PHASE.md with completion status
7. Return a summary including:
- References processed and skipped
- JS/UI aspects extracted
- Any errors
Phase 3 Subagent (Plan — Conditional):
After Phase R (or Phase 2 if no references), check needs_customization from WIDGET_PHASE.md:
true → spawn Phase 3 subagent belowfalse → skip to Phase 5Task tool with:
subagent_type: "general-purpose"
prompt: |
Execute Phase 3 (Plan) of the add-widget skill.
App directory: {app-dir}
Skill location: {path-to-this-skill}
Platform docs: {platform-docs-path}
Instructions:
1. Read {app-dir}/.appgen/WIDGET_PHASE.md for widget details and description
2. Read {skill-path}/references/phase-3-plan.md for full instructions
3. Load widget platform documentation:
- {platform-docs-path}/references/widget-architecture.md
- {platform-docs-path}/references/widget-hooks.md
4. Read the current widget component: {app-dir}/{kebab-case}/src/{PascalCase}.js
5. If {app-dir}/.appgen/WIDGET_REFERENCES.md exists, read it for reference patterns
6. Analyze requirements and resolve ambiguity via AskUserQuestion
7. Create {app-dir}/.appgen/WIDGET_PLAN.md with complete widget design
8. Update {app-dir}/.appgen/WIDGET_PHASE.md with completion status
9. Return a summary including:
- Data channels planned
- User interactions planned
- External deps needed
- Questions asked
- Brief plan summary
Phase 4 Subagent (Build — Conditional):
Only spawn if Phase 3 completed (WIDGET_PLAN.md exists).
Task tool with:
subagent_type: "general-purpose"
prompt: |
Execute Phase 4 (Build) of the add-widget skill.
App directory: {app-dir}
Skill location: {path-to-this-skill}
Platform docs: {platform-docs-path}
IMPORTANT: Do NOT use AskUserQuestion. All requirements are in WIDGET_PLAN.md.
If the plan is unclear, report this as an error.
Instructions:
1. Read {app-dir}/.appgen/WIDGET_PHASE.md for widget name and paths
2. Read {app-dir}/.appgen/WIDGET_PLAN.md for implementation details
3. Read {skill-path}/references/phase-4-build.md for full instructions
4. Load widget platform documentation for correct patterns:
- {platform-docs-path}/references/widget-architecture.md
- {platform-docs-path}/references/widget-hooks.md
5. Install external dependencies if listed in WIDGET_PLAN.md
6. Write widget JS component code following the plan exactly
7. Update {app-dir}/.appgen/WIDGET_PHASE.md with completion status
8. Return a summary including:
- Files created/modified
- Hooks used
- External deps installed
- Component structure
- Any errors
Phase 5 Subagent (Check):
Always runs as the final phase.
Task tool with:
subagent_type: "general-purpose"
prompt: |
Execute Phase 5 (Check) of the add-widget skill.
App directory: {app-dir}
Skill location: {path-to-this-skill}
Instructions:
1. Read {app-dir}/.appgen/WIDGET_PHASE.md for widget name and paths
2. Read {skill-path}/references/phase-5-check.md for full instructions
3. Run validation checks:
- npm run build in {app-dir}/{kebab-case}/
- Verify assets/{PascalCase}.js exists
- Verify doover_config.json entries
- Verify file structure
4. Update {app-dir}/.appgen/WIDGET_PHASE.md with validation results
5. Return a summary including:
- Checks passed
- Checks failed (with details)
- Recommended fixes (if any)
After all phases complete, report to the user:
Widget "{PascalCase}" added successfully.
Files created:
{kebab-case}/ — widget source directory
{kebab-case}/rsbuild.config.ts — RSBuild + Module Federation config
{kebab-case}/ConcatenatePlugin.ts — bundles output into single JS file
{kebab-case}/package.json — dependencies
{kebab-case}/src/{PascalCase}.js — widget component (edit this)
assets/{PascalCase}.js — built widget output
Config updated:
doover_config.json — file deployment + ui_state entry added
Validation:
{Phase 5 results summary}
Next steps:
Edit {kebab-case}/src/{PascalCase}.js to customize your widget
cd {kebab-case} && npm run build
If a subagent reports failure:
WIDGET_PHASE.md should remain "in_progress"WIDGET_PHASE.md and resume from the current phaseImportant: The deployment_channel_messages field in doover_config.json is a Doover 1.x feature only. In Doover 2.0, this field is not processed. For widgets to appear in the UI interpreter, the app's processor must programmatically push ui_state on deployment.
The processor should use pydoover's Application class (pydoover>=0.4.18) with RemoteComponent:
from pydoover.cloud.processor import Application
from pydoover.ui import RemoteComponent
WIDGET_NAME = "{PascalCase}"
FILE_CHANNEL = "{snake_case}"
class MyApp(Application):
async def setup(self):
self.ui_manager.set_children([
RemoteComponent(
name=WIDGET_NAME,
display_name=WIDGET_NAME,
component_url=FILE_CHANNEL,
),
])
async def on_aggregate_update(self, event):
await self.ui_manager.push_async(even_if_empty=True)
Key points:
component_url must match the file_deployments channel name (snake_case)deployment_channel_messages can be kept for Doover 1.x backward compat, but the processor is authoritative for Doover 2.0Application subclass, add the RemoteComponent to the existing setup() method{app-dir}/
├── doover_config.json (updated with widget entries)
├── .appgen/
│ ├── WIDGET_PHASE.md (widget phase tracking)
│ ├── WIDGET_REFERENCES.md (if Phase R ran)
│ └── WIDGET_PLAN.md (if Phase 3 ran)
├── {kebab-case}/ (widget folder)
│ ├── ConcatenatePlugin.ts
│ ├── package.json
│ ├── rsbuild.config.ts
│ └── src/
│ └── {PascalCase}.js (widget component)
├── assets/ (created on first build)
│ └── {PascalCase}.js (built output)
└── ... (other app files)