Bug: user-installed skills under `~/.codex/skills` are skipped when `SKILL.md` is a symlink
What version of the Codex App are you using (From “About Codex” dialog)?
Version 26.406.31014 (1395)
What subscription do you have?
ChatGPT Pro
What platform is your computer?
Darwin 25.3.0 arm64 arm
What issue are you seeing?
Codex currently skips valid user skills installed under ~/.codex/skills/<slug>/SKILL.md when the SKILL.md file itself is a symlink to a canonical skill file stored elsewhere, for example in a repo under _dev/.../skills/.../SKILL.md.
This breaks a common wrapper-install pattern:
- keep the canonical skill in a normal repo
- install a lightweight wrapper directory in
~/.codex/skills/<slug>/ - symlink only
SKILL.mdback to the canonical source
The skill is valid, enabled, and readable, but it does not appear in Codex skills/list or the desktop UI.
This blocks tools like SkillSync from using a clean symlink-first install strategy for Codex.
Without an upstream fix, users are forced to:
- duplicate or materialize skill files into
~/.codex/skills - maintain custom extra-root injection outside the normal Codex desktop flow
- accept drift between canonical repo skills and Codex-visible installs
The wrapper-directory pattern is attractive because it avoids top-level directory-symlink pollution while preserving one canonical skill source.
What steps can reproduce the bug?
- Create a valid skill in a normal repo:
/Users/me/_dev/advising/skills/advising/SKILL.md
- Install a Codex wrapper directory:
/Users/me/.codex/skills/advising/SKILL.md -> /Users/me/_dev/advising/skills/advising/SKILL.md
- Ensure it is enabled in
~/.codex/config.toml:
[[skills.config]]
path = "/Users/me/.codex/skills/advising/SKILL.md"
enabled = true
- Ask Codex app-server for
skills/listfor a workspace.
- Observe that
advisingis missing.
- Repeat the same request, but inject
perCwdExtraUserRootsthat points to the repo-backed skills root.
- Observe that
advisingnow appears immediately.
What is the expected behavior?
If ~/.codex/skills/<slug>/SKILL.md exists, is enabled, and resolves to a valid file, Codex should parse it and include it in skills/list and the desktop skills UI.
Symlinked SKILL.md files should behave the same as regular files.
Additional information
In codex-rs/core-skills/src/loader.rs, the loader handles symlinks like this:
- if the symlink resolves to a directory, it follows it
- otherwise it
continues
Relevant code:
codex-rs/core-skills/src/loader.rsaround thefile_type.is_symlink()branch
The specific behavior is visible here in current main:
- symlink branch follows directories only
- file symlinks, including
SKILL.md, are dropped
This appears to be the direct reason user wrapper installs are invisible.
In the symlink branch, if:
- the resolved metadata is a file, and
- the entry name is
SKILL.md
then call parse_skill_file(&path, scope) instead of skipping it.
That is a small, targeted fix and should preserve the existing directory-follow behavior.
Pseudo-shape:
if file_type.is_symlink() {
if !follow_symlinks {
continue;
}
let metadata = match fs::metadata(&path) {
Ok(metadata) => metadata,
Err(e) => { ... }
};
if metadata.is_dir() {
...
continue;
}
if metadata.is_file() && file_name == SKILLS_FILENAME {
match parse_skill_file(&path, scope) {
Ok(skill) => outcome.skills.push(skill),
Err(err) => { ... }
}
continue;
}
continue;
}
There is also a related but larger UX gap:
- app-server already supports `perCwdExtraUserRoots`
- the desktop/TUI currently sends `skills/list` with `per_cwd_extra_user_roots: None`
That makes extra roots a hidden protocol capability rather than a durable desktop feature.
However, the symlinked-`SKILL.md` bug above is the smaller and more direct fix for wrapper installs under `~/.codex/skills`.
Protocol/docs and tests already support extra roots:
- `codex-rs/app-server/README.md` documents `perCwdExtraUserRoots`
- `codex-rs/app-server/tests/suite/v2/skills_list.rs` includes coverage for extra roots
Desktop/TUI currently does not pass them:
- `codex-rs/tui/src/app.rs` sends `SkillsListParams { ..., per_cwd_extra_user_roots: None }`
This would make Codex interoperate cleanly with repo-backed skill managers that install safe wrapper directories into `~/.codex/skills` while keeping canonical skills in versioned repos.
It would also reduce the need for copied/materialized installs and avoid unnecessary skill drift.This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