Skip to content

Use skills from other agent harnesses #1277

Description

@santoshkumarradha

Skills written for other agent harnesses should work in codeaf. This issue is the plan for the first version, settled in the thread below: parity with the other harnesses first, nothing self-evolving.

What we are building

  • Read skills from where they already live. A skill is an agentskills.io folder: a directory holding a SKILL.md with name and description in its frontmatter, and optionally scripts/, references/ and assets/ beside it. codeaf finds them in the standard folders and never asks you to move or rewrite one.
  • Every skill is yours to invoke, any time, by its own name. /<skill-name> [what you want] puts the skill's instructions in front of the model for this turn. Every discovered skill is a slash command, and the picker under the draft lists them beside codeaf's own commands.
  • A subset you choose is auto-invoked, and you see it happen. For the skills you turn on, the model sees a one-line catalog (name, description, size) and pulls a skill's body in itself when the work matches. Every such pull draws a row in the transcript naming the skill. Nothing auto-loads because it happened to be on disk.
  • You choose where. A skill is turned on globally, for a project, or for one conversation. Narrower wins: a skill on globally can be turned off in one project, and one off globally can be turned on in one conversation.
  • Adding a skill is one command. /skill add <folder> or /skill add <git url> copies it into codeaf's own skills folder, global or project. /skill is the management command: add, remove, on, off, and use for a skill whose name a built-in command already owns.
  • Skills work anywhere the model works. In the chat and inside a task node alike. Delegating to a task is possible, never required.
  • A Skills tab in /settings lists everything discovered as one catalog, its size, and its on/off per scope. It does not say which folder a skill came from; a skill in codeaf's own folder carries one mark, an imported one carries none.

The format

One SKILL.md per folder. Frontmatter fields: name (1–64 chars, lowercase letters, digits and hyphens), description (1–1024 chars), optional license, compatibility, metadata, allowed-tools. The body is Markdown and there is no format restriction on it. We read leniently the way the spec's client guide recommends: a name that does not match its folder or exceeds 64 chars loads with a warning shown in the Skills tab, a missing description or unparseable frontmatter skips the skill and the tab says why. The one common malformation, an unquoted description: containing a colon, is repaired before the YAML parse.

allowed-tools is read and shown on the skill's row, and pre-approves nothing. A tool call made while following a skill goes through the same approval settings as any other call, so a skill can never widen what you have allowed.

Three tiers of cost, and we pay each only when it is earned:

Tier What When Cost
catalog name + description + size every request, for skills turned on ~50–100 tokens per skill
instructions the SKILL.md body when invoked or auto-invoked the body, once per conversation
resources scripts/, references/, assets/ when the body points at them read on demand with read/bash

Where we read on day one

Project scope (the git root of where codeaf was opened, else the folder itself), in this order:

<project>/.codeaf/skills/     codeaf's own
<project>/.agents/skills/     the cross-client convention
<project>/.claude/skills/
<project>/.codex/skills/
<project>/.cursor/skills/
<project>/.gemini/skills/
<project>/.opencode/skills/
<project>/.goose/skills/

User scope, same order under ~:

~/.codeaf/skills/   ~/.agents/skills/   ~/.claude/skills/   ~/.codex/skills/   ~/.cursor/skills/   ~/.gemini/skills/   ~/.opencode/skills/   ~/.goose/skills/

Every folder is scanned silently, with no import step and no question; a folder that does not exist is skipped without a word. A skill is a direct child directory holding SKILL.md; nothing is walked deeper. The result is ONE catalog. The scan order above is the precedence: the first skill seen under a name is the skill, and a later folder's skill with the same name is ignored. So a project's skill shadows a user's, and .codeaf/skills shadows every other harness's at the same level. ~/.codeaf/skills/ is also where the resident keeps its promoted commands; those folders have no SKILL.md and the scan does not see them. A folder attached with /folder contributes its project-scope skills too, listed under its own path.

How a skill reaches the model

Invoked by you. /<skill-name> [words] sends the skill body plus your words as the turn. The transcript shows the line you typed, not the body. If the skill is already in this conversation's context, only your words are sent and the skill is named as already loaded. The slash table today is a fixed list, so this needs a second, dynamic source of commands that the picker ranks and the dispatcher consults after the fixed table. codeaf's own commands always win a name; a skill named like one of them is reached through /skill use <name> [words] instead, which does exactly what its own slash would have done, and its row in the tab says so. /skills opens the Skills tab.

Auto-invoked. Skills turned on for this scope are listed in the system prompt as a catalog, and one tool, skill, whose name argument is constrained to those names, returns the body wrapped so the model can tell it from conversation:

<skill name="pdf-processing" dir="/home/you/.agents/skills/pdf-processing">
...body...
resources: scripts/extract.py, references/REFERENCE.md
</skill>

Relative paths in the body resolve against dir; the skill's folder is allow-listed for read so bundled files do not each cost a permission prompt.

