Skip to content

Codex CLI doesn't follow symlinks in .agents/skills/ — skills not discovered #333

Closed

Description

@cpdcpd

Problem

create_agents_sidecar() in setup creates symlinks in .agents/skills/ for each gstack skill:

.agents/skills/gstack-qa -> /path/to/.claude/skills/gstack/.agents/skills/gstack-qa/

Codex CLI (v0.116.0) does not follow symlinks when scanning .agents/skills/ for skill directories. Only real directories (like supabase-postgres-best-practices) are discovered. This means gstack skills are invisible to Codex's automatic skill discovery.

Evidence

When asked to list available skills, Codex reports:

"The repo-local .agents/skills only contains the Supabase skill"

It then manually explores .claude/skills/gstack/.agents/skills/ to find them — proving the SKILL.md files are correct, just not discovered via the symlink path.

Root cause

In setup, the create_agents_sidecar() function uses ln -snf for skill directories:

ln -snf "$skill_dir" "$root_skill_dir/$skill_name"

Codex likely uses a directory listing method that filters out symlinks (e.g., checking d_type or using stat instead of following links).

Workaround

Replace symlinks with real directory copies:

for link in .agents/skills/gstack-*; do
  if [ -L "$link" ]; then
    target=$(readlink "$link")
    rm "$link"
    cp -r "$target" "$link"
  fi
done

Suggested fix

In create_agents_sidecar(), use cp -r instead of ln -snf when --host codex is specified. This duplicates ~30KB of SKILL.md files per skill but ensures automatic discovery works. The root gstack sidecar (which has runtime assets like bin/ and browse/) should remain symlinked to avoid duplicating large binaries — or alternatively, copy SKILL.md and symlink only the heavy directories.

Environment

  • Codex CLI: v0.116.0
  • gstack: v0.10.2.0
  • macOS Darwin 25.3.0

Additional issue: global ~/.codex/skills/gstack/ missing runtime assets

The link_codex_skill_dirs() function points ~/.codex/skills/gstack at .agents/skills/gstack/ in the gstack source tree, which only contains SKILL.md. The workspace sidecar version (created by create_agents_sidecar()) includes bin/, browse/, qa/, review/, and ETHOS.md symlinks. The SKILL.md preamble runs ~/.codex/skills/gstack/bin/gstack-update-check which fails because bin/ doesn't exist at that path.

Fix: link_codex_skill_dirs() should point ~/.codex/skills/gstack at the workspace sidecar, not the generated skeleton.

