mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Add public skills + improve repo skill discovery and error UX (#8098)
1. Adds SkillScope::Public end-to-end (core + protocol) and loads skills from the public cache directory 2. Improves repo skill discovery by searching upward for the nearest .codex/skills within a git repo 3. Deduplicates skills by name with deterministic ordering to avoid duplicates across sources 4. Fixes garbled “Skill errors” overlay rendering by preventing pending history lines from being injected during the modal 5. Updates the project docs “Skills” intro wording to avoid hardcoded paths
This commit is contained in:
1 parent
2041b72da7
commit
4897efcced
20 files changed
+1050
-73
No files matched your search
@@ -410,7 +410,10 @@ impl ChatWidget {
|
||||
}
|
||||
// Ask codex-core to enumerate custom prompts for this session.
|
||||
self.submit_op(Op::ListCustomPrompts);
|
||||
self.submit_op(Op::ListSkills { cwds: Vec::new() });
|
||||
self.submit_op(Op::ListSkills {
|
||||
cwds: Vec::new(),
|
||||
force_reload: false,
|
||||
});
|
||||
if let Some(user_message) = self.initial_user_message.take() {
|
||||
self.submit_user_message(user_message);
|
||||
}
|
||||
@@ -1886,6 +1889,12 @@ impl ChatWidget {
|
||||
EventMsg::McpListToolsResponse(ev) => self.on_list_mcp_tools(ev),
|
||||
EventMsg::ListCustomPromptsResponse(ev) => self.on_list_custom_prompts(ev),
|
||||
EventMsg::ListSkillsResponse(ev) => self.on_list_skills(ev),
|
||||
EventMsg::SkillsUpdateAvailable => {
|
||||
self.submit_op(Op::ListSkills {
|
||||
cwds: Vec::new(),
|
||||
force_reload: true,
|
||||
});
|
||||
}
|
||||
EventMsg::ShutdownComplete => self.on_shutdown_complete(),
|
||||
EventMsg::TurnDiff(TurnDiffEvent { unified_diff }) => self.on_turn_diff(unified_diff),
|
||||
EventMsg::DeprecationNotice(ev) => self.on_deprecation_notice(ev),
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use crate::tui::FrameRequester;
|
||||
use crate::tui::Tui;
|
||||
use crate::tui::TuiEvent;
|
||||
use crate::wrapping::RtOptions;
|
||||
use crate::wrapping::word_wrap_line;
|
||||
use codex_core::skills::SkillError;
|
||||
use crossterm::event::KeyCode;
|
||||
use crossterm::event::KeyEvent;
|
||||
@@ -16,7 +18,6 @@ use ratatui::widgets::Clear;
|
||||
use ratatui::widgets::Paragraph;
|
||||
use ratatui::widgets::Widget;
|
||||
use ratatui::widgets::WidgetRef;
|
||||
use ratatui::widgets::Wrap;
|
||||
use tokio_stream::StreamExt;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
@@ -31,16 +32,24 @@ pub(crate) async fn run_skill_error_prompt(
|
||||
) -> SkillErrorPromptOutcome {
|
||||
struct AltScreenGuard<'a> {
|
||||
tui: &'a mut Tui,
|
||||
stashed_history_lines: Vec<Line<'static>>,
|
||||
}
|
||||
impl<'a> AltScreenGuard<'a> {
|
||||
fn enter(tui: &'a mut Tui) -> Self {
|
||||
let _ = tui.enter_alt_screen();
|
||||
Self { tui }
|
||||
let stashed_history_lines = tui.stash_pending_history_lines();
|
||||
Self {
|
||||
tui,
|
||||
stashed_history_lines,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Drop for AltScreenGuard<'_> {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.tui.leave_alt_screen();
|
||||
let stashed_history_lines = std::mem::take(&mut self.stashed_history_lines);
|
||||
self.tui
|
||||
.restore_pending_history_lines(stashed_history_lines);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,28 +86,16 @@ pub(crate) async fn run_skill_error_prompt(
|
||||
|
||||
struct SkillErrorScreen {
|
||||
request_frame: FrameRequester,
|
||||
lines: Vec<Line<'static>>,
|
||||
errors: Vec<SkillError>,
|
||||
done: bool,
|
||||
exit: bool,
|
||||
}
|
||||
|
||||
impl SkillErrorScreen {
|
||||
fn new(request_frame: FrameRequester, errors: &[SkillError]) -> Self {
|
||||
let mut lines: Vec<Line<'static>> = Vec::new();
|
||||
lines.push(Line::from("Skill validation errors detected".bold()));
|
||||
lines.push(Line::from(
|
||||
"Fix these SKILL.md files and restart. Invalid skills are ignored until resolved. Press enter or esc to continue, Ctrl+C or Ctrl+D to exit.",
|
||||
));
|
||||
lines.push(Line::from(""));
|
||||
|
||||
for error in errors {
|
||||
let message = format!("- {}: {}", error.path.display(), error.message);
|
||||
lines.push(Line::from(message));
|
||||
}
|
||||
|
||||
Self {
|
||||
request_frame,
|
||||
lines,
|
||||
errors: errors.to_vec(),
|
||||
done: false,
|
||||
exit: false,
|
||||
}
|
||||
@@ -154,12 +151,44 @@ impl SkillErrorScreen {
|
||||
impl WidgetRef for &SkillErrorScreen {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
Clear.render(area, buf);
|
||||
|
||||
let block = Block::default()
|
||||
.title("Skill errors".bold())
|
||||
.borders(Borders::ALL);
|
||||
Paragraph::new(self.lines.clone())
|
||||
.block(block)
|
||||
.wrap(Wrap { trim: true })
|
||||
.render(area, buf);
|
||||
|
||||
let inner = block.inner(area);
|
||||
let width = usize::from(inner.width).max(1);
|
||||
|
||||
let mut base_lines: Vec<Line<'static>> = vec![
|
||||
Line::from("Skill validation errors detected".bold()),
|
||||
Line::from("Fix these SKILL.md files and restart."),
|
||||
Line::from("Invalid skills are ignored until resolved."),
|
||||
Line::from("Press enter or esc to continue. Ctrl+C or Ctrl+D to exit."),
|
||||
Line::from(""),
|
||||
];
|
||||
|
||||
let error_start = base_lines.len();
|
||||
for error in &self.errors {
|
||||
base_lines.push(Line::from(vec![
|
||||
error.path.display().to_string().dim(),
|
||||
": ".into(),
|
||||
error.message.clone().red(),
|
||||
]));
|
||||
}
|
||||
|
||||
let error_wrap_opts = RtOptions::new(width)
|
||||
.initial_indent(Line::from("- "))
|
||||
.subsequent_indent(Line::from(" "));
|
||||
|
||||
let mut lines: Vec<Line<'_>> = Vec::new();
|
||||
for (idx, line) in base_lines.iter().enumerate() {
|
||||
if idx < error_start {
|
||||
lines.extend(word_wrap_line(line, width));
|
||||
} else {
|
||||
lines.extend(word_wrap_line(line, error_wrap_opts.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
Paragraph::new(lines).block(block).render(area, buf);
|
||||
}
|
||||
}
|
||||
@@ -335,6 +335,14 @@ impl Tui {
|
||||
self.frame_requester().schedule_frame();
|
||||
}
|
||||
|
||||
pub(crate) fn stash_pending_history_lines(&mut self) -> Vec<Line<'static>> {
|
||||
std::mem::take(&mut self.pending_history_lines)
|
||||
}
|
||||
|
||||
pub(crate) fn restore_pending_history_lines(&mut self, lines: Vec<Line<'static>>) {
|
||||
self.pending_history_lines = lines;
|
||||
}
|
||||
|
||||
pub fn draw(
|
||||
&mut self,
|
||||
height: u16,
|
||||
|
||||
Reference in new issue
Block a user