AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified Apache-2.0 Self-run

Ros2 Skill

skill-adityakamath-ros2-skill-ros2-skill · by adityakamath

Controls and monitors ROS 2 robots directly via rclpy CLI. Use for ANY ROS 2 robot task: topics (subscribe, publish, capture images, find by type), services (list, call), actions (list, send goals), parameters (get, set, presets), nodes, lifecycle management, controllers (ros2_control), Nav2 navigation (go, cancel, status, waypoints, initial-pose), diagnostics, battery, system health checks, TF f…

No reviews yet
0 installs
8 views
0.0% view→install

Install

$ agentstack add skill-adityakamath-ros2-skill-ros2-skill

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-adityakamath-ros2-skill-ros2-skill)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Ros2 Skill? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

ROS 2 Skill

> ⚠️ ATTENTION — READ FIRST. This block overrides any conflicting instinct or rationalisation below. > > If profile show returns a non-empty summary, the session is Path A. In Path A: > > - Do not run topics find, topics type, tf list (for frame names), services find, or a params list velocity-limit sweep for any data the profile already holds. This is a Rule 14 violation, not "extra safety". > - The profile fields are the source of truth for: cmd_vel_topic, velocity message type, odometry topic, velocity safety limits, TF frame names, controller names, e-stop service, joint names. > - The only live calls allowed before motion in Path A: (1) control list-controllers for runtime active/inactive state, (2) one odom subscribe for the stationary check, (3) interface proto once per session for the payload template, (4) optional topics hz if you have not yet seen the odom rate. > - Forbidden rationalisations (if you find yourself thinking any of these, you are violating Rule 13 — stop and use the profile): > - "Maximum safety requires live introspection." > - "This catches runtime changes the profile might miss." > - "The profile is just a reference for static info; control and safety checks must be done live." > - "Profile data alone is never enough for actuation." > - "Using the profile is justified because it was just rescanned / because it is fresh." — The profile is the source of truth for static data always in Path A, not just when fresh. If you think the profile might be stale, run Rule 0.0b escalation (compare to live graph and stop on disagreement); do not bypass with live discovery. > - The single test before any introspection: "Is this field present in the profile?" — yes → use it; no → fall back to live for that one field only (Rule 0.0a); disagrees with live graph → stop and escalate (Rule 0.0b). The path does not flip. > > Detailed Path A operational rules: see "Path A operational summary" section below. Authoritative rules: references/RULES-CORE.md Rule 13 + Rule 14, references/RULES-MOTION.md Rule 3 Step 1. > > Structural enforcement (since v1.0.8): the CLI itself refuses topics find / services find calls that violate Path A. The refusal JSON names the matching profile field. profile show always includes a path_a_reminder block listing forbidden commands and the profile fields that replace them. Read that block at session start — it is the contract. If the guard refuses a call you genuinely need (Path B / debug), pass --ignore-profile; that override is logged.

Provides a structured JSON interface to a live ROS 2 robot. All commands output JSON. Every skill invocation follows three mandatory phases: resolve → act → verify. Resolve means: read every static field (topic names, message types, limits, frame names, controller names) from the profile first (Path A) and run a live call only when (a) the profile is absent / does not have that field (Path B / Rule 0.0a), or (b) the value is dynamic runtime state — controller active/inactive, robot stationary, payload template, odom rate. "Resolve" is not a synonym for "introspect everything live" — in Path A it collapses to zero live calls for profile-covered fields. Act issues the command. Verify reads post-action state. Never skip any phase, and never expand "resolve" into full live discovery when Path A is active.

Entry Point

Always use ros2_cli.py. Never call ros2 directly or run submodules directly.

python3 {baseDir}/scripts/ros2_cli.py  [subcommand] [args]
python3 {baseDir}/scripts/ros2_cli.py --help           # list all commands
python3 {baseDir}/scripts/ros2_cli.py  --help # list subcommands

{baseDir} is the path to the skill root directory. Resolve it from the skill metadata before running any command.


Session Start Checklist

Run once per session before the first task. Takes seconds. Catches the most common silent failures.

