@package-system

Skills

The full skill protocol: agentskills.io-aligned SKILL.md, Neuralis frontmatter extensions, activation, and scripts.

A skill is a directory with a SKILL.md file — YAML frontmatter plus a markdown body — and optional scripts/, references/, and assets/ siblings. The format is aligned with agentskills.io, and the frontmatter parser is whitelist-policy: unknown fields are dropped silently, which is what lets skills written for other agent platforms load in Neuralis unchanged.

skills/
  deploy-checklist/
    SKILL.md
    scripts/
      verify.sh
    references/
      runbook.md

The skill's scope is the whole directory, enumerated at discovery into the contribution's files[] list (see the directory contract). A bare skill folder — SKILL.md plus scripts/, no package.json — also loads as a single-skill package whose root is the skill.

Frontmatter

---
name: deploy-checklist
description: One-line trigger sentence the model matches on to activate this skill.
disable-model-invocation: true
allowed-tools: [fs_list, fs_read]
credentials: [OPENAI_API_KEY]
maxSteps: 12
requiredFeatures: [platform.config]
---

# Title
Body. Use ${SKILL_DIR} / ${SKILL_URI} / ${SESSION_ID} / ${NEURALIS_API}.
Run scripts as `bash ${SKILL_DIR}/scripts/verify.sh`.

Standard fields:

