Annotate skill doc reads with skill names (#16813)

Addresses #16303

Problem: Skill doc reads render as plain `Read SKILL.md`, so the TUI
hides which skill was opened.

Solution: Best-effort annotate exact `SKILL.md` reads with the matching
loaded skill name from `skills_all` before rendering exec cells.

Before:
```
• Explored
  └ Read SKILL.md
```

After:
```
• Explored
  └ Read SKILL.md (pr-babysitter skill)
```
This commit is contained in:
Eric Traut
2026-04-06 08:51:34 -07:00
committed by GitHub
Unverified
parent 4294031a93
commit f44eb29181
2 changed files with 31 additions and 3 deletions
+5 -3
View File
@@ -4206,6 +4206,7 @@ impl ChatWidget {
Some(rc) => (rc.command, rc.parsed_cmd, rc.source),
None => (ev.command.clone(), ev.parsed_cmd.clone(), ev.source),
};
let parsed = self.annotate_skill_reads_in_parsed_cmd(parsed);
let is_unified_exec_interaction =
matches!(source, ExecCommandSource::UnifiedExecInteraction);
let end_target = match self.active_cell.as_ref() {
@@ -4425,11 +4426,12 @@ impl ChatWidget {
pub(crate) fn handle_exec_begin_now(&mut self, ev: ExecCommandBeginEvent) {
// Ensure the status indicator is visible while the command runs.
self.bottom_pane.ensure_status_indicator();
let parsed_cmd = self.annotate_skill_reads_in_parsed_cmd(ev.parsed_cmd.clone());
self.running_commands.insert(
ev.call_id.clone(),
RunningCommand {
command: ev.command.clone(),
parsed_cmd: ev.parsed_cmd.clone(),
parsed_cmd: parsed_cmd.clone(),
source: ev.source,
},
);
@@ -4462,7 +4464,7 @@ impl ChatWidget {
&& let Some(new_exec) = cell.with_added_call(
ev.call_id.clone(),
ev.command.clone(),
ev.parsed_cmd.clone(),
parsed_cmd.clone(),
ev.source,
interaction_input.clone(),
)
@@ -4475,7 +4477,7 @@ impl ChatWidget {
self.active_cell = Some(Box::new(new_active_exec_command(
ev.call_id.clone(),
ev.command.clone(),
ev.parsed_cmd,
parsed_cmd,
ev.source,
interaction_input,
self.config.animations,
+26
View File
@@ -19,6 +19,7 @@ use codex_core::skills::model::SkillDependencies;
use codex_core::skills::model::SkillInterface;
use codex_core::skills::model::SkillMetadata;
use codex_core::skills::model::SkillToolDependency;
use codex_protocol::parse_command::ParsedCommand;
use codex_protocol::protocol::ListSkillsResponseEvent;
use codex_protocol::protocol::SkillMetadata as ProtocolSkillMetadata;
use codex_protocol::protocol::SkillsListEntry;
@@ -142,6 +143,31 @@ impl ChatWidget {
self.skills_all = skills;
self.set_skills(Some(enabled_skills_for_mentions(&self.skills_all)));
}
pub(crate) fn annotate_skill_reads_in_parsed_cmd(
&self,
mut parsed_cmd: Vec<ParsedCommand>,
) -> Vec<ParsedCommand> {
if self.skills_all.is_empty() {
return parsed_cmd;
}
for parsed in &mut parsed_cmd {
let ParsedCommand::Read { name, path, .. } = parsed else {
continue;
};
if name != "SKILL.md" {
continue;
}
// Best effort only: annotate exact SKILL.md path matches from the loaded skills list.
if let Some(skill) = self.skills_all.iter().find(|skill| skill.path == *path) {
*name = format!("{name} ({} skill)", skill.name);
}
}
parsed_cmd
}
}
fn skills_for_cwd(cwd: &Path, skills_entries: &[SkillsListEntry]) -> Vec<ProtocolSkillMetadata> {