# Step 1 — health check
python3 {baseDir}/scripts/ros2_cli.py doctor

# Step 2 — daemon
python3 {baseDir}/scripts/ros2_cli.py daemon status
python3 {baseDir}/scripts/ros2_cli.py daemon start      # if not running

# Step 3 — simulated time (if applicable)
python3 {baseDir}/scripts/ros2_cli.py topics find rosgraph_msgs/msg/Clock

# Step 4 — lifecycle nodes (if present)
python3 {baseDir}/scripts/ros2_cli.py lifecycle nodes

# Step 5 — log directory (no command — note for diagnostics)
# Resolve: $ROS_LOG_DIR → $ROS_HOME/log/ → ~/.ros/log/

# Step 6 — graph snapshot
python3 {baseDir}/scripts/ros2_cli.py context          # topics (cap 50), services, actions, nodes

# Step 7 — robot profile (load if available; skip if absent)
python3 {baseDir}/scripts/ros2_cli.py profile show
# → robot_type, packages, launch_files, velocity_topics, safety_limits, sensor_flags
#
# Path classification (decided once, here):
#   profile show succeeded AND summary is non-empty  → Path A (profile-first)
#   profile show failed OR summary is empty          → Path B (live introspection for everything)
# Do NOT re-classify per task. Auto-rescan, freshness gating, and `profile scan` triggers:
# see RULES-PREFLIGHT.md Rules 0.0, 0.6, 0.7 (loaded at session start).
#
# If absent: build once, using these rules derived from the user's request:
#
#   ONE robot named  → pass --name   (pkg filter is auto-derived from the name)
#     python3 {baseDir}/scripts/ros2_cli.py profile scan --name lekiwi
#     python3 {baseDir}/scripts/ros2_cli.py profile scan --name depthai
#
#   MULTIPLE robots named  → pass --packages , plus --name 
#     python3 {baseDir}/scripts/ros2_cli.py profile scan --packages lekiwi,quest_teleop --name lekiwi_quest
#
#   NO robot name given (bare "create a profile" / "scan workspace")  → omit both flags
#     python3 {baseDir}/scripts/ros2_cli.py profile scan
#
# Use summary.safety_limits as the --max-vel / --max-ang ceiling (Rule 28).

Stop and tell the user if Step 1 reports critical failures. Re-run Step 3 before every timed command in simulation.


Path A operational summary (READ BEFORE EVERY ACTION when profile is loaded)

When profile show returned a non-empty summary at Step 7, the session is Path A. In Path A, the profile is the source of truth for static data — running live discovery for data the profile already holds is a rule violation (Rule 14), not extra safety.

Use directly from the profile — zero live calls:

| Need | Profile field | Forbidden in Path A | |---|---|---| | Velocity command topic | summary.cmd_vel_topic | topics find geometry_msgs/msg/Twist, topics find geometry_msgs/msg/TwistStamped | | Velocity message type | summary.velocity_topics[].type (entry matching cmd_vel_topic) | topics type | | Odometry topic | first value of summary.localization_config.fused_sources | topics find nav_msgs/msg/Odometry | | Velocity safety ceiling | summary.safety_limits.binding.{linear_x,linear_y,angular_z} | nodes list + params list sweep for max/limit/vel/speed/accel | | TF frame names | summary.tf_frames.{odom_frame,base_frame,map_frame} | tf list for frame names | | Controller names | summary.active_controllers | using control list-controllers to discover controller names (still required to check runtime state — see below) | | E-stop service | summary.estop_config.service_name | services find std_srvs/srv/SetBool | | Joint names / order / index | summary.hardware_interfaces[].joints, summary.joint_limits | params get /controller_manager:joints, parsing robot_description |

The only live calls still mandatory before motion in Path A (these read runtime state, which the profile cannot know):

  1. control list-controllers — confirm the controller named in summary.active_controllers is active right now.
  2. Subscribe --max-messages 1 --timeout 2 — confirm robot is stationary (Rule 9). This double-serves as a liveness check on the odom topic.
  3. interface proto — once per session, get the payload template (the type came from the profile, but the field layout did not).
  4. Optional: topics hz --duration 2 if publish-until is being used and you have not confirmed odom rate this session.