FieldMeaning
nameSkill id (falls back to the folder name).
descriptionThe always-visible routing contract advertised in the per-agent package overview: state the outcome, natural request/failure trigger, and decisive distinction from neighboring skills. The detailed procedure stays in the activated body.
allowed-toolsTool allowlist — a YAML list (- name lines may sit at the key's own indent), or one line of names separated by commas or spaces (the Claude Code and the agentskills.io forms both read); a trailing # comment is never read as a name. A CEILING on agents/*.md delegate personas; advisory on a skill. On a persona a delegate call's own tools is intersected with this list and can only narrow it. On a skill it is recorded and displayed, but activating a skill does not restrict the parent run's tool set — which matches the ecosystem, where the key is an approval easement rather than a restriction. Use it to state intent; do not rely on it as a control. On a persona, leaving the field out is what means "inherit everything the caller can reach", and an explicitly empty list (allowed-tools: []) means the opposite: the child gets no tools at all.
disable-model-invocationMarks the file manual-invocation-only. On a SKILL.md this gates nothing — it is recorded and displayed in the package overview and the files catalog, but the model can still activate the skill, and the skill is still listed. It is load-bearing only on instruction / rule files and on any file that opts into the prompt with activation.pinned or activation.target: "prompt-context", where a manual file is excluded from injection.
argumentsDeclared activation arguments: a list of { name, description?, required? }.
contextinline (default — the body is returned into the conversation) or fork. Load-bearing: a fork skill also registers as an agent contribution and becomes summonable as a delegate persona.
model, agent, argument-hint, compatibility, license, user-invocableRecognized and carried as metadata for cross-platform compatibility.

Neuralis extensions — all optional and additive:

FieldMeaning
credentialsList of credential-store ids. Each id doubles as the env-var name projected into skill-script child processes. The model never sees resolved values.
hooksReserved for skill-scoped lifecycle hooks. The skill frontmatter parser does not accept the key yet, so declaring it changes nothing today.
requiredFeaturesVisibility gate — see below. This is the only frontmatter key that actually withholds a skill from a caller.

Accepted but inert. contextMode, agentType, preferredModel, maxSteps and manualOnly are parsed without complaint and carried as metadata, but no runtime reads them — writing one changes nothing. In particular contextMode does not fork a skill; context: fork is the key that does, and it is in the table above. Reach for requiredFeatures when you need a real gate, and set step ceilings on the agent rather than in a skill's frontmatter.

Three authoring rules matter:

  • requiredFeatures must be a flat flow array — requiredFeatures: [platform.config]. A nested requires: block with a features: child is not parsed by the discovery frontmatter parser; it warns and ignores the declaration.
  • A skill that declares context: fork registers as an agent contribution (a delegate persona) instead of a skill — see Contributions.
  • An explicit activation: block overrides everything else. Discovery reads activation.target, activation.pinned, and activation.invocation when the block is present, and skips the disable-model-invocation inference entirely. activation.pinned: true or activation.target: "prompt-context" pins the whole skill body into every turn's system prompt — the symptom is a context window that grows by the size of the skill for every conversation, whether or not the skill is ever used. Leave the block off unless you mean it; activation.target has exactly two values, prompt-context and workspace.

Activation flow

A skill activates through the platform's execution tool (execute action="skill"), looked up by id, title, or frontmatter name. The activation does five things in order:

  1. Argument injection. Declared arguments values are substituted into the body.
  2. Credential resolution. Each credentials: id is resolved against the encrypted credential store with the caller's real scope (agent → project → user → platform, most-specific wins). Missing credentials are non-fatal: the body renders a (missing: <id>) sentinel and the id is reported in the structured result.
  3. Session ticket mint. Every activation mints a per-stream session ticket (NEURALIS_SESSION_TOKEN, an opaque nrs1.<requestId>.<secret>) carrying the caller's real identity. Skill scripts use it to authenticate host API calls — there is no long-lived token and no synthetic skill identity, and a synthetic session cannot activate skills at all. See Sessions.
  4. Placeholder substitution. The body is rendered with ${SKILL_DIR} (container path of the skill directory), ${SKILL_URI}, ${SKILL_CONNECTOR}, ${SKILL_NAME}, ${SESSION_ID}, ${NEURALIS_API} (the host API base), and ${SKILL_CREDENTIALS_RESOLVED} / ${SKILL_CREDENTIALS_MISSING}. A placeholder whose coordinate is unavailable becomes a human-readable sentinel so the model picks another path. ${MISSING:<id>} renders per-credential presence.
  5. Credential layer push. Resolved secrets and the env-projectable substitutions are pushed onto the stream's active-skill stack for subsequent shell calls. The layer pops on explicit deactivation (args.deactivate: true) or at stream end; re-activation replaces the layer idempotently.

The activation envelope returned to the model carries a [Skill directory: …] line so imported skills' relative bash scripts/foo.sh forms work — the model passes cwd: "${SKILL_DIR}" on the shell call and the placeholder expands server-side. With several skills active at once, a shell call can pass skill: "<id>" to select which active skill's directory (and ${SKILL_*} substitutions) bind for that call — the default stays the most recently activated skill.

How scripts run

There is no separate script runner: skill scripts run through the platform's sandboxed shell, from the model's perspective simply bash ${SKILL_DIR}/scripts/verify.sh. Each shell call gets:

  • a freshly re-minted session ticket, so multi-turn skill scripts keep a live authenticated identity,
  • of the platform's own secrets, only the ones an active skill declared in credentials: (the projection is conversation-scoped: a command receives the union of every skill still active, not just the most recent one — see credentials) — the shell env sanitizer deny-lists secret-shaped names (the SECRET / TOKEN / PASSWORD / CREDENTIAL / API_KEY name families, and more), known provider prefixes (OPENAI_*, AWS_*, GITHUB_*, …), and an exact list of infrastructure keys, passing through only the declared allowlist. It is a deny-list over the host environment, not a whitelist: a host variable whose name matches no pattern still reaches the script, so secrets belong in the credential store rather than in ambient env vars (see credentials),
  • URI-policy enforcement on the working directory and path arguments, with the active skill directory readable for the duration of the activation (see URI policies).

A package may advertise its script posture with the manifest's runtime.skillScripts key (host | wasm-only | none). It is a declaration, checked at manifest load — an untrusted package that declares anything but none fails validation (omitting the key is also fine) — and it is rendered as a tag in the agent's package overview. It does not itself refuse a spawn: what actually stops a script is the shell's URI-policy gate and the sandbox. See Manifest.

${SKILL_DIR} resolves to a sentinel string only when the skill has no host-side directory at all — a skill contributed by a synced source whose connector exposes no container path. An installed package and a project _packages/ drop both resolve a real directory, so bash ${SKILL_DIR}/scripts/… works for both.

Visibility follows the caller

Skills inherit both their package's enable/disable state and the caller's role/feature grants, deny-by-default. A skill gated by requiredFeatures that the caller does not hold is hidden entirely — not injected, not listed in the package overview, not present in the files catalog — and an activation attempt returns the same not-found result as a skill that never existed. Annotate a skill with the strongest route feature its scripts call; leave it off for public skills, since execution stays independently gated by route features, shell URI policy, and credentials regardless.

On this page