Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Install skills flat by Name, not namespaced InstallName#13266

Merged
SamMorrowDrums merged 1 commit into
trunkfrom
sammorrowdrums/fix-skill-install-flat-path
Apr 24, 2026
Merged

Install skills flat by Name, not namespaced InstallName#13266
SamMorrowDrums merged 1 commit into
trunkfrom
sammorrowdrums/fix-skill-install-flat-path

Conversation

@SamMorrowDrums
Copy link
Copy Markdown
Contributor

Problem

Most agent clients (Claude Code, Copilot, Cursor, etc.) only discover immediate subdirectories of their skills folder. When a skill repository uses namespaced paths like skills/author/my-skill/, the installer was creating nested directories (e.g. .claude/skills/author/my-skill/) that clients could not find.

Reported by users: skills from repos with structure like skills/<namespace>/a-skill/ install to <agent>/skills/<namespace>/a-skill/ instead of <agent>/skills/a-skill/, and the client doesn't recognize them.

Solution

Separate the skill's identity from its filesystem path:

  • InstallName() (unchanged) - returns namespace/name for namespaced skills. Used for lockfile keys, search, filtering, display, and update matching.
  • Name - the plain skill name, now used for the install directory. Skills are always installed flat:
.claude/skills/my-skill/SKILL.md       # correct (flat)
.claude/skills/author/my-skill/SKILL.md # was wrong (nested)

Changes

File What changed
installer.go Use skill.Name for directory paths instead of InstallName()
install.go Use skill.Name for overwrite checks and prompts
collisions.go Detect conflicts by Name since flat install means two skills with the same Name but different Namespace collide
update.go Clean up old namespaced directories when updating migrates to flat layout
Tests Updated to reflect flat install behavior and collision detection

Validation

  • The agentskills.io specification defines skills as flat folders (skill-name/SKILL.md)
  • Agent clients scan only first-level subdirectories of their skills directory
  • All existing tests pass; lint clean

Most agent clients (Claude Code, Copilot, etc.) only discover immediate
subdirectories of their skills folder. When a skill repository used
namespaced paths like skills/author/my-skill/, the installer created
nested directories (e.g. .claude/skills/author/my-skill/) that clients
could not find.

This separates the skill's identity (InstallName, used for lockfile keys,
search, filtering, display) from the filesystem path (Name, used for the
install directory). Skills are now always installed flat:

  .claude/skills/my-skill/SKILL.md  (not .claude/skills/author/my-skill/)

Changes:
- installer: use skill.Name for directory paths instead of InstallName
- install.go: use skill.Name for overwrite checks and prompts
- collisions: detect conflicts by Name since flat install means two
  skills with the same Name but different Namespace values will collide
- update: clean up old namespaced directories when migrating to flat

Co-authored-by: Copilot <[email protected]>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates skill installation to use a flat directory layout by skill Name (instead of namespace/name), improving compatibility with agent clients that only scan immediate subdirectories.

Changes:

  • Install skills into <skillsDir>/<skill.Name>/ rather than <skillsDir>/<namespace>/<skill.Name>/.
  • Detect install collisions by Skill.Name (flat-layout collisions across namespaces).
  • During update, remove the legacy namespaced install directory after migrating to the flat layout; update tests accordingly.
Show a summary per file
File Description
pkg/cmd/skills/update/update.go Removes legacy namespaced install directories after an update installs into the new flat location.
pkg/cmd/skills/update/update_test.go Verifies updated skills install flat and that the old namespaced directory is removed.
pkg/cmd/skills/install/install.go Updates overwrite checks/prompts to look for existing installs in flat directories.
pkg/cmd/skills/install/install_test.go Adjusts install tests to expect collisions for same-name skills across namespaces under flat installs.
internal/skills/installer/installer.go Changes actual install directory creation to use skill.Name (flat) for local and remote installs.
internal/skills/discovery/collisions.go Changes collision detection to group by Skill.Name to reflect flat install semantics.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 6/6 changed files
  • Comments generated: 1