You always see an auto-invocation. The model pulling a skill in is a tool call like any other, so it draws its own row in the transcript at the moment it happens, naming the skill and what it cost:

  ◆ used skill pdf-processing · 3.1 KB

The row is never folded into the model's prose and never omitted, so a person can always tell a turn that followed a skill from one that did not. Inside a task the same row appears on the task page among the node's steps. When no skill is turned on, the catalog and the tool are absent, not empty (the absent-not-broken law). The catalog changes only when you change a setting or attach a folder, so it rides message[0] with the standing orders and folders and does not re-price the transcript per turn. A loaded skill is not summarised away by compaction.

Inside a task. A task node renders its own system prompt, so it gets the same catalog and the same tool for its scope. propose_task gains an optional skills list to pin named skills into a node's brief, so the chat can hand skilled work to a task with the skill attached. A worker on the bash belt gets the catalog and reads SKILL.md with the shell it has.

Where the choice is kept

Scope File Shape
global ~/.codeaf/skills.json, beside config.json in the profile on: [names], off: [names]
project <project>/.codeaf/skills.json the same two lists, written by the product like autonomy.json, gitignore it or commit it as you like
conversation the conversation's meta.json the same two lists

The global file is its own file rather than rows in config.json: a list of skill names is not a setting the registry can describe, and a config.json key nothing reads is one the unread-key notice would flag.

Effective state for a skill: conversation says on/off, else project, else global, else off. Every write goes through one engine door so the Skills tab and /skill on|off cannot disagree.

Adding a skill

/skill add ~/some/folder                    copies it into ~/.codeaf/skills/<name>/
/skill add ~/some/folder --project          into <project>/.codeaf/skills/<name>/
/skill add https://github.com/o/r           clones, finds every SKILL.md, copies each
/skill add https://github.com/o/r/tree/main/skills/pdf   one folder out of a repo

The URL form clones to a temp directory with the git on PATH and copies out; nothing is executed. A name already present asks before it overwrites. /skill remove <name> removes only from a codeaf folder, never from another harness's.

The Skills tab

One row per discovered skill:

◆ pdf-processing     3.1 KB    on here · on globally
  brand-voice        12 KB     off
  data-analysis      8.4 KB    on for this project

The mark on the first row says it lives in codeaf's own folder; the other two were found in another harness's folder, and the tab does not say which.

Enter cycles the row's state for the current conversation; a second key sets it for the project or globally. A row with a warning shows it dimly, and a skill declaring allowed-tools shows the list. The size is what an invocation costs, in bytes and an estimated token count, so the choice is yours with the number in front of you.

Not in this version

Kept from the design note in the thread for later, once the basics have been used: per-skill checks attached to task acceptance, a cost and outcome ledger per skill, demotion of skills that show no value, cue write-back, trust levels beyond "you turned it on", and forging skills from repeated runs. Editing a skill when it is wrong is the first tool, and the rest should earn its place from real use.

Build order

  1. Discover and parse. internal/skills: the ordered scan into one catalog, first-seen-wins by name, lenient frontmatter, warning records, size, the native mark. Pure, tested against fixtures under testdata/.
  2. Invoke. /<skill-name> [words]: the dynamic command source beside the fixed table, ranked by the picker and consulted by the dispatcher, body sent as the turn's text; the manual page and command-table gates.
  3. Choose and auto-invoke. Scope storage in the three files, the catalog in message[0], the skill tool with its belt-fact sentence, the transcript row every auto-invocation draws, dedup and compaction protection.
  4. Settings tab. The Skills tab, /skill on|off [--project|--global], warnings and shadowing shown.
  5. Add and remove. Folder and git URL, project or global.
  6. Tasks. propose_task.skills, catalog and tool in a node, the bash belt reads SKILL.md.

The six steps land as ONE pull request against dev, in that order of commits, with one change entry. docs/CHAT-V3.md records that v3 deliberately dropped "skills & rules" in favour of routed memory; step 3's entry invalidates that.

Settled since the first draft

  • A project's skills from a freshly cloned repository are invocable by you and never auto-invoked until turned on. The scope model gives this without a trust setting.
  • allowed-tools pre-approves nothing; a skill's tool use is judged by the existing approval settings like any other.
  • OpenCode and Goose folders are read on day one. Every folder is scanned silently into one catalog; there is no import step. First seen wins a name, in the scan order above.
  • A skill is invoked by its own name as a slash command, not through /skill <name>. /skill manages skills: add, remove, on, off, and use as the fallback when a built-in command owns the name.
  • An auto-invocation is always visible: one transcript row per pull, naming the skill and its size.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:toolsThe tool layer: built-in tools, registries, beltsfeatureWork that adds a capability; developers break it into tasksneeds groomingShape and scope are still being decided — the approach is not settledroadmapA public roadmap outcome; the card on the Agent-Field board links here for votes and discussion

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions