Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
e57fb43
add skills command scaffold
tommaso-moro Mar 30, 2026
5d049cb
register initial skills commands
tommaso-moro Mar 30, 2026
758785b
improve test coverage/cleanup
tommaso-moro Mar 30, 2026
40b2a78
add core logic and improve test coverage
tommaso-moro Mar 30, 2026
8ea84d0
Expand test coverage and fix invariants/bugs
BagToad Apr 3, 2026
ba61ded
use markdown renderer in preview when previewing multi-file skills
tommaso-moro Apr 8, 2026
b26256a
show loading spinner during installation, even for multi-file skills
tommaso-moro Apr 8, 2026
3b50bbb
add .agents/skills as default installation path for hosts that suppor…
tommaso-moro Apr 8, 2026
663df07
cleanup frontmatter fields
tommaso-moro Apr 8, 2026
1f5a6b8
clean up interface and fix a few bugs
tommaso-moro Apr 8, 2026
45d0ec0
address review comments
tommaso-moro Apr 8, 2026
6c7743e
fix: align relevanceScore comments with implementation and fix typo
SamMorrowDrums Apr 15, 2026
a6f6ab3
fix: enforce size cap on first preview file, surface corrupted skills…
SamMorrowDrums Apr 15, 2026
1d5c74a
fix: use target directory remotes in skills publish
SamMorrowDrums Apr 15, 2026
92e40ea
fix: preserve namespace in skills search deduplication
SamMorrowDrums Apr 15, 2026
013d531
chore: remove unused newTestGitClient function
SamMorrowDrums Apr 15, 2026
e04dceb
fix: address review feedback on namespace changes
SamMorrowDrums Apr 15, 2026
f2d978d
Disable auth check for local-only skill flags
SamMorrowDrums Apr 15, 2026
d4c6d61
Merge pull request #13173 from cli/sammorrowdrums/disable-auth-check-…
SamMorrowDrums Apr 15, 2026
9f9b93a
URL-encode parentPath in skills discovery API call
SamMorrowDrums Apr 15, 2026
ce3e081
Merge pull request #13172 from cli/sammorrowdrums/fix-url-encode-pare…
SamMorrowDrums Apr 15, 2026
9552d22
Update acceptance/testdata/skills/skills-search-noresults.txtar
SamMorrowDrums Apr 15, 2026
980428c
Merge pull request #13169 from cli/sammorrowdrums/fix-skills-publish-…
SamMorrowDrums Apr 15, 2026
a6e9722
fix skills names in examples
tommaso-moro Apr 16, 2026
3dce81a
docs(skill): improve help docs
babakks Apr 16, 2026
a1eb707
fix(skill publish): remove misleading `validate` alias
babakks Apr 16, 2026
c5cff0f
Merge pull request #13183 from cli/babakks/improve-skill-docs
SamMorrowDrums Apr 16, 2026
b63f5bf
refactor: use shared discovery logic in publish command
SamMorrowDrums Apr 15, 2026
e559a7c
feat(skills): auto-push unpushed commits before publish
SamMorrowDrums Apr 15, 2026
31265d3
Merge pull request #13171 from cli/sammorrowdrums/skill-publish-unpus…
SamMorrowDrums Apr 16, 2026
cf1afc9
Merge pull request #13167 from cli/sammorrowdrums/publish-use-shared-…
SamMorrowDrums Apr 16, 2026
29e55fe
refactor: remove redundant nil-client fallback in skills publish (#13…
SamMorrowDrums Apr 16, 2026
963a143
Merge pull request #13170 from cli/sammorrowdrums/fix-skills-namespac…
SamMorrowDrums Apr 16, 2026
1aa1984
Merge branch 'trunk' into sm/add-skills-command
SamMorrowDrums Apr 16, 2026
08a0c11
fix: update acceptance test to match current error message
SamMorrowDrums Apr 16, 2026
a69a5fb
Merge pull request #13187 from cli/sammorrowdrums/fix-skills-acceptan…
williammartin Apr 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,4 @@
*~

vendor/
gh
10 changes: 9 additions & 1 deletion acceptance/acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ import (

"math/rand"

"github.com/MakeNowJust/heredoc"
"github.com/cli/cli/v2/internal/ghcmd"
"github.com/cli/go-internal/testscript"
"github.com/MakeNowJust/heredoc"
)

func ghMain() int {
Expand Down Expand Up @@ -434,3 +434,11 @@ func (e *testScriptEnv) fromEnv() error {

return nil
}

func TestSkills(t *testing.T) {
var tsEnv testScriptEnv
if err := tsEnv.fromEnv(); err != nil {
t.Fatal(err)
}
testscript.Run(t, testScriptParamsFor(tsEnv, "skills"))
}
11 changes: 11 additions & 0 deletions acceptance/testdata/skills/skills-install-force.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Install with --force should overwrite an existing skill without error
exec gh skill install github/awesome-copilot git-commit --force --dir $WORK/force-test
stdout 'Installed git-commit'

# Install again with --force — should succeed (overwrite)
exec gh skill install github/awesome-copilot git-commit --force --dir $WORK/force-test
stdout 'Installed git-commit'

# Without --force, non-interactive should fail when skill exists
! exec gh skill install github/awesome-copilot git-commit --dir $WORK/force-test
stderr 'already installed'
15 changes: 15 additions & 0 deletions acceptance/testdata/skills/skills-install-from-local.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Install from a local directory using --from-local
exec gh skill install --from-local $WORK/local-repo git-commit --dir $WORK/output --force
stdout 'Installed git-commit'

# Verify the skill was copied
exists $WORK/output/git-commit/SKILL.md
grep 'local-path' $WORK/output/git-commit/SKILL.md

-- local-repo/skills/git-commit/SKILL.md --
---
name: git-commit
description: Write good git commits
---
# Git Commit
Body content.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Invalid agent ID should error with valid options
! exec gh skill install github/awesome-copilot git-commit --agent bogus-agent --force
stderr 'invalid argument'
stderr 'github-copilot'
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Nonexistent repo should error
! exec gh skill install nonexistent-owner-xyz/nonexistent-repo-abc --force --dir $WORK/tmp
stderr 'Not Found'
60 changes: 60 additions & 0 deletions acceptance/testdata/skills/skills-install-namespaced.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Two namespaced skills with the same base name in the same repo should
# be independently installable using path-based disambiguation.

# Use gh as a credential helper
exec gh auth setup-git

# Create a repo with two namespaced skills that share the name "deploy"
exec gh repo create $ORG/$SCRIPT_NAME-$RANDOM_STRING --public --add-readme
defer gh repo delete --yes $ORG/$SCRIPT_NAME-$RANDOM_STRING

exec gh repo clone $ORG/$SCRIPT_NAME-$RANDOM_STRING
cd $SCRIPT_NAME-$RANDOM_STRING

mkdir -p skills/alice/deploy
mkdir -p skills/bob/deploy
cp $WORK/alice-skill.md skills/alice/deploy/SKILL.md
cp $WORK/bob-skill.md skills/bob/deploy/SKILL.md

exec git add -A
exec git commit -m 'Add namespaced skills'
exec git push origin main

# Publish so the skills are discoverable
exec gh skill publish --tag v1.0.0

# Install alice's deploy skill using the full path to disambiguate
exec gh skill install $ORG/$SCRIPT_NAME-$RANDOM_STRING skills/alice/deploy --scope user --force
stdout 'Installed alice/deploy'

# Install bob's deploy skill using the full path
exec gh skill install $ORG/$SCRIPT_NAME-$RANDOM_STRING skills/bob/deploy --scope user --force
stdout 'Installed bob/deploy'

# Verify both were installed to separate directories
exists $HOME/.copilot/skills/alice/deploy/SKILL.md
exists $HOME/.copilot/skills/bob/deploy/SKILL.md

# Verify each has the correct content
grep 'Alice' $HOME/.copilot/skills/alice/deploy/SKILL.md
grep 'Bob' $HOME/.copilot/skills/bob/deploy/SKILL.md

-- alice-skill.md --
---
name: deploy
description: Alice's deployment skill
---

# Deploy by Alice

Deploys infrastructure using Alice's conventions.

-- bob-skill.md --
---
name: deploy
description: Bob's deployment skill
---

# Deploy by Bob

Deploys infrastructure using Bob's conventions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Install a skill that has nested subdirectories and verify file tree
exec gh skill install github/awesome-copilot git-commit --force --dir $WORK/nested-test
exists $WORK/nested-test/git-commit/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Installing a skill that doesn't exist in a valid repo should error
! exec gh skill install github/awesome-copilot nonexistent-skill-xyz --force --dir $WORK/tmp
stderr 'not found'
7 changes: 7 additions & 0 deletions acceptance/testdata/skills/skills-install-pin.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Install with --pin to a specific ref
exec gh skill install github/awesome-copilot git-commit --scope user --force --pin main
stdout 'Installed git-commit'

# Install without --pin should resolve latest version
exec gh skill install github/awesome-copilot git-commit --scope user --force
stdout 'Installed git-commit'
9 changes: 9 additions & 0 deletions acceptance/testdata/skills/skills-install-scope.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Install with --scope project writes to the git repo's .agents/skills/
exec git init --initial-branch=main $WORK/myrepo
cd $WORK/myrepo
exec gh skill install github/awesome-copilot git-commit --scope project --force --agent github-copilot
exists $WORK/myrepo/.agents/skills/git-commit/SKILL.md

# Install with --scope user writes to home directory
exec gh skill install github/awesome-copilot git-commit --scope user --force --agent github-copilot
exists $HOME/.copilot/skills/git-commit/SKILL.md
20 changes: 20 additions & 0 deletions acceptance/testdata/skills/skills-install.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Install a single skill from a public repo
exec gh skill install github/awesome-copilot git-commit --scope user --force --agent github-copilot
stdout 'Installed git-commit'

# Verify SKILL.md has frontmatter metadata injected
exists $HOME/.copilot/skills/git-commit/SKILL.md
grep 'github-repo' $HOME/.copilot/skills/git-commit/SKILL.md
grep 'github-tree-sha' $HOME/.copilot/skills/git-commit/SKILL.md

# Verify lockfile was written
exists $HOME/.agents/.skill-lock.json
grep 'git-commit' $HOME/.agents/.skill-lock.json

# Install with --dir to a custom directory
exec gh skill install github/awesome-copilot git-commit --force --dir $WORK/custom-skills
stdout 'Installed git-commit'

# Verify the skill was written to the custom directory
exists $WORK/custom-skills/git-commit/SKILL.md
grep 'github-repo' $WORK/custom-skills/git-commit/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Preview with repo only and non-interactive should error
! exec gh skill preview github/awesome-copilot
stderr 'must specify a skill name'
9 changes: 9 additions & 0 deletions acceptance/testdata/skills/skills-preview.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Preview renders skill content and file tree
exec gh skill preview github/awesome-copilot git-commit
stdout 'SKILL.md'
# Verify actual content is rendered, not just the filename
stdout 'git-commit/'

# Preview a skill that doesn't exist should error
! exec gh skill preview github/awesome-copilot nonexistent-skill-xyz
stderr 'not found'
58 changes: 58 additions & 0 deletions acceptance/testdata/skills/skills-publish-dir-remote.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# When a directory argument is provided to `gh skill publish --dry-run`,
# the remote detection must use the target directory's git remotes,
# not the current working directory's remotes.
#
# This test creates two separate git repos:
# - cwd-repo (the working directory) with remote pointing to owner/cwd-repo
# - target-repo (the dir argument) with remote pointing to owner/target-repo
#
# If the bug is present, the command would detect cwd-repo's remote instead of
# target-repo's remote.

# Set up credential helper
exec gh auth setup-git

# Create two test repos on GitHub
exec gh repo create $ORG/$SCRIPT_NAME-cwd-$RANDOM_STRING --private --add-readme
defer gh repo delete --yes $ORG/$SCRIPT_NAME-cwd-$RANDOM_STRING

exec gh repo create $ORG/$SCRIPT_NAME-target-$RANDOM_STRING --private --add-readme
defer gh repo delete --yes $ORG/$SCRIPT_NAME-target-$RANDOM_STRING

# Clone both repos
exec gh repo clone $ORG/$SCRIPT_NAME-cwd-$RANDOM_STRING cwd-repo
exec gh repo clone $ORG/$SCRIPT_NAME-target-$RANDOM_STRING target-repo

# Add a skill to the target repo only
mkdir target-repo/skills/hello-world
cp $WORK/skill.md target-repo/skills/hello-world/SKILL.md
exec git -C $WORK/target-repo add -A
exec git -C $WORK/target-repo commit -m 'Add test skill'
exec git -C $WORK/target-repo push origin main

# Run publish dry-run from cwd-repo, pointing at target-repo
cd cwd-repo
exec gh skill publish --dry-run $WORK/target-repo

# Verify the output references the target repo, not the cwd repo
stdout 'hello-world'

# Publish with a tag from within cwd-repo, targeting target-repo
exec gh skill publish --tag v0.1.0 $WORK/target-repo

# Verify the release was created on the TARGET repo, not the cwd repo
exec gh release view v0.1.0 --repo $ORG/$SCRIPT_NAME-target-$RANDOM_STRING
stdout 'v0.1.0'

# Verify NO release was created on the cwd repo
! exec gh release view v0.1.0 --repo $ORG/$SCRIPT_NAME-cwd-$RANDOM_STRING

-- skill.md --
---
name: hello-world
description: A test skill that greets the user.
---

# Hello World

Greet the user warmly.
33 changes: 33 additions & 0 deletions acceptance/testdata/skills/skills-publish-dry-run.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Publish dry-run from a directory with no skills/ should fail gracefully
Comment thread
williammartin marked this conversation as resolved.
! exec gh skill publish --dry-run $WORK
stderr 'no skills found in'

# Publish dry-run against a valid skill directory should succeed
exec gh skill publish --dry-run $WORK/test-repo
stdout 'hello-world'

# Validate alias should work identically
exec gh skill validate --dry-run $WORK/test-repo
stdout 'hello-world'

# Publish dry-run with --tag
exec gh skill publish --dry-run --tag v1.0.0 $WORK/test-repo
stdout 'hello-world'

# Publish dry-run with --fix
exec gh skill publish --dry-run --fix $WORK/test-repo
stdout 'hello-world'

-- test-repo/skills/hello-world/SKILL.md --
---
name: hello-world
description: A test skill that greets the user.
---

# Hello World

Greet the user warmly.

-- test-repo/skills/hello-world/scripts/setup.sh --
#!/bin/bash
echo "Hello from the hello-world skill!"
64 changes: 64 additions & 0 deletions acceptance/testdata/skills/skills-publish-lifecycle.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Full publish lifecycle: create repo, publish, install from it, clean up

# Use gh as a credential helper
exec gh auth setup-git

# Create a private repo for testing
exec gh repo create $ORG/$SCRIPT_NAME-$RANDOM_STRING --private --add-readme
defer gh repo delete --yes $ORG/$SCRIPT_NAME-$RANDOM_STRING

# Clone the repo
exec gh repo clone $ORG/$SCRIPT_NAME-$RANDOM_STRING
cd $SCRIPT_NAME-$RANDOM_STRING

# Add a test skill
mkdir skills/hello-world/scripts
cp $WORK/skill.md skills/hello-world/SKILL.md
cp $WORK/setup.sh skills/hello-world/scripts/setup.sh
exec git add -A
exec git commit -m 'Add test skill'
exec git push origin main

# Publish with a tag
exec gh skill publish --tag v0.1.0

# Verify the release was created on GitHub
exec gh release view v0.1.0
stdout 'v0.1.0'

# Install from our test repo
exec gh skill install $ORG/$SCRIPT_NAME-$RANDOM_STRING hello-world --scope user --force
stdout 'Installed hello-world'

# Verify installed files exist with correct metadata
exists $HOME/.copilot/skills/hello-world/SKILL.md
exists $HOME/.copilot/skills/hello-world/scripts/setup.sh
grep 'github-repo' $HOME/.copilot/skills/hello-world/SKILL.md

# Install with --pin
exec gh skill install $ORG/$SCRIPT_NAME-$RANDOM_STRING hello-world --scope user --force --pin v0.1.0
stdout 'Installed hello-world'

# Preview from our test repo
exec gh skill preview $ORG/$SCRIPT_NAME-$RANDOM_STRING hello-world
stdout 'Hello World'

# Update dry-run should find installed skill
exec gh skill update --dry-run --all
stderr 'up to date'

-- skill.md --
---
name: hello-world
description: A test skill that greets the user.
---

# Hello World

Greet the user warmly and offer to run the setup script.

-- setup.sh --
#!/bin/bash
echo "Hello from the hello-world skill!"
echo "Setting up environment..."
echo "Done."
4 changes: 4 additions & 0 deletions acceptance/testdata/skills/skills-search-noresults.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Search for something unlikely to exist returns empty stdout
# NoResultsError is silent in non-TTY (exits 0 with no output)
exec gh skill search zzzznonexistenttotallyfakeskillxyz123
! stdout .
3 changes: 3 additions & 0 deletions acceptance/testdata/skills/skills-search-page.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Pagination returns results on page 2
exec gh skill search copilot --page 2
stdout 'copilot'
12 changes: 12 additions & 0 deletions acceptance/testdata/skills/skills-search.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Search for skills matching a query
exec gh skill search copilot
stdout 'copilot'

# Search with JSON output
exec gh skill search copilot --json skillName,repo --limit 1
stdout '"skillName"'
stdout '"repo"'

# Search with a short query should error
! exec gh skill search a
stderr 'at least'
5 changes: 5 additions & 0 deletions acceptance/testdata/skills/skills-update-noinstalled.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Update with no installed skills should report appropriately
exec gh skill update --dry-run --all --dir $WORK/empty-dir
stderr 'No installed skills found'

-- empty-dir/.gitkeep --
22 changes: 22 additions & 0 deletions acceptance/testdata/skills/skills-update.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Dry-run update should find the installed skill and report status
exec gh skill update --dry-run --all --dir $WORK/skills-dir
stdout 'git-commit'

# Force update should re-download and rewrite files
exec gh skill update --force --all --dir $WORK/skills-dir
stdout 'Updated'

# Verify the SKILL.md was rewritten with real content (not our placeholder)
grep 'github-repo' $WORK/skills-dir/git-commit/SKILL.md
! grep 'Test skill content' $WORK/skills-dir/git-commit/SKILL.md

-- skills-dir/git-commit/SKILL.md --
---
name: git-commit
description: Git commit helper
metadata:
github-repo: https://github.com/github/awesome-copilot.git
github-tree-sha: 0000000000000000000000000000000000000000
github-path: skills/git-commit
---
Test skill content
Loading
Loading