# Skills System Overview

> Applies to: ChengOS v0.1.0+ ｜ Last verified: 2026-08-12 ｜ Source: `chengflow/skills/`, `crates/cheng-core/src/domain/skill.rs`, `crates/cheng-core/src/domain/active_skill.rs`, `crates/cheng-nodes/src/nodes/builtin/tools/skills_hub`, `crates/cheng-api/src/rest/handlers/skill/mod.rs`

A **skill** packages a capability so an agent can find it and use it correctly. Where a tool is
one callable function, a skill is a named unit with instructions, input and output schemas, and
often reference documentation — the difference between handing a model a button and handing it
a manual.

## What a skill is

```text
Skill
  ├── name              ← slug: 2+ chars, alphanumerics, - and _ only
  ├── display_name, description
  ├── instructions      ← what the model reads to use it
  ├── workflow_id?      ← the workflow that implements it, when backed by one
  ├── input_schema / output_schema
  ├── category, tags
  ├── enabled, is_user_invocable
  ├── version           ← defaults to "1.0.0"
  └── workspace_id?     ← None means global
```

Skills are workspace-scoped, or global when `workspace_id` is `None`. `is_user_invocable`
separates skills a person can trigger from ones only an agent uses.

On disk a skill is a directory under `skills/<name>/` with a `SKILL.md` carrying YAML
front-matter:

```markdown
---
name: workflow-helper
description: Use this skill when the user has questions about building, debugging,
  or understanding ChengOS workflows — including node selection, port wiring,
  field configuration, error troubleshooting, and pattern recommendations.
---
```

The `description` is a **trigger condition**, not a summary. It is what the agent matches
against to decide whether this skill applies, so write it as "use this when…" rather than as a
blurb. Larger skills add a `references/` directory the instructions link into.

## The skills that ship

| Skill | Purpose |
|---|---|
| `workflow-helper` | Answers "how do I build / wire / configure / debug this workflow", querying live node schemas |
| `workflow-json-builder` | Guides a user through generating importable workflow JSON — read-only APIs only |
| `http-tool-guide` | How to fill in `tools/http` correctly |
| `browser-tool-guide` | How to choose `tools/browser` actions correctly |
| `skill-importer` | The pipeline for importing external skills |

Two of these are worth studying as patterns.

`workflow-helper` **queries the running API for live schemas** rather than embedding a snapshot,
because a node's schema changes with every build. An embedded copy would be wrong within a week.

`workflow-json-builder` is explicitly restricted to read-only endpoints — templates, node types,
node schemas, models — and is **forbidden** from calling create, update, execute, or save. A
skill can constrain what the agent may do, not only tell it what to do.

`SKILLS_INDEX.md` is the registry. Consulting it before creating a new skill is how you avoid
five near-duplicate capabilities.

## The Skills Hub

`tools/skills_hub` is the unified gateway. Connect it to an agent and the agent can:

| Operation | Returns |
|---|---|
| `list` | Discoverable skills in the current workspace |
| `search` | Skills matching a capability, name, description, or tag |
| `describe` | A skill's full descriptor: required inputs, execution requirements, declared Tool Hub tools |
| `credential_status` | Whether the required credentials are available |
| `approval_status` | Whether execution needs human approval |
| `execute` | Runs the skill with `input_data` |

The node's own description tells the model the rule that matters: **the catalog is dynamic.**
Use `list` or `search` to discover skills instead of assuming which exist, and call `describe`
before invoking any tool a skill declares — then use the returned tool identifiers, allowed
operations, and argument schemas. Do not guess skill or tool contracts.

`credential_status` and `approval_status` exist so an agent can find out *before* executing that
it lacks an API key or will hit an approval gate, instead of discovering it halfway through.

The older `tools/skill` node ("Execute Skill") finds and runs a skill by name. Prefer the hub
for anything new.

## Active skill bindings

When Skills Hub successfully resolves a skill through `describe`, it records an **active skill
binding**. If the model later omits `skill_name` on a Tool Hub `execute` command, that binding
supplies the lookup key.

Three properties make this safe, and they are worth spelling out because "remember which skill
we were using" is the kind of convenience that usually becomes a vulnerability:

1. **Bindings are established only by trusted runtime code** — the Skills Hub resolution path.
   They are never derived from model text, chat memory, or ordinary workflow input.
2. **A binding is only a lookup key.** Tool Hub re-resolves the skill against the current
   workspace, visibility, and canonical spec before *every* execution. A stale or revoked
   binding can never authorise a command on its own.
3. **The shortcut requires exactly one.** Uniqueness is judged by distinct canonical name plus
   workspace: the same skill resolved repeatedly collapses to one binding and stays unambiguous,
   while two different skills make the omission shortcut ambiguous and unavailable.

Note that the binding stores the **resolved canonical name**, not the lookup key the model
supplied.

## Importing external skills

`skill-importer` covers the pipeline for bringing in a skill from a URL, a Git repository, or
pasted text:

```text
fetch → LLM parse & generate → tools/validate_skill_spec → approval
      → tools/write_skill_package writes skills/<name>/
      → SkillFileWatcher syncs it into the database
```

The safety property is in the last step: an imported skill lands as **`needs_review` with
`enabled = false`**. It does not become available to agents because it was written to disk. A
human enables it, and until then it is not added to the tool catalog.

`skills/<name>/` is watched, so editing a `SKILL.md` on disk syncs into the database without a
restart. The `lock` / `unlock` endpoints control whether the watcher may overwrite a skill.

## REST surface

```text
GET|POST  /skills                       list, create
POST      /skills/export-workflow       turn a workflow into a skill
GET       /skills/uncompleted           drafts
GET       /skills/by-name/:name
POST      /skills/validate              validate skill YAML
POST      /skills/preview-tools         preview the tools a skill would expose
POST      /skills/:id/execute
POST      /skills/:id/enable | /disable
GET       /skills/:id/credential-status
GET       /skills/:id/runtime-policy
POST      /skills/:id/review
POST      /skills/:id/lock | /unlock
POST      /skills/:id/normalize
          …plus skill categories and CLI tool endpoints
```

`export-workflow` is the shortest path to your first skill: build a workflow, export it, and you
have a named capability with schemas an agent can discover.

## Writing a good skill

- **Write `description` as a trigger condition.** "Use this skill when the user asks X" beats
  "This skill does X".
- **Query live data instead of embedding snapshots** where the underlying thing changes.
- **State what is forbidden.** The most useful line in `workflow-json-builder` is the list of
  APIs it must not call.
- **Check `SKILLS_INDEX.md` first** and extend an existing skill rather than adding a fifth
  near-duplicate.
- **Keep instructions short and put detail in `references/`**, linked from the instructions —
  the agent loads what it needs rather than the whole manual every turn.

## Next steps

- [Tool Discovery & Tool Hub](/docs/agent-tools-and-hub) — how skills and tools meet.
- [ReAct Agent Guide](/docs/agent-react-guide) — the agent that uses them.
- [Connecting MCP Servers](/docs/agent-mcp-integration) — external capability instead of packaged.
