doover-add-widget
npx skills add https://github.com/getdoover/doover-skills --skill doover-add-widget
Agent 安装分布
Skill 文档
Add Widget to Doover App
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.
Overview
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:
- Determine app directory and widget name (orchestrator)
- Scaffold widget boilerplate from template (Phase 2)
- Extract JS/UI patterns from reference repos (Phase R, optional)
- Plan widget JS implementation (Phase 3, optional)
- Build widget JS component code (Phase 4, optional)
- Validate widget build (Phase 5)
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.
Skill Directory Structure
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 Storage
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:
- Widget name variants (PascalCase, kebab-case, snake_case, Title Case)
- Widget/app description
- Current phase and status
- Completed phases
- Reference list (if any)
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.
Name Conventions
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.
Available Phases
| 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 |
Phase Gating Protocol
Rules:
- Each phase must complete before the next begins
- Check
.appgen/WIDGET_PHASE.mdstatus before loading next phase - Phase R is optional â only runs if references are provided
- Phases 3-4 are optional â only run if widget customization is requested
- Phase 5 always runs as the final phase
- Never skip phases or run phases out of order
Phase Execution Protocol
Each phase runs in an isolated subagent to manage context efficiently.
Orchestrator Responsibilities (this file)
- Determine app directory and widget name
- Determine customization scope (blank scaffold vs custom widget)
- Initialize
.appgen/WIDGET_PHASE.mdwith widget state - Spawn a Task subagent for each phase
- Report the subagent’s summary to the user
- Check completion and spawn next phase
Subagent Responsibilities
- Read the phase instructions from
references/phase-{N}-*.md - Read existing state from
.appgen/WIDGET_PHASE.md - Execute the phase steps
- Update
.appgen/WIDGET_PHASE.mdwith results - Return a summary to the orchestrator
Prerequisites
- An existing Doover app directory with a
doover_config.json - GitHub CLI (
gh) authenticated - Node.js 18+ available
Execution Instructions
When this skill is invoked:
Step 1: Determine App Directory
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:
- “Where is the Doover app you want to add a widget to?”
- Options: “Current directory” / “I’ll provide a path”
- If “I’ll provide a path”: ask for the absolute path to the app directory
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}.
Step 2: Determine Widget Name
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:
- “What would you like to name your widget?”
Accept any naming format (PascalCase, kebab-case, snake_case, or space-separated words).
Derive all name variants using these rules:
- Split words: Insert boundary before uppercase letters, replace non-alphanumeric with spaces, split on whitespace
- PascalCase: Capitalize first letter of each word, join with no separator
- kebab-case: Lowercase all words, join with hyphens
- snake_case: Lowercase all words, join with underscores
- Title Case: Capitalize first letter of each word, join with spaces
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}
Step 3: Gather Detailed Widget Description
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:
- If the user provided a detailed description â store as
widget_description, setneeds_customization: true - If the user chose blank scaffold â set
widget_descriptionto “blank scaffold”, setneeds_customization: false
Step 4: Gather References (Standalone Only)
From appgen: References are passed in the prompt (or “none”) â skip this step.
Standalone: Use AskUserQuestion:
- “Do you have any references you’d like to draw from? These can be code repos (for UI patterns) or images (mockups, screenshots, design references, or image assets to include in the widget).”
- Options: “No references needed” / “Yes, I have references”
If yes â enter the reference-gathering loop:
Reference-gathering loop (repeat until user says no more):
-
Use
AskUserQuestionwith two questions in a single call:- Question 1: “What is the reference?” (header: “Source”) â options: “Local file or directory path” / “GitHub org/repo” / “Image URL”
- Question 2: “What should be extracted or how should this be used?” (header: “Extract”) â options: “UI layout/component pattern” / “Data access/hook pattern” / “Visual design reference (image)” / “Image asset to include in widget” (user can type their own via Other)
-
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 itself
-
Use
AskUserQuestionwith one question:- “Would you like to add another reference?” (header: “More refs?”)
- Options: “No, that’s all” / “Yes, add another”
-
If “Yes, add another” â go back to step 1
-
If “No, that’s all” â exit loop, set
has_references: true, proceed
Step 5: Resolve Platform Docs Path
The platform documentation lives in the doover-platform-docs skill, located relative to this skill:
{platform-docs-path}={this-skill-path}/../doover-platform-docs- Verify the path exists (check for
{platform-docs-path}/references/index.md) - This path is passed to Phase 3 and Phase 4 subagents
Step 6: Initialize State
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.)
Step 7: Spawn Phase Subagents
Execute phases sequentially using the Task tool. After each phase, check WIDGET_PHASE.md to confirm completion before proceeding.
Phase flow:
- Always: Phase 2 (Config) â scaffold boilerplate
- If
has_references: Phase R (References) â extract JS/UI patterns - If
needs_customization: Phase 3 (Plan) â Phase 4 (Build) â design and implement - Always: Phase 5 (Check) â validate build
Subagent Invocation Templates
Phase 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:
- If
trueâ spawn Phase R subagent below - If
falseâ skip to Phase 3 check
Task 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:
- If
trueâ spawn Phase 3 subagent below - If
falseâ skip to Phase 5
Task 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)
Step 8: Report Summary
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
Error Handling
If a subagent reports failure:
- Report the error to the user
- The phase status in
WIDGET_PHASE.mdshould remain “in_progress” - Suggest the user can re-invoke the skill to retry
- Re-invoking will read
WIDGET_PHASE.mdand resume from the current phase
Doover 2.0: Processor Must Push ui_state
Important: 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_urlmust match thefile_deploymentschannel name (snake_case)deployment_channel_messagescan be kept for Doover 1.x backward compat, but the processor is authoritative for Doover 2.0- If the app already has a processor with an
Applicationsubclass, add theRemoteComponentto the existingsetup()method - When invoked from appgen, Phase 2w already handles the processor side â this note is for standalone use
Reference: App Directory Structure After Adding Widget
{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)