Skip to main content
Code-defined skills are Python classes that implement robot behaviors with explicit logic. Three things make them easy to write: The agent reads your code. Your guidelines() and your execute() signature are the API contract — the agent sees execute(target: str, speed: float = 0.5) and knows exactly how to call your skill. Type hints matter.
Dependencies are one-line type annotations. Declare what you need and the system injects it — no wiring in __init__:
Then just use them. No callbacks, no message passing, no None checks — a declared feed is guaranteed before execute() starts:

The Skill Class

Every code-defined skill extends Skill and needs two things: guidelines(), which tells the agent when to use the skill, and execute(), which does the work. guidelines() is the prompt the agent reads about your skill, so write it out explicitly — it’s the part you’ll come back and tune when the robot calls your skill at the wrong moment.
The class name is the skill name: MySkill is callable as my_skill. Put it in any .py file in a workspace package — the file name doesn’t matter, and one file can define several skills.
Once you’ve written a few skills, you can drop guidelines() and let the class docstring stand in for it — the two mean exactly the same thing to the agent:
Most shipped skills are written this way, so you’ll see it when you read them. It’s shorter, but it hides the fact that this text is a prompt going to a model. Prefer the explicit guidelines() until that’s second nature.

Skill Results

Return the message. A plain string is success:
To fail, call self.fail() — it raises, so there’s no error branch to thread back through your code:
When the message isn’t enough, return a SkillOutput — the same type either way, with two optional extras:
.data is anything you like — a dict, or a small pydantic model when you want the caller’s editor to know the fields. The shipped move_straight and turn_in_place skills both do this, which is why a caller can write result.data.traveled_m.

Cancellation

The user or agent can stop a running skill at any time. Cancellation is the framework’s job, not yours — write your loops as if it couldn’t happen. The only rule: use self.sleep() instead of time.sleep().
Every blocking call the framework gives you raises SkillCancelled on a stop. The base is braked and the arm halted automatically, and the run reports CANCELLED — you don’t catch it, and you don’t need a cancel() method. Cleanup belongs in a try/finally inside execute(). self.on_cancel(hook) exists only to forward a cancel to an external action goal. time is still fine for measuringtime.time() and time.monotonic() for deadlines and elapsed checks. The rule is only about blocking.

Feedback

Send progress updates during long-running skills. The agent reads feedback in real time and can act on it — for example, canceling the skill or triggering another one immediately:

Speech, storage, and waiting

The base class also provides:

Next Steps