Path A violation — worked counterexample (what NOT to do):

User request: "drive forward 1 m". Profile is loaded.

❌ topics find geometry_msgs/msg/Twist             # Rule 14: profile has cmd_vel_topic
❌ topics find geometry_msgs/msg/TwistStamped      # Rule 14: profile has velocity_topics[].type
❌ topics find nav_msgs/msg/Odometry               # Rule 14: profile has fused_sources
❌ topics type                   # Rule 14: profile has the type
❌ nodes list && params list            # Rule 14: profile has safety_limits.binding

Cost of the violation: 5–30 seconds wasted, and if any topics find returns a topic that disagrees with the profile, the agent will silently use the live answer — masking the disagreement that Rule 0.0b is specifically designed to surface. Live discovery in Path A is not "extra safety"; it actively hides safety-relevant mismatches and delays the command.

Correct Path A motion sequence:

✅ Read VEL_TOPIC, VEL_TYPE, ODOM_TOPIC, MAX_VEL, MAX_ANG from profile  (0 live calls)
✅ interface proto                                            (1 live call, once/session)
✅ control list-controllers                                             (1 live call — runtime state)
✅ topics subscribe  --max-messages 1 --timeout 2           (1 live call — stationary + liveness)
✅ topics publish-until  '' --monitor    (the actual command)
✅ topics subscribe  --max-messages 1 --timeout 2           (post-motion verify, Rule 8)

3 live calls before the command + 1 after. Not the 6–9 live discovery calls of Path B.

If a profile field is missing (Rule 0.0a): fall back to live discovery for that one field only — the path does not flip. Other fields stay on the profile.

If a profile field's value disagrees with the live graph (e.g., summary.cmd_vel_topic is not in topics list): stop and escalate per Rule 0.0b. Do not silently retry with live discovery.


Critical Rules

Read the domain-specific rule files from references/ before the first action. These are hard constraints — not guidelines. Use [references/RULES.md](references/RULES.md) as the index to find the right file. Always load at session start: RULES-CORE.md, RULES-PREFLIGHT.md, and RULES-MOTION.md. Add RULES-DIAGNOSTICS.md when something fails. Key principles:

  1. Two-Path Model — profile first, live fallback. Path A (profile loaded) uses profile fields for static data and live calls only for runtime state; Path B (no profile) does full live introspection. Path is fixed for the session, decided once at Step 7. Never hardcode names, types, or limits. Full guardrails (field-presence rules, exact field names, escalation on runtime mismatch, auto-rescan gating, scan triggers) live in RULES-PREFLIGHT.md Rules 0.0, 0.0a, 0.0b, 0.6, 0.7 and RULES-CORE.md Rules 13, 14.
  1. Get the payload template. Before publishing or calling: interface proto . Copy the output. Modify only the fields the task requires. For non-primitive nested fields, run interface show recursively until all leaf fields are primitives.
  1. Verify every effect. A zero-error CLI response means the request was delivered — not that the effect occurred. Always follow up: params get, control list-controllers, subscribe to confirm, etc.
  1. Diagnose before reporting. On any error: introspect → self-correct → retry → report. Never ask the user to interpret an error you can check with the CLI.
  1. Safety is non-negotiable. Velocity limits, pre-motion odom checks, and post-motion verify are mandatory. Never bypass them.
  1. Context discovery is parallel. When discovering multiple independent facts (velocity topic, odom topic, limits, controller state), issue all commands simultaneously — never sequentially.

Common Operations

Introspection