Comment on lines +428 to +432
_ = os.RemoveAll(u.local.dir)
// Remove the parent if it is now empty (leftover namespace directory).
parent := filepath.Dir(u.local.dir)
if entries, readErr := os.ReadDir(parent); readErr == nil && len(entries) == 0 {
_ = os.Remove(parent)
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cleanup logic ignores errors from removing the old install directory and its parent (os.RemoveAll / os.Remove). If removal fails (permissions, locked files), the stale namespaced copy will remain and may cause confusing duplicate installs with no user feedback. Consider handling these errors (e.g., warn to stderr and/or fail the update for that skill) so users know the migration cleanup didn't complete.

Suggested change
_ = os.RemoveAll(u.local.dir)
// Remove the parent if it is now empty (leftover namespace directory).
parent := filepath.Dir(u.local.dir)
if entries, readErr := os.ReadDir(parent); readErr == nil && len(entries) == 0 {
_ = os.Remove(parent)
if removeErr := os.RemoveAll(u.local.dir); removeErr != nil {
fmt.Fprintf(opts.IO.ErrOut, "%s Warning: updated %s but failed to remove old install directory %q: %v\n", cs.WarningIcon(), u.local.name, u.local.dir, removeErr)
} else {
// Remove the parent if it is now empty (leftover namespace directory).
parent := filepath.Dir(u.local.dir)
if entries, readErr := os.ReadDir(parent); readErr == nil && len(entries) == 0 {
if removeParentErr := os.Remove(parent); removeParentErr != nil {
fmt.Fprintf(opts.IO.ErrOut, "%s Warning: updated %s but failed to remove empty parent directory %q: %v\n", cs.WarningIcon(), u.local.name, parent, removeParentErr)
}
}

Copilot uses AI. Check for mistakes.
@SamMorrowDrums SamMorrowDrums added gh-skill relating to the gh skill command needs-triage needs to be reviewed unmet-requirements and removed needs-triage needs to be reviewed unmet-requirements labels Apr 23, 2026
@SamMorrowDrums SamMorrowDrums merged commit 96b9af3 into trunk Apr 24, 2026
43 checks passed
@SamMorrowDrums SamMorrowDrums deleted the sammorrowdrums/fix-skill-install-flat-path branch April 24, 2026 09:41
tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request May 8, 2026
This MR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [cli/cli](https://github.com/cli/cli) | minor | `v2.90.0` → `v2.92.0` |

MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot).

**Proposed changes to behavior should be submitted there as MRs.**

---

### Release Notes

<details>
<summary>cli/cli (cli/cli)</summary>

### [`v2.92.0`](https://github.com/cli/cli/releases/tag/v2.92.0): GitHub CLI 2.92.0

[Compare Source](cli/cli@v2.91.0...v2.92.0)

#### Support GitHub Enterprise Cloud (GHEC) in `skill` commandset

Now `gh skill` subcommands (`install`, `preview`, `publish`, `search`, `update`) are able to work with [GHEC](https://docs.github.com/en/enterprise-cloud@latest/admin/overview/about-github-enterprise-cloud) hosts with data residency.

#### Add `--allow-hidden-dirs` flag to `skill preview`

Following the addition of `--allow-hidden-dirs` to `skill install` in the previous release, now the flag is also supported in `skill preview`, allowing users to preview skills located in hidden (dot-prefixed) directories such as `.claude/skills/`, `.agents/skills/`, and `.github/skills/`.

#### What's Changed

##### ✨ Features

- feat(skills): add --allow-hidden-dirs flag to preview command by [@&#8203;SamMorrowDrums](https://github.com/SamMorrowDrums) in [#&#8203;13265](cli/cli#13265)
- feat(skills): support GHEC with data residency hosts by [@&#8203;SamMorrowDrums](https://github.com/SamMorrowDrums) in [#&#8203;13264](cli/cli#13264)

##### 🐛 Fixes

- Fix SetSampleRate not updating sample\_rate dimension by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;13259](cli/cli#13259)
- Fix log terminal injection by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;13272](cli/cli#13272)
- Add "Resource not accessible" to ProjectsV2IgnorableError by [@&#8203;maxbeizer](https://github.com/maxbeizer) in [#&#8203;13281](cli/cli#13281)

##### 📚 Docs & Chores

- fix: using variable interpolation \`${{ in deployment.yml... by [@&#8203;orbisai0security](https://github.com/orbisai0security) in [#&#8203;13258](cli/cli#13258)
- docs: correct typo in Linux Homebrew copy by [@&#8203;cassidyjames](https://github.com/cassidyjames) in [#&#8203;13273](cli/cli#13273)
- Install skills flat by Name, not namespaced InstallName by [@&#8203;SamMorrowDrums](https://github.com/SamMorrowDrums) in [#&#8203;13266](cli/cli#13266)
- chore: fix zsh completion on debian by [@&#8203;babakks](https://github.com/babakks) in [#&#8203;13274](cli/cli#13274)
- Add trust disclaimer to extension help text by [@&#8203;travellertales](https://github.com/travellertales) in [#&#8203;13296](cli/cli#13296)
- Bump Go to 1.26.2 by [@&#8203;github-actions](https://github.com/github-actions)\[bot] in [#&#8203;13301](cli/cli#13301)

##### :dependabot: Dependencies

- chore(deps): bump github.com/mattn/go-isatty from 0.0.20 to 0.0.21 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;13161](cli/cli#13161)
- chore(deps): bump github.com/google/go-containerregistry from 0.21.4 to 0.21.5 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;13162](cli/cli#13162)
- chore(deps): bump charm.land/lipgloss/v2 from 2.0.2 to 2.0.3 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;13163](cli/cli#13163)
- chore(deps): bump charm.land/bubbletea/v2 from 2.0.2 to 2.0.6 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;13206](cli/cli#13206)
- chore(deps): bump github.com/gdamore/tcell/v2 from 2.13.8 to 2.13.9 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;13241](cli/cli#13241)
- chore(deps): bump github.com/mattn/go-isatty from 0.0.21 to 0.0.22 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;13298](cli/cli#13298)

#### New Contributors

- [@&#8203;orbisai0security](https://github.com/orbisai0security) made their first contribution in [#&#8203;13258](cli/cli#13258)
- [@&#8203;cassidyjames](https://github.com/cassidyjames) made their first contribution in [#&#8203;13273](cli/cli#13273)
- [@&#8203;travellertales](https://github.com/travellertales) made their first contribution in [#&#8203;13296](cli/cli#13296)

**Full Changelog**: <cli/cli@v2.91.0...v2.92.0>

### [`v2.91.0`](https://github.com/cli/cli/releases/tag/v2.91.0): GitHub CLI 2.91.0

[Compare Source](cli/cli@v2.90.0...v2.91.0)

#### GitHub CLI now collects pseudonymous telemetry

To better understand how features are used in practice, especially as agentic adoption grows, GitHub CLI now sends pseudonymous telemetry.

See [Telemetry](https://cli.github.com/telemetry) for more details on what's collected, why, and how to opt out.

#### Support more agents in `gh skill`

Thanks to community feedback, `gh` now supports a large number of agent hosts. Run `gh skill install --help` for the list of available agents.

#### Improve skill discovery

`gh skill install` now adds the `--allow-hidden-dirs` flag to support discovering skills in hidden (dot-prefixed) directories such as `.claude/skills/`, `.agents/skills/`, and `.github/skills/`.

#### Detect skills re-published from other sources

GitHut CLI now detects if the skill to be installed is re-published from an upstream source and offers the option to install it from there. The `--upstream` flag is also added for non-interactive use cases.

#### What's Changed

##### ✨ Features

- Add support for installation in multiple agent hosts in `gh skills install` by [@&#8203;tommaso-moro](https://github.com/tommaso-moro) in [#&#8203;13209](cli/cli#13209)
- Add --allow-hidden-dirs flag to gh skill install by [@&#8203;SamMorrowDrums](https://github.com/SamMorrowDrums) in [#&#8203;13213](cli/cli#13213)
- Make skill discovery less strict: support nested `skills/` directories by [@&#8203;SamMorrowDrums](https://github.com/SamMorrowDrums) in [#&#8203;13235](cli/cli#13235)
- feat(skills): detect re-published skills and offer upstream install by [@&#8203;SamMorrowDrums](https://github.com/SamMorrowDrums) in [#&#8203;13236](cli/cli#13236)

##### 🐛 Fixes

- Fix `skills publish --fix` to not publish by [@&#8203;SamMorrowDrums](https://github.com/SamMorrowDrums) in [#&#8203;13237](cli/cli#13237)
- fix(skills): match skills by install name in preview command by [@&#8203;SamMorrowDrums](https://github.com/SamMorrowDrums) in [#&#8203;13249](cli/cli#13249)

##### 📚 Docs & Chores

- Remove misleading text by [@&#8203;tommaso-moro](https://github.com/tommaso-moro) in [#&#8203;13203](cli/cli#13203)
- Add sampled command telemetry by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;13191](cli/cli#13191)
- Do not send telemetry for aliases by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;13192](cli/cli#13192)
- Add skills specific telemetry by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;13204](cli/cli#13204)
- Record CI context in telemetry by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;13210](cli/cli#13210)
- Record official extension telemetry by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;13205](cli/cli#13205)
- Add telemetry command by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;13253](cli/cli#13253)
- Log when there is no telemetry by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;13255](cli/cli#13255)
- docs(skills): add gh and gh-skill agent skills by [@&#8203;BagToad](https://github.com/BagToad) in [#&#8203;13244](cli/cli#13244)
- Enable telemetry without env var by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;13254](cli/cli#13254)

**Full Changelog**: <cli/cli@v2.90.0...v2.91.0>

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNjkuNCIsInVwZGF0ZWRJblZlciI6IjQzLjE2OS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJSZW5vdmF0ZSBCb3QiLCJhdXRvbWF0aW9uOmJvdC1hdXRob3JlZCIsImRlcGVuZGVuY3ktdHlwZTo6bWlub3IiXX0=-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gh-skill relating to the gh skill command

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants