Bug: user-installed skills under `~/.codex/skills` are skipped when `SKILL.md` is a symlink

Resolved 💬 1 comment Opened Apr 10, 2026 by light-merlin-dark Closed Apr 10, 2026

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.md back 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?

  1. Create a valid skill in a normal repo:
/Users/me/_dev/advising/skills/advising/SKILL.md
  1. Install a Codex wrapper directory:
/Users/me/.codex/skills/advising/SKILL.md -> /Users/me/_dev/advising/skills/advising/SKILL.md
  1. Ensure it is enabled in ~/.codex/config.toml:
[[skills.config]]
path = "/Users/me/.codex/skills/advising/SKILL.md"
enabled = true
  1. Ask Codex app-server for skills/list for a workspace.
  1. Observe that advising is missing.
  1. Repeat the same request, but inject perCwdExtraUserRoots that points to the repo-backed skills root.
  1. Observe that advising now 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.rs around the file_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.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