# Skill & Package Nodes

> Applies to: ChengOS v0.2.0+ ｜ Last verified: 2026-09-25 ｜ Source: `crates/cheng-nodes/src/nodes/builtin/tools`

Skills as objects you can build and check, not just run. `tools/skill` executes one.
`tools/write_skill_package` produces a package, and `tools/validate_skill_spec` checks it against the
package format before anyone tries to publish or import it — validation before distribution is the
whole point, since a published artifact is immutable. `tools/cli_install` runs the install steps a
package declares.

## Execute Skill — `tools/skill`

Find and execute registered Skill by name

### Inputs

| Field | Type | Required | Default | Description |
|---|---|:-:|---|---|
| `skill_name` | string | ✅ | — | Name of the skill to execute (unique slug) |
| `input_data` | any | — | `null` | Input data passed to the workflow (optional) |
| `timeout_seconds` | integer | — | `3600` | Maximum wait time in seconds, default 3600 |
| `async_execution` | boolean | — | `false` | If true, returns immediately after starting workflow without waiting for completion |
| `env_vars` | object | — | `null` | Environment variables from skill credential injection node (optional) |

### Outputs

| Field | Type | Description |
|---|---|---|
| `status` | string | Skill execution status (see SkillOutput::status docs for the closed enum and frontend mapping) |
| `skill_id` | string | Matched skill ID |
| `skill_name` | string | Matched skill name |
| `execution_id` | string | Workflow execution ID (if started) |
| `outputs` | any | Workflow output data |
| `instructions` | string | Skill usage instructions (returned when no associated workflow) |
| `next_action` | string | Suggested next action (for LLM reference) |
| `error` | string | Error message |
| `missing_binaries` | array | Required binaries that were not found on PATH; install them or run the skill on a host that has them. |
| `missing_credentials` | array | List of credential bindings that could not be resolved; rendered into the credential-setup UI. |
| `approval_outcome` | string | Approval gate outcome for high-risk skills |
| `normalization_status` | string | Canonical spec normalization status |
| `policy_warnings` | array&lt;string&gt; | Runtime policy warnings |
| `operation` | string | Hub operation that produced this output |
| `data` | any | Operation-specific payload (discovery page or status report) |

## Write Skill Package — `tools/write_skill_package`

Restricted: safely writes a skill package under skills/<name>/. Respects admin locks and prevents path traversal.

### Inputs

| Field | Type | Required | Default | Description |
|---|---|:-:|---|---|
| `package` | object | — | — | Combined validated package from tools/validate_skill_spec. Prefer wiring Validate.validated_package into this single port. |
| `skill_name` | string | — | `""` | Stable skill slug. Accepts either a plain string or a JSON string of the form {"skill_name": "..."} (the generator-prompt shape). |
| `files` | array&lt;object&gt; | — | — | File payloads to write. Accepts either a structured array or a JSON string of the same array, so it can be wired directly to an ai/llm response that returns JSON text. |
| `force_admin_override` | boolean | — | `false` | Caller must set this only after capturing explicit admin consent to overwrite a locked skill. |
| `workspace_id` | string | — | `null` | Owning workspace for the imported skill. When set, the writer marks the skill private to this workspace via SKILL.md import metadata. Usually resolved from the execution context rather than supplied directly. |
| `source_snapshot` | object | — | `null` | Opaque source-snapshot artifact reference from tools/fetch_repository. Forward it verbatim; never reconstruct, enumerate, or omit source bytes. |

### Outputs

| Field | Type | Description |
|---|---|---|
| `success` | boolean | Indicates whether the package was validated, approved, and committed. |
| `status` | string | Stable status: written \| validation_failed \| approval_rejected \| approval_timed_out \| target_changed \| write_failed \| restricted. |
| `retryable` | boolean | True when the caller can plausibly repair and retry (e.g. a repairable validation failure or a stale target). |
| `skill_name` | string | Actual skill slug that was written (passed slug validation). |
| `written_paths` | array&lt;string&gt; | Skills-root-relative display paths of committed files (e.g. skills/<name>/SKILL.md). Absolute host paths are never returned. |
| `package_digest` | string | sha256 digest of the exact committed (sanitized + trusted-metadata) package bytes. Present on success. |
| `validation` | any | Structured validation result (valid, issue_fingerprint, issues, warnings) when the package failed deterministic validation. |
| `error` | string | Error description when the write fails. Present only when success=false. |
| `error_code` | string | Structured error code for upstream conditional branching. Present only when success=false. |

## Validate Skill Spec — `tools/validate_skill_spec`

Validates an LLM-generated skill package against the ChengOS CanonicalSkillSpec contract before files are written.

### Inputs

