agent_ui: Replace @rule mentions with @skill (#56902)
Replaces the `@rule` autocomplete entry in the agent chat input with `@skill`, since Zed Rules are being deprecated in favor of Skills. Behavior matches the old `@rule` flow: selecting a skill inserts a Skill mention crease that loads the skill file as context, and the entry only appears when there are available skills. The underlying `MentionUri::Rule` parsing/handling is left in place so previously saved rule mentions still load correctly — only the autocomplete surface area was migrated. Release Notes: - Replaced the `@rule` autocomplete entry with `@skill` in the agent chat input.
This commit is contained in:
1 parent
b85e6ad929
commit
ee41bc1ca9
4 files changed
+71
-71
No files matched your search
@@ -22,7 +22,7 @@ use project::{
|
||||
Completion, CompletionDisplayOptions, CompletionGroup, CompletionIntent, CompletionResponse,
|
||||
DiagnosticSummary, PathMatchCandidateSet, Project, ProjectPath, Symbol, WorktreeId,
|
||||
};
|
||||
use prompt_store::{PromptStore, UserPromptId};
|
||||
|
||||
use rope::Point;
|
||||
use settings::Settings;
|
||||
use terminal::terminal_settings::TerminalSettings;
|
||||
@@ -158,7 +158,7 @@ pub(crate) enum PromptContextType {
|
||||
Symbol,
|
||||
Fetch,
|
||||
Thread,
|
||||
Rules,
|
||||
Skill,
|
||||
Diagnostics,
|
||||
BranchDiff,
|
||||
}
|
||||
@@ -197,7 +197,7 @@ impl TryFrom<&str> for PromptContextType {
|
||||
"symbol" => Ok(Self::Symbol),
|
||||
"fetch" => Ok(Self::Fetch),
|
||||
"thread" => Ok(Self::Thread),
|
||||
"rule" => Ok(Self::Rules),
|
||||
"skill" => Ok(Self::Skill),
|
||||
"diagnostics" => Ok(Self::Diagnostics),
|
||||
"diff" => Ok(Self::BranchDiff),
|
||||
_ => Err(format!("Invalid context picker mode: {}", value)),
|
||||
@@ -212,7 +212,7 @@ impl PromptContextType {
|
||||
Self::Symbol => "symbol",
|
||||
Self::Fetch => "fetch",
|
||||
Self::Thread => "thread",
|
||||
Self::Rules => "rule",
|
||||
Self::Skill => "skill",
|
||||
Self::Diagnostics => "diagnostics",
|
||||
Self::BranchDiff => "branch diff",
|
||||
}
|
||||
@@ -224,7 +224,7 @@ impl PromptContextType {
|
||||
Self::Symbol => "Symbols",
|
||||
Self::Fetch => "Fetch",
|
||||
Self::Thread => "Threads",
|
||||
Self::Rules => "Rules",
|
||||
Self::Skill => "Skills",
|
||||
Self::Diagnostics => "Diagnostics",
|
||||
Self::BranchDiff => "Branch Diff",
|
||||
}
|
||||
@@ -236,7 +236,7 @@ impl PromptContextType {
|
||||
Self::Symbol => IconName::Code,
|
||||
Self::Fetch => IconName::ToolWeb,
|
||||
Self::Thread => IconName::Thread,
|
||||
Self::Rules => IconName::Reader,
|
||||
Self::Skill => IconName::Sparkle,
|
||||
Self::Diagnostics => IconName::Warning,
|
||||
Self::BranchDiff => IconName::GitBranch,
|
||||
}
|
||||
@@ -249,7 +249,7 @@ pub(crate) enum Match {
|
||||
Thread(SessionMatch),
|
||||
RecentThread(SessionMatch),
|
||||
Fetch(SharedString),
|
||||
Rules(RulesContextEntry),
|
||||
Skill(AvailableSkill),
|
||||
Entry(EntryMatch),
|
||||
BranchDiff(BranchDiffMatch),
|
||||
}
|
||||
@@ -267,7 +267,7 @@ impl Match {
|
||||
Match::Thread(_) => 1.,
|
||||
Match::RecentThread(_) => 1.,
|
||||
Match::Symbol(_) => 1.,
|
||||
Match::Rules(_) => 1.,
|
||||
Match::Skill(_) => 1.,
|
||||
Match::Fetch(_) => 1.,
|
||||
Match::BranchDiff(_) => 1.,
|
||||
}
|
||||
@@ -291,12 +291,6 @@ fn session_title(title: Option<SharedString>) -> SharedString {
|
||||
.unwrap_or_else(|| SharedString::new_static(DEFAULT_THREAD_TITLE))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RulesContextEntry {
|
||||
pub prompt_id: UserPromptId,
|
||||
pub title: SharedString,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AvailableSkill {
|
||||
pub name: Arc<str>,
|
||||
@@ -354,7 +348,6 @@ pub struct PromptCompletionProvider<T: PromptCompletionProviderDelegate> {
|
||||
source: Arc<T>,
|
||||
editor: WeakEntity<Editor>,
|
||||
mention_set: Entity<MentionSet>,
|
||||
prompt_store: Option<Entity<PromptStore>>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
}
|
||||
|
||||
@@ -363,7 +356,6 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
source: T,
|
||||
editor: WeakEntity<Editor>,
|
||||
mention_set: Entity<MentionSet>,
|
||||
prompt_store: Option<Entity<PromptStore>>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
) -> Self {
|
||||
Self {
|
||||
@@ -371,7 +363,6 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
editor,
|
||||
mention_set,
|
||||
workspace,
|
||||
prompt_store,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -460,8 +451,8 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
}
|
||||
}
|
||||
|
||||
fn completion_for_rules(
|
||||
rule: RulesContextEntry,
|
||||
fn completion_for_skill(
|
||||
skill: AvailableSkill,
|
||||
source_range: Range<Anchor>,
|
||||
source: Arc<T>,
|
||||
editor: WeakEntity<Editor>,
|
||||
@@ -469,25 +460,35 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
workspace: Entity<Workspace>,
|
||||
cx: &mut App,
|
||||
) -> Completion {
|
||||
let uri = MentionUri::Rule {
|
||||
id: rule.prompt_id.into(),
|
||||
name: rule.title.to_string(),
|
||||
let uri = MentionUri::Skill {
|
||||
name: skill.name.to_string(),
|
||||
source: skill.source.to_string(),
|
||||
skill_file_path: skill.skill_file_path.clone(),
|
||||
};
|
||||
let new_text = format!("{} ", uri.as_link());
|
||||
let new_text_len = new_text.len();
|
||||
let icon_path = uri.icon_path(cx);
|
||||
let crease_text: SharedString = uri.name().into();
|
||||
let source_highlight_id = cx
|
||||
.theme()
|
||||
.syntax()
|
||||
.highlight_id("variable")
|
||||
.map(HighlightId::new);
|
||||
let label = build_slash_item_label(&skill.name, Some(&skill.source), source_highlight_id);
|
||||
Completion {
|
||||
replace_range: source_range.clone(),
|
||||
new_text,
|
||||
label: CodeLabel::plain(rule.title.to_string(), None),
|
||||
documentation: None,
|
||||
label,
|
||||
documentation: Some(CompletionDocumentation::MultiLinePlainText(
|
||||
skill.description.into(),
|
||||
)),
|
||||
insert_text_mode: None,
|
||||
source: project::CompletionSource::Custom,
|
||||
match_start: None,
|
||||
snippet_deduplication_key: None,
|
||||
icon_path: Some(icon_path),
|
||||
confirm: Some(confirm_completion_callback(
|
||||
rule.title,
|
||||
crease_text,
|
||||
source_range.start,
|
||||
new_text_len - 1,
|
||||
uri,
|
||||
@@ -989,20 +990,16 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
}
|
||||
}
|
||||
|
||||
Some(PromptContextType::Rules) => {
|
||||
if let Some(prompt_store) = self.prompt_store.as_ref() {
|
||||
let search_rules_task =
|
||||
search_rules(query, cancellation_flag, prompt_store, cx);
|
||||
cx.background_spawn(async move {
|
||||
search_rules_task
|
||||
.await
|
||||
.into_iter()
|
||||
.map(Match::Rules)
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
} else {
|
||||
Task::ready(Vec::new())
|
||||
}
|
||||
Some(PromptContextType::Skill) => {
|
||||
let skills = self.source.available_skills(cx);
|
||||
let search_skills_task = search_skills(query, cancellation_flag, skills, cx);
|
||||
cx.background_spawn(async move {
|
||||
search_skills_task
|
||||
.await
|
||||
.into_iter()
|
||||
.map(Match::Skill)
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
}
|
||||
|
||||
Some(PromptContextType::Diagnostics) => Task::ready(Vec::new()),
|
||||
@@ -1231,9 +1228,10 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
));
|
||||
}
|
||||
|
||||
if self.prompt_store.is_some() && self.source.supports_context(PromptContextType::Rules, cx)
|
||||
if self.source.supports_context(PromptContextType::Skill, cx)
|
||||
&& !self.source.available_skills(cx).is_empty()
|
||||
{
|
||||
entries.push(PromptContextEntry::Mode(PromptContextType::Rules));
|
||||
entries.push(PromptContextEntry::Mode(PromptContextType::Skill));
|
||||
}
|
||||
|
||||
if self.source.supports_context(PromptContextType::Fetch, cx) {
|
||||
@@ -1617,8 +1615,8 @@ impl<T: PromptCompletionProviderDelegate> CompletionProvider for PromptCompletio
|
||||
cx,
|
||||
))
|
||||
}
|
||||
Match::Rules(user_rules) => Some(Self::completion_for_rules(
|
||||
user_rules,
|
||||
Match::Skill(skill) => Some(Self::completion_for_skill(
|
||||
skill,
|
||||
source_range.clone(),
|
||||
source.clone(),
|
||||
editor.clone(),
|
||||
@@ -2281,29 +2279,36 @@ async fn filter_sessions(
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn search_rules(
|
||||
pub(crate) fn search_skills(
|
||||
query: String,
|
||||
cancellation_flag: Arc<AtomicBool>,
|
||||
prompt_store: &Entity<PromptStore>,
|
||||
skills: Vec<AvailableSkill>,
|
||||
cx: &mut App,
|
||||
) -> Task<Vec<RulesContextEntry>> {
|
||||
let search_task = prompt_store.read(cx).search(query, cancellation_flag, cx);
|
||||
) -> Task<Vec<AvailableSkill>> {
|
||||
if skills.is_empty() {
|
||||
return Task::ready(Vec::new());
|
||||
}
|
||||
let executor = cx.background_executor().clone();
|
||||
cx.background_spawn(async move {
|
||||
search_task
|
||||
.await
|
||||
let candidates = skills
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(id, skill)| StringMatchCandidate::new(id, &skill.name))
|
||||
.collect::<Vec<_>>();
|
||||
let matches = fuzzy::match_strings(
|
||||
&candidates,
|
||||
&query,
|
||||
false,
|
||||
true,
|
||||
100,
|
||||
&cancellation_flag,
|
||||
executor,
|
||||
)
|
||||
.await;
|
||||
matches
|
||||
.into_iter()
|
||||
.flat_map(|metadata| {
|
||||
// Default prompts are filtered out as they are automatically included.
|
||||
if metadata.default {
|
||||
None
|
||||
} else {
|
||||
Some(RulesContextEntry {
|
||||
prompt_id: metadata.id.as_user()?,
|
||||
title: metadata.title?,
|
||||
})
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.map(|mat| skills[mat.candidate_id].clone())
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -4229,8 +4229,8 @@ impl ThreadView {
|
||||
}),
|
||||
)
|
||||
.item(
|
||||
ContextMenuEntry::new("Rules")
|
||||
.icon(IconName::Reader)
|
||||
ContextMenuEntry::new("Skills")
|
||||
.icon(IconName::Sparkle)
|
||||
.icon_color(Color::Muted)
|
||||
.icon_size(IconSize::XSmall)
|
||||
.handler({
|
||||
@@ -4238,7 +4238,7 @@ impl ThreadView {
|
||||
move |window, cx| {
|
||||
message_editor.focus_handle(cx).focus(window, cx);
|
||||
message_editor.update(cx, |editor, cx| {
|
||||
editor.insert_context_type("rule", window, cx);
|
||||
editor.insert_context_type("skill", window, cx);
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -63,7 +63,6 @@ pub struct PromptEditor<T> {
|
||||
pub editor: Entity<Editor>,
|
||||
mode: PromptEditorMode,
|
||||
mention_set: Entity<MentionSet>,
|
||||
prompt_store: Option<Entity<PromptStore>>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
model_selector: Entity<AgentModelSelector>,
|
||||
edited_since_done: bool,
|
||||
@@ -334,7 +333,6 @@ impl<T: 'static> PromptEditor<T> {
|
||||
PromptEditorCompletionProviderDelegate,
|
||||
cx.weak_entity(),
|
||||
self.mention_set.clone(),
|
||||
self.prompt_store.clone(),
|
||||
self.workspace.clone(),
|
||||
))));
|
||||
});
|
||||
@@ -1214,7 +1212,7 @@ impl PromptCompletionProviderDelegate for PromptEditorCompletionProviderDelegate
|
||||
PromptContextType::Symbol,
|
||||
PromptContextType::Thread,
|
||||
PromptContextType::Fetch,
|
||||
PromptContextType::Rules,
|
||||
PromptContextType::Skill,
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1286,7 +1284,6 @@ impl PromptEditor<BufferCodegen> {
|
||||
let mut this: PromptEditor<BufferCodegen> = PromptEditor {
|
||||
editor: prompt_editor.clone(),
|
||||
mention_set,
|
||||
prompt_store,
|
||||
workspace,
|
||||
model_selector: cx.new(|cx| {
|
||||
AgentModelSelector::new(
|
||||
@@ -1438,7 +1435,6 @@ impl PromptEditor<TerminalCodegen> {
|
||||
let mut this = Self {
|
||||
editor: prompt_editor.clone(),
|
||||
mention_set,
|
||||
prompt_store,
|
||||
workspace,
|
||||
model_selector: cx.new(|cx| {
|
||||
AgentModelSelector::new(
|
||||
|
||||
@@ -100,7 +100,7 @@ impl SessionCapabilities {
|
||||
supported.extend(&[
|
||||
PromptContextType::Diagnostics,
|
||||
PromptContextType::Fetch,
|
||||
PromptContextType::Rules,
|
||||
PromptContextType::Skill,
|
||||
PromptContextType::BranchDiff,
|
||||
]);
|
||||
}
|
||||
@@ -516,7 +516,6 @@ impl MessageEditor {
|
||||
},
|
||||
editor.downgrade(),
|
||||
mention_set.clone(),
|
||||
prompt_store.clone(),
|
||||
workspace.clone(),
|
||||
));
|
||||
editor.update(cx, |editor, _cx| {
|
||||
|
||||
Reference in new issue
Block a user