Display actual skill names instead of "Read SKILL.md"

Resolved 💬 8 comments Opened Mar 31, 2026 by zly2006 Closed Apr 6, 2026
💡 Likely answer: A maintainer (etraut-openai, contributor) responded on this thread — see the highlighted reply below.

What variant of Codex are you using?

CLI

What feature would you like to see?

Currently, when a skill is explored, the system displays a generic Read SKILL.md. This makes it slightly difficult to grasp which specific skill is being executed at a glance.

Possible solution
It would be much clearer and more intuitive if we displayed the actual skill name (e.g., Skill docx) instead of the markdown file name.

Expected Behavior (Before & After)

Before:

• Explored
  └ Read SKILL.md
    List wechat_sxhm_report

After:

• Explored
  └ Skill docx
    List wechat_sxhm_report

Additional information

I have already implemented a draft of this improvement on my end. If the maintainers think this is a valuable enhancement, I would be more than happy to open a Pull Request for your review!
https://github.com/zly2006/codex/tree/feat/skill-names

View original on GitHub ↗

8 Comments

theshivam7 · 3 months ago

I've looked into this and identified the fix. The issue is in codex-rs/tui/src/exec_cell/render.rs in the reads_only branch of exploring_display_lines() (around line 297). Currently it extracts only name (which is always "SKILL.md") and labels it "Read". The fix is to detect when name == "SKILL.md" and extract path.parent().file_name() as the skill identifier instead, displaying "Skill python" rather than "Read SKILL.md". Regular file reads would be labeled "File". Edge case: when path has no parent, fall back to the raw filename safely via .and_then(). I have a working implementation ready if the team would like to review it.

theshivam7 · 3 months ago

Adding more detail on the root cause and fix for anyone evaluating this.

Root causecodex-rs/tui/src/exec_cell/render.rs, exploring_display_lines(), lines 297-309:

// current code
let names = call.parsed.iter()
    .map(|parsed| match parsed {
        ParsedCommand::Read { name, .. } => name.clone(), // always "SKILL.md"
        _ => unreachable!(),
    })
    .unique();
vec![("Read", Itertools::intersperse(names.into_iter().map(Into::into), ", ".dim()).collect())]

name in ParsedCommand::Read is the bare filename — it is always "SKILL.md" for skill reads. The path field (a PathBuf) contains the full path like /home/user/.codex/skills/python/SKILL.md, so the parent directory name is the actual skill identifier.

Fix — detect name == "SKILL.md" and extract path.parent().file_name():

let skills: Vec<String> = call.parsed.iter()
    .filter_map(|parsed| match parsed {
        ParsedCommand::Read { name, path, .. } => {
            if name == "SKILL.md" {
                let skill_name = path
                    .parent()
                    .and_then(|p| p.file_name())
                    .map(|n| n.to_string_lossy().into_owned())
                    .unwrap_or_else(|| name.clone()); // safe fallback if no parent
                Some(skill_name)
            } else { None }
        }
        _ => unreachable!(),
    })
    .unique()
    .collect();

Result: "Read SKILL.md""Skill python". Regular file reads (non-SKILL.md) get a "File" label. Mixed calls (skill + regular file in the same merged block) produce two separate display entries.

Tests written (in the existing #[cfg(test)] block):

  • skill_reads_display_skill_name_not_filename — full path resolves to skill name, SKILL.md hidden
  • mixed_skill_and_regular_reads_display_both_labels — both Skill and File labels render
  • skill_read_with_bare_path_falls_back_gracefully — bare path with no parent does not panic

The same fix is applied consistently in the else branch (mixed reads/non-reads calls).

Happy to submit a PR if invited.

zly2006 · 3 months ago

@theshivam7 are you spamming the community using LLMs? According to the skill standards, the SKILL.md must have a parent. And why are you voting up yourself?

theshivam7 · 3 months ago

Hey @zly2006, fair points. I did use AI assistance to help understand the codebase — I should have mentioned that upfront. That said, I did put in genuine effort to read through the code, understand the root cause, and write proper tests for the edge cases. The fallback for the bare path was intentional defensive coding, but I take your point that per skill standards it's not needed.

The upvote was just me reacting to the issue, not trying to game anything. No bad intent.

I'm still learning and trying to contribute where I can. Apologies if it came across the wrong way.

etraut-openai contributor · 3 months ago

The challenge here is that reads from a SKILL.md file are just like reads of any other project files from the perspective of the harness.

@theshivam7, the solution you're suggesting is a bit of a hack — a heuristic that will break under some circumstances. In particular, it won't work for a skill whose frontmatter-specified name differs from its directory name. To get the proper name, we'd need to look up its frontmatter name in an internal map of known skills.

theshivam7 · 3 months ago

Good point — I've updated the implementation to use the frontmatter name instead of the directory name.

The fix now builds a HashMap<PathBuf, String> from ChatWidget.skills_all at ExecCell creation time, mapping each skill's path_to_skills_md (normalized via dunce::canonicalize) to its display name (using skill_display_name, which respects interface.display_name before falling back to name). The render layer looks up ParsedCommand::Read { path } in that map — if found, the frontmatter name is shown; if not found, it falls back to the raw filename unchanged.

Changes across 5 files (exec_cell/model.rs, exec_cell/render.rs, chatwidget/skills.rs, chatwidget.rs, history_cell.rs). The implementation is on my fork at theshivam7/codex, branch fix/skill-names-in-tui, if useful for reference.

(I used an AI assistant only to help navigate and understand the codebase — the analysis, design, and implementation decisions are entirely my own.)

etraut-openai contributor · 3 months ago

This will be included in the next release.

theshivam7 · 3 months ago

Thank you! Really appreciate the feedback and the opportunity to contribute.