Claude
Cursor
Skill
skill-author
Author and structure practical Robium skills without unnecessary context or ceremony.
Virus-scanned
Reviewed automatically before listing.
Download
robium-ai-robium-skills_skill-author-498ea4e.zip · 6 KB
Install
skills CLI
npx skills add https://github.com/robium-ai/robium/tree/main/skills/skill-author
Claude Code
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install robium-ai-robium@llmmart
Git
git clone https://github.com/robium-ai/robium.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole robium-ai/robium collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Skill author
Treat every loaded line as a cost. A skill earns that cost by changing an ordinary coding agent's decision.
Start with the trigger
- Give the skill one clear job and a short name that matches its directory.
- Write a brief description that says what the skill helps accomplish and distinguishes it from its closest neighbors.
- Do not turn the description into a keyword inventory. Test realistic user requests when the boundary is uncertain.
- If an existing skill already owns the decision, deepen it instead of adding another trigger surface.
Write the entrypoint
- Frontmatter contains only
nameanddescription. - Organize
SKILL.mdaround one useful mental model, not a universal template. - Prefer short, natural bullets that a human can scan without decoding process language.
- Assume the agent can already code, search a repository, and read ordinary documentation. Keep only constraints, choices, and failure patterns that materially improve its work.
- Avoid fixed sequences unless order protects correctness, safety, money, or external state.
- Mention another skill only where evidence crosses that skill's boundary.
Put depth behind links
- Keep the common path in
SKILL.md. Put conditional commands, failure diagnosis, tuning, platform compatibility, schemas, and substantial examples in focused supporting files. - Link each supporting file at the point where it becomes useful and say when to read it. Do not load every reference by default.
- Reuse a focused existing file before creating another layer of navigation.
- Preserve exact Robium-observed values with their measured conditions. They are evidence, not universal defaults.
- Use current official documentation for volatile APIs, flags, packages, and configuration. Do not copy a manual into the skill.
- Keep executable helpers only when deterministic reuse justifies maintaining code; keep examples only when they show something prose cannot.
Test usefulness
- Read QUALITY.md for the review bar and mechanical checks.
- Run
uv run skills/skill-author/scripts/validate_skills.pyonce after a coherent batch of skill changes, not after every edit. It checks the lightweight contract, not writing quality. - For description, routing, or behavioral changes, review one common request, one likely failure, and one neighboring request that should route elsewhere. A typo or source-link refresh needs only the relevant mechanical check.
- Start with manual scenario review. Run bounded live-agent evals only for unresolved behavioral uncertainty, meaningful regression evidence, or an explicit request; do not rerun every model or prompt after each prose edit.
- Prefer observable behavior checks over tests that assert headings or exact
prose. Keep
evals.yamlonly when a real routing or task regression is worth preserving.
Done
- The entrypoint is small enough to load routinely.
- Optional detail is discoverable without being injected into every task.
- Every strong claim is either stable, sourced upstream, or tied to observed conditions.
- There is no version field, changelog, README, or format-only section.
Files (robium)
-
scripts
-
validate_skills.py 8.1 KB
# /// script # requires-python = ">=3.10" # dependencies = ["pyyaml"] # /// """Validate Robium's lightweight live-skill contract. The validator protects discovery and obvious packaging mistakes. It does not prescribe headings, prose, or a universal workflow; usefulness remains a human review question. """ import re import sys from pathlib import Path from urllib.parse import unquote import yaml ENGINE_DIR = Path(__file__).resolve().parents[3] / "scripts" / "engine" sys.path.insert(0, str(ENGINE_DIR)) from task_schema import TaskSchemaError, validate_tasks # noqa: E402 NAME_RE = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$") FRONTMATTER_KEYS = {"name", "description"} MAX_DESCRIPTION_CHARS = 240 MAX_BODY_LINES = 120 MARKDOWN_LINK_RE = re.compile(r"\[[^\]]+\]\(([^)]+)\)") BACKTICK_PATH_RE = re.compile( r"`((?:references|scripts|examples)/[^`\s]+|[A-Z][A-Z0-9-]*\.md)`" ) REMOTE_SCHEMES = ("http://", "https://", "mailto:") def _without_fenced_code(text: str) -> str: kept: list[str] = [] fence: str | None = None for line in text.splitlines(): marker = line.lstrip()[:3] if marker in {"```", "~~~"}: fence = None if fence == marker else marker if fence is None else fence continue if fence is None: kept.append(line) return "\n".join(kept) def _local_target(raw: str) -> str | None: value = raw.strip() if value.startswith("<") and ">" in value: target = value[1:value.index(">")] else: target = value.split(maxsplit=1)[0] target = unquote(target) if not target or target.startswith("#") or target.startswith(REMOTE_SCHEMES): return None target = target.split("#", 1)[0] if not target: return None return target def _check_links(skill_dir: Path) -> list[str]: errors: list[str] = [] repo_root = skill_dir.parents[1].resolve() for source in sorted(skill_dir.rglob("*.md")): text = _without_fenced_code(source.read_text(encoding="utf-8")) targets = list(MARKDOWN_LINK_RE.findall(text)) if source.name == "SKILL.md": targets.extend(BACKTICK_PATH_RE.findall(text)) for raw in targets: target = _local_target(raw) if target is None: continue resolved = (source.parent / target).resolve() try: resolved.relative_to(repo_root) except ValueError: errors.append( f"{skill_dir.name}: {source.relative_to(skill_dir)} link escapes repository: {target}" ) continue if not resolved.exists(): errors.append( f"{skill_dir.name}: {source.relative_to(skill_dir)} missing link target: {target}" ) entrypoint = _without_fenced_code((skill_dir / "SKILL.md").read_text(encoding="utf-8")) linked = { (skill_dir / target).resolve() for raw in MARKDOWN_LINK_RE.findall(entrypoint) + BACKTICK_PATH_RE.findall(entrypoint) if (target := _local_target(raw)) is not None } for support in sorted(skill_dir.glob("*.md")): if support.name == "SKILL.md" or support.name.lower() == "readme.md": continue if support.resolve() not in linked: errors.append( f"{skill_dir.name}: support file {support.name} is not linked from SKILL.md" ) return errors def _check_evals(skill_dir: Path) -> list[str]: path = skill_dir / "evals.yaml" if not path.exists(): return [] try: data = yaml.safe_load(path.read_text(encoding="utf-8")) or {} if not isinstance(data, dict): raise ValueError("top level must be a mapping") triggers = data.get("triggers", {}) if not isinstance(triggers, dict): raise ValueError("'triggers' must be a mapping") for side in ("positive", "negative"): cases = triggers.get(side) or [] if not isinstance(cases, list): raise ValueError(f"triggers.{side} must be a list") for case in cases: if not isinstance(case, dict) or not case.get("phrase"): raise ValueError(f"each triggers.{side} case needs a 'phrase'") if side == "negative" and "expect" in case: expected = case["expect"] if not isinstance(expected, str) or not expected.strip(): raise ValueError("negative trigger 'expect' must be a skill name") expected_dir = skill_dir.parent / expected if expected == skill_dir.name or not (expected_dir / "SKILL.md").exists(): raise ValueError( f"negative trigger 'expect' names unknown or current skill: {expected}" ) validate_tasks( data.get("tasks", []), repo_root=skill_dir.parents[1], skill_dir=skill_dir, ) except TaskSchemaError as exc: return [f"{skill_dir.name}: evals.yaml invalid: {exc}"] except Exception as exc: return [f"{skill_dir.name}: evals.yaml invalid: {exc}"] return [] def check_skill(skill_dir: Path) -> list[str]: errors: list[str] = [] path = skill_dir / "SKILL.md" if not path.exists(): return [f"{skill_dir.name}: missing SKILL.md"] text = path.read_text(encoding="utf-8") match = re.match(r"^---\r?\n(.*?)\r?\n---\r?\n(.*)$", text, re.DOTALL) if not match: return [f"{skill_dir.name}: missing or malformed frontmatter"] try: frontmatter = yaml.safe_load(match.group(1)) or {} except yaml.YAMLError as exc: return [f"{skill_dir.name}: frontmatter YAML error: {exc}"] if not isinstance(frontmatter, dict): return [f"{skill_dir.name}: frontmatter must be a mapping"] missing = FRONTMATTER_KEYS - set(frontmatter) extra = set(frontmatter) - FRONTMATTER_KEYS if missing: errors.append(f"{skill_dir.name}: frontmatter missing {', '.join(sorted(missing))}") if extra: errors.append(f"{skill_dir.name}: unsupported frontmatter fields: {', '.join(sorted(extra))}") name = frontmatter.get("name") if name != skill_dir.name: errors.append(f"{skill_dir.name}: frontmatter name {name!r} != directory name") if not isinstance(name, str) or not NAME_RE.fullmatch(name) or len(name) > 64: errors.append(f"{skill_dir.name}: name violates Agent Skills constraints") description = frontmatter.get("description") if not isinstance(description, str) or not description.strip(): errors.append(f"{skill_dir.name}: description missing") elif len(description.strip()) > MAX_DESCRIPTION_CHARS: errors.append( f"{skill_dir.name}: description {len(description.strip())} chars " f"(>{MAX_DESCRIPTION_CHARS})" ) body = match.group(2) if not body.strip(): errors.append(f"{skill_dir.name}: body is empty") body_lines = len(body.splitlines()) if body_lines > MAX_BODY_LINES: errors.append( f"{skill_dir.name}: body {body_lines} lines (>{MAX_BODY_LINES}); move conditional depth to support files" ) if re.search(r"^##\s+changelog\s*$", body, re.IGNORECASE | re.MULTILINE): errors.append(f"{skill_dir.name}: live skills do not carry changelogs") if any(path.name.lower() == "readme.md" for path in skill_dir.iterdir()): errors.append(f"{skill_dir.name}: use SKILL.md or a focused support file, not README.md") errors.extend(_check_links(skill_dir)) errors.extend(_check_evals(skill_dir)) return errors def main() -> None: skills_root = Path(__file__).resolve().parents[2] skill_dirs = sorted( path for path in skills_root.iterdir() if path.is_dir() and path.name != "_TEMPLATE" ) errors = [error for skill_dir in skill_dirs for error in check_skill(skill_dir)] for error in errors: print(f"FAIL: {error}") print(f"Checked {len(skill_dirs)} skills: {'FAIL' if errors else 'PASS'}") raise SystemExit(1 if errors else 0) if __name__ == "__main__": main()
-
-
evals.yaml 310 B
triggers: positive: - phrase: write a new robium skill for depth cameras source: learning-engine Phase 2b restructure negative: - phrase: absorb these learnings into the skills expect: learning-loop - phrase: mine the nav2 tutorials repo for patterns expect: mining tasks: [] -
QUALITY.md 2.7 KB
# A practical quality bar Use this as a review guide, not as a writing template. Skills can have different shapes when their work calls for it. ## Discovery - `name` matches the directory and uses lowercase kebab-case. - `description` is short, specific, and discriminates the nearest adjacent skill. - A likely user request can select the skill without an exhaustive keyword list. ## Entrypoint - One organizing idea makes the guidance easy to remember and extend. - The common path and important boundaries are visible without opening another file. - Every instruction changes a capable agent's decision; generic coding advice and repeated repository policy are removed. - Bullets sound like useful directions, not a compliance questionnaire. - The entrypoint stays comfortably below the mechanical limit. Passing the limit is not evidence that the file is lean. ## Conditional depth - Commands, failures, tuning, platform profiles, schemas, and long examples live in focused files when only some tasks need them. - Each support file has a clear reason to load and is linked from a relevant decision point. - Support files do not duplicate the entrypoint or reproduce an upstream manual. - Existing examples and scripts remain only when they are reusable and their provenance or validation status is clear. ## Evidence and scope - Stable concepts may be stated directly. Version-sensitive syntax and APIs point to current official documentation. - Observed values name the robot, platform, version, workload, or measurement that produced them. They are never silently promoted into defaults. - One proven application may justify a narrowly scoped platform or compatibility note. Require recurrence before turning it into common-path advice or a universal default. - Adjacent skills are reached only after the relevant subsystem boundary has been identified. ## Testing - The validator checks frontmatter, size, links, and optional eval structure. It deliberately does not prescribe headings or prose. - Routing evals cover meaningful ambiguity, not every synonym in the description. - Task checks verify user-visible behavior or a fragile reusable artifact, not Markdown wording. - Manual review asks whether the skill helps a capable agent act better while loading less context. ## Repository contract - Live skill frontmatter contains only `name` and `description`. - Live skills do not carry versions or changelogs. Git history is the normal record of change. - A skill may contain `SKILL.md` alone or the smallest set of support files it actually needs. No README or empty placeholder directories are required. - Robium provides practical knowledge and real reusable examples, never an invented robotics DSL. -
SKILL.md 3.3 KB
--- name: skill-author description: Author and structure practical Robium skills without unnecessary context or ceremony. --- # Skill author Treat every loaded line as a cost. A skill earns that cost by changing an ordinary coding agent's decision. ## Start with the trigger - Give the skill one clear job and a short name that matches its directory. - Write a brief description that says what the skill helps accomplish and distinguishes it from its closest neighbors. - Do not turn the description into a keyword inventory. Test realistic user requests when the boundary is uncertain. - If an existing skill already owns the decision, deepen it instead of adding another trigger surface. ## Write the entrypoint - Frontmatter contains only `name` and `description`. - Organize `SKILL.md` around one useful mental model, not a universal template. - Prefer short, natural bullets that a human can scan without decoding process language. - Assume the agent can already code, search a repository, and read ordinary documentation. Keep only constraints, choices, and failure patterns that materially improve its work. - Avoid fixed sequences unless order protects correctness, safety, money, or external state. - Mention another skill only where evidence crosses that skill's boundary. ## Put depth behind links - Keep the common path in `SKILL.md`. Put conditional commands, failure diagnosis, tuning, platform compatibility, schemas, and substantial examples in focused supporting files. - Link each supporting file at the point where it becomes useful and say when to read it. Do not load every reference by default. - Reuse a focused existing file before creating another layer of navigation. - Preserve exact Robium-observed values with their measured conditions. They are evidence, not universal defaults. - Use current official documentation for volatile APIs, flags, packages, and configuration. Do not copy a manual into the skill. - Keep executable helpers only when deterministic reuse justifies maintaining code; keep examples only when they show something prose cannot. ## Test usefulness - Read [QUALITY.md](QUALITY.md) for the review bar and mechanical checks. - Run `uv run skills/skill-author/scripts/validate_skills.py` once after a coherent batch of skill changes, not after every edit. It checks the lightweight contract, not writing quality. - For description, routing, or behavioral changes, review one common request, one likely failure, and one neighboring request that should route elsewhere. A typo or source-link refresh needs only the relevant mechanical check. - Start with manual scenario review. Run bounded live-agent evals only for unresolved behavioral uncertainty, meaningful regression evidence, or an explicit request; do not rerun every model or prompt after each prose edit. - Prefer observable behavior checks over tests that assert headings or exact prose. Keep `evals.yaml` only when a real routing or task regression is worth preserving. ## Done - The entrypoint is small enough to load routinely. - Optional detail is discoverable without being injected into every task. - Every strong claim is either stable, sourced upstream, or tied to observed conditions. - There is no version field, changelog, README, or format-only section.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.