upskill is a standalone Go CLI and stdio MCP server for sharing agent skills
with a team. It can install skills from local or Git sources, keep installed
skills in sync with upstream sources, build generated output from local
customizations, and expose skills to agents through MCP or folders on disk
without allowing agents to overwrite base skills.
What makes Upskill effective for teams is that you can customize the behavior of any installed skill without changing the skill. Agents see a rendered version of the skill with your changes applied. To make team skills easier to customize, you can optionally author skill files as Jinja templates, and users of the skill can change them with Jinja syntax.
make
mkdir -p ~/.local/bin
install -m 0755 bin/upskill ~/.local/bin/upskill
upskill doctor
upskill add ./examples --skill hello-skill --agent codex
upskill show hello-skill --agent codexThe binary is written to bin/upskill. Installing it to ~/.local/bin is
optional; you can also run bin/upskill directly.
Start with docs/getting-started.md for installation, first-run config, the local example skill, and MCP setup. Use docs/workflows.md for task-oriented examples covering local customizations through MCP, drafts, promotion to PRs, updates, and Jinja template customizations.
For repository validation:
make test
make coverageupskill add <source> [--agent AGENT] [--skill NAME] [--ref REF] [--subpath PATH] [--update-policy manual|auto] [--no-render]
upskill show <source-or-skill> [--agent AGENT] [--skill NAME] [--ref REF] [--subpath PATH] [--stats] [--nocontent]
upskill list|ls [--json] [--agent AGENT]
upskill find|search [query] [--json] [--limit N]
upskill remove|rm <skill-name>... [--delete-overlays]
upskill update [skill-name...] [--agent AGENT] [--no-render] [--check] [--diff]
upskill sync [--agent AGENT]
upskill migrate [--from unmanaged|SOURCE] [--agent AGENT] [--skill NAME] [--to-source NAME] [--to-source-path DIR] [--yes]
upskill init [name] [--dir DIR]
upskill workon <skill-name> [--from SKILL] [--template NAME] [--no-edit]
upskill status
upskill doctor
upskill mcp
upskill stats use <skill> [--agent AGENT] [--idempotency-key KEY] [--json]
upskill stats feedback <skill> --sentiment positive|negative --situation TEXT --details TEXT --proposed-change TEXT [--agent AGENT] [--idempotency-key KEY] [--json]Source shortcuts:
upskill source list|ls [--json]
upskill source add <source> [--name NAME] [--ref REF] [--subpath PATH] [--shared|--personal|--external] [--write-policy POLICY] [--default-update-policy manual|auto]
upskill source show <source-or-name> [--json]
upskill source edit <source-name> <skill-name> --file PATH (--content TEXT | --base64-content DATA) [--yes] [--reason TEXT]
upskill source skills <source-or-name> [--json] [--ref REF] [--subpath PATH]
upskill source find|search <source-or-name> [query] [--json] [--limit N] [--ref REF] [--subpath PATH]
upskill source remove|rm <source-or-name> [--yes] [--keep-installed]Global option:
upskill <command> --no-check--no-check skips implicit update notices and automatic updates for one
command. Set UPSKILL_NO_CHECK=1 for the same behavior in the environment.
Explicit update, sync, and source catalog commands still run their requested
live operation.
Local customizations:
upskill overlay list|ls [--skill NAME] [--agent AGENT] [--json]
upskill overlay add <skill-name> [--agent AGENT] [--path] [--no-edit]
upskill overlay edit <skill-name> [--agent AGENT] [--file PATH] [--operation OPERATION] [--no-edit]
upskill overlay diff <skill-name> [--agent AGENT]Generated output:
upskill render [skill-name] [--agent AGENT] [--copy=false]
upskill render <skill-name> --print [--file SKILL.md] [--base-only]Drafts:
upskill draft list|ls [--json]
upskill draft create <skill-name> [--from SKILL] [--from-rendered] [--template NAME] [--no-edit]
upskill draft edit <skill-name> [--file PATH] [--no-edit]
upskill draft path <skill-name>
upskill draft files <skill-name> [--json]
upskill draft read <skill-name> [--file PATH]
upskill draft add-file <skill-name> <local-path> --path PATH [--no-edit]
upskill draft import <skill-name> --archive PATH [--sha256 SHA256] [--strip-components N] [--inspect] [--no-edit]
upskill draft render <skill-name> [--agent AGENT] [--copy=true|false] [--apply-overlays]
upskill draft diff <skill-name>
upskill draft rebase <skill-name>
upskill draft promote <skill-name> (--to PATH | --to-source NAME) [--dry-run]
upskill draft remove|rm <skill-name>Interactive draft commands use $VISUAL or $EDITOR: workon, draft create, draft edit, draft add-file, and non-inspection draft import
create the needed file and open it when running in a terminal. Use --no-edit
for automation.
Supported source inputs include local paths, Git URLs, GitHub shorthand, refs, and subpaths:
./skills
owner/repo
owner/repo/path/to/skills
owner/repo@ref
owner/repo#ref
https://github.com/owner/repo/tree/ref/path/to/skills
git@github.com:owner/repo.git
upskill add discovers directories containing SKILL.md, copies full skill
directories into the base store, writes upskill-lock.json, and renders by
default. upskill source skills and upskill source find inspect the current
source every time. Local sources are read directly from disk; Git sources are
resolved from the requested upstream ref instead of from the installed store.
Installed skills are separate from source catalogs. upskill update --check
previews locked source changes without writing. upskill update --diff prints
contextual unified diffs with relative paths for incoming file changes.
upskill update applies updates, and
upskill sync updates all locked skills and renders final agent outputs.
Upskill does not install newly discovered upstream skills automatically; run
upskill add <source> --skill <name> once for each new skill you want managed.
When a SKILL.md has no frontmatter block, Upskill infers default name and
description values from the directory name and first prose paragraph for
search/list output and rendered agent copies. The original source file is left
unchanged.
The primary local example source is ./examples:
upskill add ./examples --skill hello-skill --agent codexA successful mutation groups its primary result and labels the managed render path separately from the configured agent-output copy:
Result
OK installed hello-skill
digest: sha256:<full-digest>
OK rendered hello-skill for codex
rendered: /Users/example/.skills/rendered/codex/hello-skill
copied: /Users/example/.codex/skills/hello-skill
Upskill still executes preflight before the requested command. It defers only
non-fatal preflight messages so a successful result appears first in a combined
terminal view. Primary results remain on stdout. Passive Preflight and
Agent output audit sections remain on stderr. Payload commands such as
show, render --print, and JSON modes do not gain a Result heading.
By default, upskill uses:
~/.skills/
store/ # synced base skills; do not edit directly
overlays/ # global and agent-specific overlay definitions
drafts/ # mutable draft skill bundles
imports/ # shared MCP-safe import inbox
rendered/ # generated output, safe to delete
cache/ # advisory update-check cache, safe to delete
config.json # sources and output directories
upskill-lock.json # installed skill source metadata
upskill-stats.jsonl # local retrieval, use, and feedback activity
Environment overrides:
UPSKILL_HOME
UPSKILL_STORE_DIR
UPSKILL_OVERLAYS_DIR
UPSKILL_DRAFTS_DIR
UPSKILL_IMPORTS_DIR
UPSKILL_RENDERED_DIR
UPSKILL_CONFIG
UPSKILL_LOCK
UPSKILL_STATS
UPSKILL_SOURCE_IGNORE
UPSKILL_NO_CHECK
UPSKILL_PREFLIGHT_CHECK_TTL
UPSKILL_PREFLIGHT_CHECK_TTL accepts Go duration syntax, defaults to 1h, and
controls how long implicit automatic Git checks reuse a successful result. Set
it to 0 to probe automatic Git remotes on every preflight.
Upskill records one retrieval after a command or MCP tool successfully returns installed skill content. A retrieval is not a use: record a use only after an agent materially applies the skill to a task.
upskill show hello-skill --stats
upskill show hello-skill --stats --nocontent
upskill stats use hello-skill --agent codex --idempotency-key task-123:hello:useThe first show command returns content, records its retrieval, and prints the
updated counts. The --nocontent form prints only statistics and does not
record a retrieval.
Agents can leave structured feedback from real use:
upskill stats feedback hello-skill \
--sentiment negative \
--situation "Reviewing unresolved comments on a Go pull request" \
--details "The skill did not direct the agent to inspect inline review threads" \
--proposed-change "Require inline review-thread inspection before completion"
upskill stats feedback hello-skill \
--sentiment positive \
--situation "Promoting a local skill revision into a team source" \
--details "The dry-run made the write target clear and prevented an unintended change" \
--proposed-change "Keep the dry-run requirement and show the same target summary in CLI examples"Feedback stays in the local upskill-stats.jsonl activity log. It never edits
a skill, source, overlay, draft, rendered output, or VCS state. Do not include
secrets, credentials, private customer data, or unnecessary personal data.
Use upskill doctor after installation or when changing UPSKILL_HOME. It
creates the default local directories when they do not exist:
store/
overlays/
drafts/
imports/
rendered/
Most commands create the managed files and directories they need automatically.
Use upskill status to inspect the current home, store path, installed skill
count, overlay count, and draft count. doctor and status show a detailed
Agent output audit when configured output directories contain unmanaged
skills, managed skills without render markers, or directories that cannot be
inspected. The audit is read-only: add, status, and doctor leave those
skills unchanged. Run upskill migrate to preview copying unmanaged skills
into a local source; run upskill migrate --yes to apply the migration,
install the copied skills, and render managed output. By default, migrate
reads from unmanaged agent output directories and writes to a local-skills
source at ~/Documents/Upskill/local-skills. The command is a dry run unless
--yes is passed. Duplicate skill names are preserved with numeric suffixes
such as redis, redis-2, and redis-3. migrate can also copy from one
configured local source to another with --from SOURCE --to-source NAME.
A source is where skills come from. It can be a GitHub repo, a Git URL, a GitHub
subdirectory, or a local directory such as an Obsidian skills folder. upskill
does not require local skills to be moved into a special layout.
The important distinction is:
- A source is the upstream skill collection.
store/is the installed copy thatupskillrenders from and agents read.overlays/,drafts/, andimports/are managedupskillworking state.
Use upskill list to see which source owns each installed skill. If a source
record was removed while an old installed copy remains, list marks that source
as unconfigured.
Add an existing local directory as a named source:
upskill source add ~/Documents/Notes/Memory/Skills --name Obsidian --personal
upskill source skills Obsidian
upskill source find Obsidian redis
upskill add Obsidian --skill redisSource records can include policy metadata that tells agents which write
workflow to prefer. Use --personal for local sources where direct source edits
are acceptable, --shared for team sources where draft promotion is the normal
upstream path, and --external for read-only sources. --write-policy can be
direct_allowed, overlay_preferred, draft_promote, or read_only.
upskill source list --json and upskill source show include the effective
policy, including metadata read from an optional checked-in upskill.toml.
Removing a source also removes skills installed from that source, plus managed
rendered output and marked agent-output copies. When installed skills would be
removed, upskill prints the affected skill list and requires --yes. Source
files, overlays, and drafts are left alone. Use
upskill source remove <source> --keep-installed only when you want to detach
the source record but keep the installed copies.
You can also install directly from a raw path or Git source. Raw sources are recorded automatically and are displayed by their canonical source string:
upskill add ~/Documents/Notes/Memory/Skills --skill redis
upskill add owner/repo --subpath skills --skill redisupskill find and upskill search search installed skills in the local store.
upskill source find and upskill source search search one live source by
skill ID, display name, description, SKILL.md body, and path without
installing anything.
Upskill treats source catalogs and installed copies as different things:
- Source inspection commands always read the current source catalog and can show newly added upstream skills.
- Installed skill checks compare the locked copy in
store/with the current source version of that same installed skill. - New upstream skills do not produce generic notifications and are not installed
until you run
upskill add.
Most commands run a source-aware preflight check. Local skills are checked live. Manual Git skills never contact their remote during ordinary commands; run an explicit check to discover Git changes. A successful explicit check can seed a cached notice for later ordinary commands. Automatic Git skills reuse a successful check for one hour by default, then probe the remote revision and clone only when that revision changed.
If a known installed skill change has policy manual, Upskill prints a notice
to stderr. JSON stdout stays machine-readable.
Preflight
NOTICE redis: update available
next: upskill update --diff redis
Use manual updates when you want to review changes:
upskill update --check
upskill update redis --diff
upskill syncupskill update --check, upskill update --diff, upskill sync --dry-run,
and source catalog commands are always live. They are not suppressed by
--no-check.
Use automatic updates when you want a skill refreshed whenever Upskill runs a normal command:
upskill add Obsidian --skill redis --update-policy auto
upskill source add ~/Documents/Notes/Memory/Skills --name Obsidian --default-update-policy autoTo change an installed skill's policy later, reinstall it from the same source:
upskill add Obsidian --skill redis --update-policy manualAutomatic updates refresh installed base skill files, update the lock entry,
and render/copy configured agent outputs before the command's main behavior.
upskill update --check, upskill update --diff, and upskill sync --dry-run
inspect installed sources live without applying updates. upskill source skills and upskill source find inspect their requested source catalog live.
These explicit commands skip implicit automatic-update preflight. Use
--no-check or UPSKILL_NO_CHECK=1 to skip implicit preflight for other
commands. Configure the automatic Git freshness window in config.json or the
environment:
{
"preflight_check_ttl": "1h"
}UPSKILL_PREFLIGHT_CHECK_TTL=30m upskill listIf an implicit automatic Git refresh fails, Upskill warns, reuses a stale successful status when available, and continues the primary command. A failure while actually applying an automatic update remains fatal.
Agent output directories can be configured with agents:
{
"agents": {
"claude-code": "~/.claude/skills",
"codex": "~/.codex/skills"
}
}If no outputs are configured, upskill only auto-detects well-known
directories that already exist.
An overlay is a local customization layer for an installed skill. Use one when
you want to change how a skill behaves for yourself, a team, or a specific agent
without editing the synced copy in store/. The base skill can still update
from its source; your overlay is reapplied the next time generated output is
built.
An overlay can append to SKILL.md, add a support file, replace a file, apply a
JSON Patch to a JSON file, or provide a .j2 template file. upskill applies
those changes when it builds the effective skill output.
For CLI users, overlays are editor-first. upskill overlay add creates the
default SKILL.md append file and opens $VISUAL or $EDITOR immediately.
upskill overlay edit revisits an existing overlay file or creates the
requested operation file before opening the editor. --path and --no-edit are
available for scripts. MCP-connected agents can create overlays with
upsert_skill_overlay_file.
Example: add Codex-only guidance to hello-skill:
upskill add ./examples --skill hello-skill --agent codex
upskill overlay add hello-skill --agent codex
upskill render hello-skill --agent codex
upskill show hello-skill --agent codexThe editor opens the Codex-specific append overlay for SKILL.md. Save the
guidance there, then render or sync to build generated output.
During render, upskill builds the effective skill in this order:
- Base skill from
store/<skill-name>/ - Global overlay from
overlays/<skill-name>/ - Agent overlay from
overlays/<skill-name>/agents/<agent>/
The overlay directory layout controls the operation:
overlays/<skill>/SKILL.append.md # append to SKILL.md for every agent
overlays/<skill>/agents/codex/SKILL.append.md # append only for Codex
overlays/<skill>/files/<path> # add a new file
overlays/<skill>/replace/<path> # replace or create a file
overlays/<skill>/patches/*.json # JSON Patch for a JSON file
overlays/<skill>/templates/<path>.j2 # Jinja template file
files/<path> adds a new file and fails if the file already exists.
replace/<path> overwrites or creates a file.
append/<path> appends text with a deterministic separator.
Root-level append shorthand is supported:
SKILL.append.md -> SKILL.md
AGENTS.append.md -> AGENTS.md
patches/*.json applies JSON Patch operations to .json files only:
{
"target": "metadata.json",
"operations": [
{"op": "add", "path": "/tags/-", "value": "codex"}
]
}Most commands build generated output automatically. add, update, and sync
render by default unless a command has a --no-render flag and you use it.
Use upskill render when you need to rebuild or inspect generated output
without updating base skills. This is useful after editing overlay files
directly, changing agent output config, recreating rendered/, or checking the
base skill with --base-only. Output is skipped when the base skill, overlays,
mode, and agent are unchanged.
Human render results always label the two path roles:
Result
OK rendered redis for codex
rendered: /Users/example/.skills/rendered/codex/redis
copied: /Users/example/.codex/skills/redis
render --copy=false reports not copied. render --print keeps stdout as
the requested file content without headings.
Only .j2 files are rendered through Jinja-style templates using
github.com/nikolalohinski/gonja/v2. Non-.j2 files are copied literally.
Base template:
---
name: Template Skill
description: Example skill with a Jinja-rendered entrypoint
---
# Template Skill
{% block guidance %}
Base guidance.
{% endblock %}Overlay template:
{% extends "parent/SKILL.md.j2" %}
{% block guidance %}
{{ super() }}
Codex-specific guidance.
{% endblock %}Template output strips .j2:
templates/SKILL.md.j2 -> SKILL.md
Run:
upskill mcpThe server publishes generated instructions from its live tool registry during
MCP initialization. Use tools/list for the complete registry and call
describe_tool with a tool name for its authoritative inputSchema and
accepted parameter names. Tool errors include the same schema and accepted
parameter list, so client-visible guidance cannot drift from the registered
surface.
The activity tools are get_skill_stats, record_skill_use, and
record_skill_feedback. Successful installed calls to read_skill and
read_skill_file record retrievals; discovery, file manifests, statistics,
source reads, and draft reads do not. record_skill_use is only for material
use, and structured feedback never increments use or changes a skill.
Use the same discover, describe, preview, apply, and read-back lifecycle for mutations:
- Discover
promote_skill_draftwithtools/list. - Call
describe_toolwithname: "promote_skill_draft"and use only names fromaccepted_parameter_names. - Call
promote_skill_draftwithdry_run: trueand inspectactions,warnings, andcanonical_resources. - Repeat the same arguments with
dry_run: falseonly after the preview is approved. - Read the destination or source again to confirm the applied state.
For example, the tool-call arguments for steps 2 through 4 are:
{"name":"describe_tool","arguments":{"name":"promote_skill_draft"}}{"name":"promote_skill_draft","arguments":{"draft_id":"draft:redis","to_source":"personal","dry_run":true}}{"name":"promote_skill_draft","arguments":{"draft_id":"draft:redis","to_source":"personal","dry_run":false}}These argument examples are illustrative because they require a matching draft and configured local source. The stdio integration suite runs the equivalent lifecycle in a disposable environment.
Mutating tools that expose dry_run follow the same preview convention: call
with dry_run: true, inspect the planned targets and actions, then repeat with
dry_run: false to apply. Existing apply-by-default tools retain that default
for compatibility, while draft promotion and direct source edits remain
preview-by-default.
MCP writes are limited to overlays, mutable drafts, dry-run-first draft
promotion, and policy-checked source-file edits in configured local sources.
Agents can inspect configured sources, search live source skills, check
installed updates, and read base or generated skills, but they cannot install
new source skills, change update policy, perform VCS operations, or overwrite
store/ through the MCP server. To change an installed skill's update policy,
reinstall it with upskill add plus --skill <name> --update-policy manual|auto. Draft promotion and source-file edits reject managed upskill
directories such as store/, rendered/, overlays/, drafts/, and
imports/.
Copy prompts/AGENTS.md into an agent configuration when you want an agent to
use upskill MCP tools for skill management instead of built-in skill servers.
The prompt's Using Skills section tells agents that agent-local SKILL.md
files are generated output, not the source to edit when a skill needs to change.
Keep prompts/AGENTS.md as the always-on bootstrap and safety contract. An
agent needs those instructions before it can discover Upskill's registry or
load an Upskill usage skill, so an installable skill should complement the
prompt, not replace it. The prompt should retain tool discovery, live-schema,
write-target, and generated-output rules; a skill can carry longer task recipes
and examples on demand.
The repository does not yet ship that self-usage skill. Before adding one, generate both surfaces from one maintained source or contract-test their shared requirements so tool names, preview behavior, and source semantics cannot drift independently.
upskill sync updates locked base skills, builds generated output, and copies
the effective final skill directories to configured outputs. If overlays exist,
they are always applied. There is no --with-overlays or --without-overlays
mode.
Text sync output uses OK for applied results and PLAN for --dry-run
targets. sync --json and MCP sync_skills preserve their legacy warnings
and add agent_output_audit, including counts, per-agent summaries, and the
detailed findings used to build those warnings. MCP preflight notices include
phase: "preflight"; audit findings stay in the sync result rather than the
notice list.
To inspect the base state:
upskill render my-skill --print --base-onlyEvery rendered skill contains .upskill-render.json, which records render mode,
agent, base path, overlay paths, overlay hash, and timestamp.
- Installed-skill search is local to the store. Source search inspects one requested source live. Broad remote registry search is not implemented.
- Draft promotion writes to a local checkout or named local source. It does not create branches, commit, push, or open pull requests.
syncupdates locked skills and renders outputs. It does not discover a new skill that has never been installed; runupskill add <source> --skill <name>once for new skills.- Template customization files are full
.j2files. They are not JSON or YAML variable maps.