Skip to content

Add a skill

Status: Current guide.

Skills are foxctl’s task plugins. A good skill has a small manifest, a narrow runtime boundary, protocol-compliant output, and tests that prove the behavior users rely on.

Add a skill when the operation is:

  • useful as a repeatable command
  • safe to expose through foxctl run or foxctl skills run
  • small enough to keep its own input and output contract clear
  • valuable to agents, hooks, CI, or humans outside one local workflow

Do not add a skill only to hide ordinary Go helpers. Keep domain logic in the right package family, then expose it through a skill if the command surface is worth supporting.

A typical exec skill has:

skills/<skill_dir>/
main.go
main_test.go
skill.yaml

The manifest is the user-facing contract. The implementation should keep IO in the shell and push deterministic logic into ordinary functions that can be tested without running the CLI.

skill.yaml must declare:

FieldPurpose
apiVersion and kindIdentifies the manifest as a foxctl skill
metadata.nameStable command name, for example fs/read
metadata.versionSkill contract version
distribution.typeexec or wasi
signature.commandCommand emitted in envelopes
signature.parametersDirect flags for foxctl skills run
capabilitiesRuntime policy, especially filesystem and network

Use capabilities.network: "none" unless the skill genuinely requires egress. WASI skills must keep network disabled.

Most Go skills use skillmain.Main:

func main() {
skillmain.Main("fs/read", run)
}

The run function receives parsed JSON input and a RunContext. It should:

  1. Validate input and paths at the boundary.
  2. Perform the narrow side effect.
  3. Store large or blob-like output in CAS.
  4. Emit a Protocol v1 envelope through shared helpers.

Skill stdout is reserved for JSON envelopes. Logs belong on stderr. Large outputs should return a compact data.summary plus a CAS artifact pointer instead of inline blobs.

The repo currently has a historical 64KB rule in some agent docs and a stricter Protocol v1 inline rule for data.body. For new skills, prefer the stricter Protocol v1 behavior and explain any exception in review notes.

Build the repo and install skills through the repo’s normal path:

Terminal window
make build
Terminal window
make skills-install

Then verify discovery:

Terminal window
foxctl skills list
Terminal window
foxctl skills info <skill-name>

Use job-tracked execution when history, dedupe, or trajectory capture matters:

Terminal window
foxctl run <skill-name> --input '{"key":"value"}'

Use ephemeral execution for one-off retrieval, hooks, or sandboxed agents:

Terminal window
foxctl run <skill-name> --ephemeral --input '{"key":"value"}'

Use direct flags when validating the manifest’s parameter schema:

Terminal window
foxctl skills run <skill-name> --param value

Add focused tests at the smallest useful layer:

TestWhat it should prove
Pure function testsDeterministic planning, parsing, ranking, or formatting
Skill run testsInput validation, envelope shape, CAS behavior, and errors
Golden testsStable JSON output when the output is a contract
Integration smokeCLI discovery and one representative command

Keep generated timestamps, UUIDs, and ordering deterministic. Inject clocks and IDs where the code would otherwise make output unstable.

Update the user-facing docs when the skill adds a new command or changes a contract:

  • add the skill to the relevant guide or command map
  • link the canonical source docs
  • mark planned or experimental behavior clearly
  • run the docs link checker
Terminal window
make check-doc-links

Before opening a PR, check:

  • The skill keeps stdout envelope-only.
  • WASI skills use network: "none".
  • Path handling uses the shared path validation boundary.
  • Large output goes to CAS.
  • Tests cover the behavior users will depend on.
  • The command is listed in the docs when it is public.