| Field | Type | Required | Default | Description |
|---|---|:-:|---|---|
| `name_path` | string | — | `null` | Combined skill name and package path input. This is the ergonomic single-port path for hand-built importer workflows: plain string is treated as package_path and skill_name is inferred from the final path segment; JSON string or object uses { "skill_name": "...", "package_path": "..." }. |
| `skill_name` | string | — | `""` | Stable skill slug. Must match the canonical spec's name. |
| `files` | array&lt;object&gt; | — | `[]` | File payloads the importer would write — typically SKILL.md, skill.yaml, workflow.json, plus references. Accepts either a structured array or a JSON string of the same shape, so it can be wired directly to an ai/llm response that returns JSON text. |
| `package_path` | string | — | `null` | Optional package path under the current workspace root. Accepts the skills/<name> path returned by fetch_repository and loads SKILL.md, skill.yaml, optional workflow.json, and optional references/*. |
| `sandbox_root` | string | — | `null` | Base directory that package_path resolves against. Fill this with the SAME sandbox_root the file-operations tool used when it wrote the package — the absolute directory shown in that tool's [Instance Constraints] block and in its view_file/edit_file results (e.g. /home/cheng/works/chengos/chengflow/skills). Then pass just the package folder name in package_path. This is how the validator finds the files the writer created. |
| `skip_review` | boolean | — | `false` | Trusted import: skip only the LLM/human review stage (promote to ready) for a known-safe package. Deterministic validation always runs. Honored only when the server enables CHENG_ALLOW_SKILL_SKIP_REVIEW, the package has no errors, and the tool surface is not high-risk. |
| `skip_review_reason` | string | — | `null` | Audit reason recorded when skip_review is honored (e.g. owned-package-reimport). |
| `trusted_source` | boolean | — | `false` | Advisory hint that the import source is trusted; does not by itself bypass any check. |

### Outputs

| Field | Type | Description |
|---|---|---|
| `valid` | boolean | Indicates whether the skill package passed validation. |
| `errors` | array&lt;string&gt; | Structured list of errors returned when validation fails. |
| `warnings` | array&lt;string&gt; | Warnings raised during validation. |
| `skill_name_out` | string | Runtime-conditional validated skill name. Present only when validation succeeds. |
| `normalization_status` | string | Normalization status of the spec after validation. |
| `canonical_spec` | any | Runtime-conditional canonical spec object. Present only when validation succeeds. |
| `sanitized_files` | array&lt;object&gt; | Original file list with skill.yaml replaced by the sanitized canonical spec (normalization.status forced to needs_review, execution.auto_execute forced to false, any LLM-injected frontmatter trust knobs removed). Downstream tools/write_skill_package MUST consume sanitized_files, never the raw LLM output, so the on-disk skill.yaml reflects the validated policy. |
| `sanitized_files_if_valid` | array | Runtime-conditional sanitized files. Present only when validation succeeds. |
| `validated_package` | object | Runtime-conditional package containing skill_name and sanitized files. Present only when validation succeeds. |
| `approval_summary` | string | Runtime-conditional JSON summary for admin approval. Present only when validation succeeds. |
| `backend_type` | string | The validated backend kind (workflow \| cli \| agentic \| …). Present once skill.yaml parses. |
| `llm_tools` | any | The LLM-visible tool surface (llm_tools.expose) for an agentic skill. Absent for non-agentic skills. |
| `requires_tool_review` | boolean | True when the exposed tool surface grants broad/high-risk access and must be reviewed by an admin before the skill can be promoted to ready. |
| `review_skipped` | boolean | True when a trusted skip-review was honored (promoted to ready without the LLM/human review stage). Deterministic validation still ran. |
| `issues` | array&lt;object&gt; | Structured validation issues with stable codes, severity, file/field locations, and retryability. Compatibility errors/warnings are rendered from the same collection. |
| `issue_fingerprint` | string | Stable fingerprint over sorted (severity, code, file, path) tuples; excludes message text and source content. Absent when there are no issues. |

## CLI Install Runner — `tools/cli_install`

Restricted: installs a CLI from the trusted package index. The LLM supplies only cli_name; this node selects the package manager, builds and validates the manifest, obtains approval for mutating steps, and executes the fixed argv commands.

### Inputs

| Field | Type | Required | Default | Description |
|---|---|:-:|---|---|
| `cli_name` | string | ✅ | — | Target CLI to install (for example gh, jq, or rg). It must resolve in the trusted package index. |
| `working_subdir` | string | — | `null` | Optional relative working subdirectory inside the execution boundary. |
| `timeout_ms` | integer | — | `120000` | Execution timeout in milliseconds. |
| `memory_mb` | integer | — | `512` | Memory limit in MB. |

### Outputs

| Field | Type | Description |
|---|---|---|
| `success` | boolean | Success |
| `status` | string | Stable machine-readable outcome: `ok`, `not_found`, or `error`. |
| `cli_name` | string | Cli Name |
| `provider_id` | string | Provider Id |
| `package_manager` | string | Package Manager |
| `steps` | array&lt;object&gt; | Steps |
| `error` | string | Error |

## Next steps

- [Skills System Overview](/docs/agent-skills-system)
- [Hub Nodes](/docs/nodes-hubs)
- [Using the CLI](/docs/cli-usage)