python3 {baseDir}/scripts/ros2_cli.py context                             # session-start graph snapshot
python3 {baseDir}/scripts/ros2_cli.py context --include-schemas           # + schemas for all discovered topic types (opt-in; adds token cost)
python3 {baseDir}/scripts/ros2_cli.py topics list [--limit N]             # list topics (cap at N)
# Velocity topic — profile fast-path: use summary.cmd_vel_topic + summary.velocity_topics[].type
# Odom topic — profile fast-path: use summary.localization_config.fused_sources values
# Fallback (profile absent or field missing):
python3 {baseDir}/scripts/ros2_cli.py topics find geometry_msgs/msg/Twist # find velocity topic
python3 {baseDir}/scripts/ros2_cli.py topics find nav_msgs/msg/Odometry   # find odom topic
python3 {baseDir}/scripts/ros2_cli.py topics type                  # confirm exact type
python3 {baseDir}/scripts/ros2_cli.py topics details               # publisher count + QoS
python3 {baseDir}/scripts/ros2_cli.py topics hz                    # confirm topic is live
python3 {baseDir}/scripts/ros2_cli.py nodes list
python3 {baseDir}/scripts/ros2_cli.py services list
python3 {baseDir}/scripts/ros2_cli.py actions list
python3 {baseDir}/scripts/ros2_cli.py tf list                             # discover TF frames

Publishing

Path A (profile loaded): resolve ` and from the profile — do NOT run topics find / topics type`. See "Path A operational summary" above.

# Path A field resolution (no live calls):
#   VEL_TOPIC     = summary.cmd_vel_topic                          (e.g. /base_controller/cmd_vel)
#   VEL_TYPE      = summary.velocity_topics[].type                 (e.g. geometry_msgs/msg/TwistStamped)
#   ODOM_TOPIC    = first value of summary.localization_config.fused_sources   (e.g. /base_controller/odom)
#   MAX_VEL       = summary.safety_limits.binding.linear_x
#   MAX_ANG       = summary.safety_limits.binding.angular_z
# Path B (no profile): discover via `topics find geometry_msgs/msg/Twist` and
# `topics find geometry_msgs/msg/TwistStamped`, then `topics type` to confirm.

# Get payload template once per session — always; the type may be Twist OR TwistStamped
python3 {baseDir}/scripts/ros2_cli.py interface proto 
# Twist:        {"linear":{"x":0,"y":0,"z":0},"angular":{"x":0,"y":0,"z":0}}
# TwistStamped: {"header":{"stamp":{"sec":0},"frame_id":""},"twist":{"linear":{...},"angular":{...}}}

# Single publish
python3 {baseDir}/scripts/ros2_cli.py topics publish  '' \
  --max-vel  --max-ang 

# Closed-loop linear (Euclidean distance) — payload shape depends on VEL_TYPE
python3 {baseDir}/scripts/ros2_cli.py topics publish-until  \
  '' \
  --monitor  --field pose.pose.position --euclidean --delta 1.0 --timeout 60 \
  --max-vel  --max-ang 

# Closed-loop rotation (sign of --rotate MUST match sign of angular.z)
python3 {baseDir}/scripts/ros2_cli.py topics publish-until  \
  '' \
  --monitor  --rotate 90 --degrees --timeout 30 \
  --max-vel  --max-ang 

# Subscribe (read sensor data)
python3 {baseDir}/scripts/ros2_cli.py topics subscribe  --max-messages 1 --timeout 5

# Subscribe and return the first message only, then exit
python3 {baseDir}/scripts/ros2_cli.py topics echo-once  [--timeout 5]

--max-vel N / --max-ang N (Twist / TwistStamped only): clamp linear x/y/z to ±N m/s and angular.z to ±N rad/s inside the CLI before the message is sent. Pass summary.safety_limits.binding.linear_x / .angular_z from the profile (Path A) or the Rule 28 limit-scan result (Path B). Clamped axes are reported in velocity_clamped in the JSON output. Other message types pass through unchanged.

Services and Actions

python3 {baseDir}/scripts/ros2_cli.py services details           # request/response fields
python3 {baseDir}/scripts/ros2_cli.py services call  ''
python3 {baseDir}/scripts/ros2_cli.py actions details

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [adityakamath](https://github.com/adityakamath)
- **Source:** [adityakamath/ros2-skill](https://github.com/adityakamath/ros2-skill)
- **License:** Apache-2.0

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.