Activity

  1. cpdcpd commented on Mar 22, 2026

    @cpdcpd
    Author

    Additional finding: Codex requires agents/openai.yaml for skill registration

    Codex CLI doesn't discover skills from SKILL.md alone. It requires agents/openai.yaml with at minimum:

    interface:
      display_name: "skill-name"
      short_description: "Brief description"

    All curated Codex skills (figma, openai-docs, skill-installer, etc.) include this file. Without it, Codex can read the SKILL.md content when pointed to it manually, but won't register the skill in its UI or autocomplete.

    Suggested fix for gen-skill-docs.ts: when --host codex is specified, also generate agents/openai.yaml alongside each SKILL.md using the name and description from the frontmatter.

  2. malikrohail commented on Mar 22, 2026

    @malikrohail
    Contributor

    Tested on March 22, 2026 against current main (cc9e6f8) with codex-cli 0.116.0.

    I could not reproduce the symlink-specific failure described here on current main: direct skill dirs, symlinked skill dirs, repo-local .agents/skills symlinks, and chained symlinks all loaded in my repros.

    The current reproducible Codex bug on main was different: the root gstack skill failed to load because its YAML frontmatter description was over Codex CLI's current 1024-character limit. I reproduced:

    failed to load skill .../gstack/SKILL.md: invalid description: exceeds maximum length of 1024 characters
    

    and the same failure for:

    .../gstack/.agents/skills/gstack/SKILL.md
    

    I opened PR #334 with the smallest end-to-end fix I could verify: shorten the root skill description and add a regression test covering both root skill surfaces Codex loads today.

    PR #326 does not address this path. It does not touch the root gstack skill frontmatter or Codex root-skill loading.

  3. malikrohail commented on Mar 22, 2026

    @malikrohail
    Contributor

    I reproduced the additional Codex metadata/discovery symptom from the thread on March 22, 2026.

    On this machine, the gstack-* skill symlinks existed under ~/.codex/skills/, but the Codex picker symptom still showed only gstack-upgrade reliably when searching by the gstack prefix. The missing piece was Codex metadata:

    • no root agents/openai.yaml for the gstack bundle
    • no agents/openai.yaml files for the generated .agents/skills/gstack-* skills

    I opened PR #339 to add that metadata and verified it with a real ./setup --host codex + codex exec run. After the change, the visible gstack-* set is:

    gstack, gstack-autoplan, gstack-benchmark, gstack-browse, gstack-canary, gstack-careful, gstack-cso, gstack-design-consultation, gstack-design-review, gstack-document-release, gstack-freeze, gstack-guard, gstack-investigate, gstack-land-and-deploy, gstack-office-hours, gstack-plan-ceo-review, gstack-plan-design-review, gstack-plan-eng-review, gstack-qa, gstack-qa-only, gstack-retro, gstack-review, gstack-setup-browser-cookies, gstack-setup-deploy, gstack-ship, gstack-unfreeze, gstack-upgrade

    PR: #339

  4. 100yenadmin commented on Jul 12, 2026

    @100yenadmin

    Retested this against current main (7c9df1c568a9ea745508f679a329332b2c338063, gstack v1.60.1.0) with Codex CLI 0.144.0 on macOS. The symlink-specific failure is reproducible for the global Codex install path.

    Before the fix:

    • ./setup --host codex left 53 ~/.codex/skills/gstack-* directory symlinks.
    • ~/.codex/skills/gstack/SKILL.md was also a file symlink.
    • A fresh codex app-server --stdio skills/list request did not register those symlinked skill directories or the root router.
    • The runtime sidecar exposed a second nested gstack-upgrade/SKILL.md, creating a duplicate upgrade surface if symlink traversal behavior changed.

    I tested a local patch with these changes:

    1. Copy each generated Codex skill directory with cp -R after removing the previous gstack-* target.
    2. Copy the root router SKILL.md as a regular file while retaining symlinks only for large runtime assets (bin, browse, review assets, and ETHOS.md).
    3. Remove the nested runtime-sidecar gstack-upgrade/SKILL.md.
    4. Generate Codex upgrade references as $GSTACK_ROOT/../gstack-upgrade/SKILL.md; preserve the existing nested path for other hosts.

    Verification after running the patched setup twice:

    • 54 top-level GStack skill directories (53 generated skills plus the router), all regular directories.
    • 54 top-level SKILL.md files, all regular files.
    • Exactly one gstack router and one gstack-upgrade skill.
    • No nested ~/.codex/skills/gstack/gstack-upgrade/SKILL.md.
    • Fresh app-server inventory: 54 installed GStack skills, zero GStack source-tree paths, zero duplicate unqualified names, and no skill-load errors.
    • Generator/setup regression suite: 404 passing tests, 0 failures; bash -n setup and git diff --check pass.

    The relevant local patch is 33 insertions / 13 deletions across setup, scripts/resolvers/preamble/generate-upgrade-check.ts, and test/gen-skill-docs.test.ts. I have not opened a PR; happy to do so if maintainers want the tested diff.

  5. time-attack commented on Jul 15, 2026

    @time-attack
    Contributor

    @cpdcpd Codex CLI skill discovery through symlinks remains unresolved until the selected implementation lands.

  6. capy-ai commented on Oct 4, 2026

    @capy-ai
    Contributor

    Setup now installs the gstack router and nested SKILL.md files as real copies instead of symlinks, so Codex discovers them, and ./setup --status reports whether each install's router is a real file. Fixed in v1.91.19.0 (#3025).

    To upgrade: cd <source from ./setup --status> && git pull && ./setup --host codex, then start a new session.

    If you still see this, please reopen with ./setup --status output.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions