Skip to content

Adding Skills

Adding skills for new apps is the most valuable contribution. Every new app skill makes the framework more useful for everyone. This page covers the process specifically for contributors.

TypeDifficultyImpact
Add UI elements for a new appEasy-MediumHigh
Write an Action for an existing appMediumHigh
Write a Workflow (multi-step)MediumHigh
Build a complete app SkillMedium-HardVery High
Terminal window
mkdir -p marketing_system/skills/whatsapp/actions
mkdir -p marketing_system/skills/whatsapp/workflows
touch marketing_system/skills/whatsapp/__init__.py
touch marketing_system/skills/whatsapp/actions/__init__.py
touch marketing_system/skills/whatsapp/workflows/__init__.py

Use the Skill Creator tab (live device stream with element overlay) to identify elements on the target app:

marketing_system/skills/whatsapp/elements.yaml
package: com.whatsapp
app_version: "2.24.x"
elements:
search_icon:
resource_id: "com.whatsapp:id/menuitem_search"
content_desc: "Search"
description: "Search icon in toolbar"
chat_input:
resource_id: "com.whatsapp:id/entry"
description: "Message text input field"
send_button:
resource_id: "com.whatsapp:id/send"
content_desc: "Send"
description: "Send message button"

Always provide at least 2 locator fields per element (e.g., resource_id + content_desc).

marketing_system/skills/whatsapp/actions/send_message.py
from marketing_system.skills.base import Action, ActionResult
class SendMessage(Action):
name = "send_message"
description = "Type a message and tap send"
def precondition(self, dev, xml):
return dev.find_bounds(xml, resource_id="com.whatsapp:id/entry") is not None
def execute(self, dev, text="Hello", **kwargs):
xml = dev.dump_xml()
bounds = dev.find_bounds(xml, resource_id="com.whatsapp:id/entry")
dev.tap(*dev.bounds_center(bounds))
dev.type_text(text)
xml = dev.dump_xml()
bounds = dev.find_bounds(xml, resource_id="com.whatsapp:id/send")
if bounds:
dev.tap(*dev.bounds_center(bounds))
return ActionResult(success=True)
return ActionResult(success=False, error="Send button not found")
marketing_system/skills/whatsapp/workflows/send_dm.py
from marketing_system.skills.base import Workflow
class SendDM(Workflow):
name = "send_dm"
description = "Send a DM to a contact"
def steps(self):
return [
("open_app", {}),
("search_contact", {"query": self.params["contact"]}),
("send_message", {"text": self.params["message"]}),
]
marketing_system/skills/whatsapp/__init__.py
from marketing_system.skills.base import Skill
def load():
skill = Skill.from_yaml("marketing_system/skills/whatsapp")
from .actions.send_message import SendMessage
skill.register_action(SendMessage)
from .workflows.send_dm import SendDM
skill.register_workflow(SendDM)
return skill
Terminal window
# Verify it loads
python3 -c "
from marketing_system.skills.whatsapp import load
s = load()
print(f'{s.name}: {len(s.elements)} elements, {s.list_actions()}, {s.list_workflows()}')
"
# Run on device
python3 -c "
from marketing_system.skills.whatsapp import load
from marketing_system.bots.common.adb import Device
s = load()
dev = Device()
action = s.get_action('send_message', dev)
result = action.run(text='Hello from the bot')
print(f'Success: {result.success}')
"
Terminal window
git checkout -b skill/whatsapp
git add marketing_system/skills/whatsapp/
git commit -m "Add WhatsApp skill: send_message action, send_dm workflow"

Include in the PR:

  • App name and version tested on
  • Device model and Android version
  • What actions and workflows are included
  • Screenshot of the skill working (appreciated but optional)

Before submitting:

  • skill.yaml has name, version, app_package, description
  • elements.yaml uses multiple locator fields per element
  • All actions have name and description
  • At least one action has a postcondition()
  • Skill loads without errors
  • Tested on a real device
  • No hardcoded serials or credentials

Even a skeleton with just skill.yaml and elements.yaml (no actions) is a valid contribution. It saves the next person from starting from scratch and provides the UI element mappings for the app.