add-app-skill
SkillDev toolsScaffold a new skill in an existing app (BaseSkill, schemas, app.yml, i18n)
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the add-app-skill skill
What this skill tells your AI
The instructions your AI receives, as published by glowingkitty/openmates in .agents/skills/add-app-skill/SKILL.md and read by ahel’s review.
Arguments
Parse $ARGUMENTS into three parts:
appId— app directory name (e.g.,web,news,travel)skillId— kebab-case skill identifier (e.g.,deep-research)SkillClassName— PascalCase class name (e.g.,DeepResearchSkill)
If any are missing, ask the user before proceeding.
Instructions
You are adding a new skill to an existing app microservice. This touches backend Python code, YAML config, and i18n.
Step 1: Understand the Target App
Read these files to understand the app's patterns:
backend/apps/{appId}/app.yml— existing skills, embed types, categoriesbackend/apps/base_skill.py(lines 1-145) — BaseSkill interface- An existing skill in
backend/apps/{appId}/skills/— use as template backend/shared/python_schemas/app_metadata_schemas.py— AppYAML schema (for valid field names)
Step 1b: Create Skill Behavior Spec
Before scaffolding a new app skill, run create-plan or create an inline Plan using
docs/contributing/guides/spec-driven-development.md.
New app skills usually require a full spec because they define user-facing AI behavior, tool contracts, provider behavior, and often embed behavior. The spec must include:
- Sample user prompts
- Expected tool input parameters
- Expected skill output shape
- No-results behavior
- Provider-error behavior
- Permission, privacy, and API-key behavior
- Embed preview/fullscreen behavior if the skill produces embeds
- App-store examples that double as executable examples
The spec or inline contract must also define the phase gate for this skill:
- Implement and test the skill through OpenMates CLI against the dev server first, using real prompts or CLI contract tests that exercise the backend skill without browser state. This proof must use real CLI commands against the real dev API/WebSocket path; mocked OpenMates API calls, mocked SDK clients, stubbed servers, direct function calls, and fixture replay do not satisfy the gate.
- Implement and test npm SDK and pip SDK parity locally against the dev server for the same callable behavior when the skill is exposed programmatically. After local CLI and SDK evidence is green, reproduce or wire the same coverage into GitHub Actions for CI/daily tests.
- Implement web app surfaces, embeds, and app-store examples only after CLI and SDK parity are green.
- Run deployed Playwright visual smoke for larger web/UI surfaces in both laptop and mobile viewports, fixing and redeploying any objective rendering, error, loading, or responsiveness issue, then ask the user to confirm the deployed dev web behavior works and looks correct before starting any Apple parity work. Use Firecrawl only as a recorded fallback when Playwright is impractical or blocked. Playwright specs alone are not enough.
- Start Apple parity only after CLI, SDK, web, and user-confirmation evidence are complete, or after the spec records an explicit waiver/blocker.
Step 2: Create the Skill File
Create backend/apps/{appId}/skills/{skill_file}.py where skill_file is skillId with hyphens replaced by underscores.
Follow this structure exactly:
"""
{SkillClassName} — {brief description}.
Architecture: docs/architecture/app_skills.md
"""
import logging
from typing import Dict, Any, Optional
from pydantic import BaseModel, Field
from backend.apps.base_skill import BaseSkill
logger = logging.getLogger(__name__)
class {SkillName}Request(BaseModel):
"""{description}."""
# Define input fields from tool_schema
class {SkillName}Response(BaseModel):
"""{description}."""
success: bool = Field(default=False)
# Define output fields
class {SkillClassName}(BaseSkill):
"""
{Description of what this skill does}.
"""
async def execute(
self,
# Skill-specific params (must match tool_schema properties)
secrets_manager=None,
cache_service=None,
encryption_service=None,
directus_service=None,
user_id: Optional[str] = None,
chat_id: Optional[str] = None,
**kwargs
) -> {SkillName}Response:
"""Execute the skill."""
try:
# Implementation here
return {SkillName}Response(success=True)
except Exception as e:
logger.error(f"{SkillClassName} error: {e}", exc_info=True)
return {SkillName}Response(success=False, error=str(e))
Step 3: Register in app.yml
Add to the skills: list in backend/apps/{appId}/app.yml:
- id: {skillId}
name_translation_key: {appId}.{skill_id_underscored}
description_translation_key: {appId}.{skill_id_underscored}.description
icon_image: {icon}.svg
preprocessor_hint: >
Natural language description for AI model routing
providers:
- name: OpenMates
no_api_key: true
class_path: backend.apps.{appId}.skills.{skill_file}.{SkillClassName}
tool_schema:
type: object
properties:
# Define input parameters
required:
# List required params
If the skill produces embeds, also add an embed_types: entry.
Step 4: Add i18n Entries
Add skill name and description to frontend/packages/ui/src/i18n/sources/skills.yml (all 20 locales).
Then rebuild:
cd frontend/packages/ui && npm run build:translations
Step 5: Add App-Store Examples
Every new app skill must include app-store examples so the skill details page can show realistic preview cards.
If the skill produces embeds:
- Run or script at least two real CLI skill requests that cover the main provider/result shapes.
- Create
frontend/packages/ui/src/components/embeds/{appId}/{SkillName}EmbedPreview.examples.tsnext to the preview component. - Export an array of flat preview props matching the preview component, with
query_translation_keyvalues undersettings.app_store_examples.{appId}.{skill_id_underscored}.<n>. - Add those query labels to
frontend/packages/ui/src/i18n/sources/settings/app_store_examples.yml.
If the skill does not produce embeds, add equivalent user-facing examples in backend/apps/{appId}/app.yml using the existing example_entries or example_translation_keys pattern for that app.
Step 6: Create Test Script (Optional)
If scripts/test_skills/ exists, create test_{skill_id_underscored}.py following the pattern of other test scripts in that directory.
Step 7: Check for Embed Need
Ask the user: "Does this skill produce embeds that need a frontend component?"
If yes, suggest running /add-embed-type {appId} {skillId} {SkillName} next.
Rules
- Skills must NOT import from other skills — shared logic goes to
BaseSkillorbackend/shared/ - All
execute()params must matchtool_schema.propertiesnames exactly - Use
logger = logging.getLogger(__name__)— neverprint() - Type hints on all function parameters and return values
- Pydantic models use
PascalCase— end request models withRequest, response withResponse - Do not add
stage; implemented skills are enabled by default unlessdefault_enabled: falseis explicitly needed - App-store examples are required for every new skill; do not ship a skill with an empty examples section
Signals
- GitHub stars
- 46
- Forks
- 3
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
add-app-skill- Source
- github.com/glowingkitty/openmates