mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
Remove Skills feature flag (#57162)
Removes the `skills` feature flag now that Skills are ready to ship to all users. Cleans up all `cx.has_flag::<SkillsFeatureFlag>()` gates across the agent, agent_ui, prompt_store, and title_bar crates, drops the now-unused `feature_flags` dependency from `prompt_store` and `title_bar`, and removes the dead `update_flags(true, vec!["skills"])` calls from the agent's skills tests. Closes AI-269 Release Notes: - Enabled Skills for all users. --------- Co-authored-by: MartinYe1234 <52641447+MartinYe1234@users.noreply.github.com> Co-authored-by: Martin Ye <martinye022@gmail.com>
This commit is contained in:
parent
952119de41
commit
2e70059cd9
9 changed files with 43 additions and 123 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -13688,7 +13688,6 @@ dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"collections",
|
"collections",
|
||||||
"db",
|
"db",
|
||||||
"feature_flags",
|
|
||||||
"fs",
|
"fs",
|
||||||
"futures 0.3.32",
|
"futures 0.3.32",
|
||||||
"fuzzy",
|
"fuzzy",
|
||||||
|
|
@ -18251,7 +18250,6 @@ dependencies = [
|
||||||
"client",
|
"client",
|
||||||
"cloud_api_types",
|
"cloud_api_types",
|
||||||
"db",
|
"db",
|
||||||
"feature_flags",
|
|
||||||
"fs",
|
"fs",
|
||||||
"git_ui",
|
"git_ui",
|
||||||
"gpui",
|
"gpui",
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ use agent_skills::{
|
||||||
use anyhow::{Context as _, Result, anyhow};
|
use anyhow::{Context as _, Result, anyhow};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use collections::{HashMap, HashSet, IndexMap};
|
use collections::{HashMap, HashSet, IndexMap};
|
||||||
use feature_flags::{FeatureFlagAppExt as _, SkillsFeatureFlag};
|
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use futures::channel::{mpsc, oneshot};
|
use futures::channel::{mpsc, oneshot};
|
||||||
use futures::future::Shared;
|
use futures::future::Shared;
|
||||||
|
|
@ -392,11 +392,11 @@ impl NativeAgent {
|
||||||
/// Kicks off a one-time scan of the global skills directory if one
|
/// Kicks off a one-time scan of the global skills directory if one
|
||||||
/// isn't already in progress and a watch isn't already active.
|
/// isn't already in progress and a watch isn't already active.
|
||||||
///
|
///
|
||||||
/// Idempotent and cheap: returns immediately if the user lacks the
|
/// Idempotent and cheap: returns immediately if a scan or watch is
|
||||||
/// skills feature flag, or if a scan or watch is already running.
|
/// already running. The expected callers are user-interaction events
|
||||||
/// The expected callers are user-interaction events from the agent
|
/// from the agent panel (input focus, slash autocomplete, conversation
|
||||||
/// panel (input focus, slash autocomplete, conversation submit);
|
/// submit); firing this from any of them is equivalent and safe to
|
||||||
/// firing this from any of them is equivalent and safe to repeat.
|
/// repeat.
|
||||||
///
|
///
|
||||||
/// The scan itself runs detached on the foreground executor. If
|
/// The scan itself runs detached on the foreground executor. If
|
||||||
/// `~/.agents/skills/` exists it transitions state to
|
/// `~/.agents/skills/` exists it transitions state to
|
||||||
|
|
@ -405,9 +405,6 @@ impl NativeAgent {
|
||||||
/// next trigger retries (covering the case where the user creates
|
/// next trigger retries (covering the case where the user creates
|
||||||
/// the directory after the first scan).
|
/// the directory after the first scan).
|
||||||
pub fn ensure_skills_scan_started(&mut self, cx: &mut Context<Self>) {
|
pub fn ensure_skills_scan_started(&mut self, cx: &mut Context<Self>) {
|
||||||
if !cx.has_flag::<SkillsFeatureFlag>() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if !matches!(self.skills_state, SkillsState::Idle) {
|
if !matches!(self.skills_state, SkillsState::Idle) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -598,12 +595,10 @@ impl NativeAgent {
|
||||||
// after the thread is constructed are still visible to the
|
// after the thread is constructed are still visible to the
|
||||||
// model — without this, the catalog and tool would drift out
|
// model — without this, the catalog and tool would drift out
|
||||||
// of sync until the session was reopened.
|
// of sync until the session was reopened.
|
||||||
if cx.has_flag::<SkillsFeatureFlag>() {
|
thread.add_tool(SkillTool::new(
|
||||||
thread.add_tool(SkillTool::new(
|
skills_resolver_for_project(weak.clone(), project_id),
|
||||||
skills_resolver_for_project(weak.clone(), project_id),
|
self.fs.clone(),
|
||||||
self.fs.clone(),
|
));
|
||||||
));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let subscriptions = vec![
|
let subscriptions = vec![
|
||||||
|
|
@ -822,12 +817,8 @@ impl NativeAgent {
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// Skills are gated behind the "skills" feature flag. Without it we
|
|
||||||
// skip all on-disk lookups so users see no behavior change.
|
|
||||||
let skills_enabled = cx.has_flag::<SkillsFeatureFlag>();
|
|
||||||
|
|
||||||
// Load global skills
|
// Load global skills
|
||||||
let global_skills_task = if skills_enabled {
|
let global_skills_task = {
|
||||||
let global_skills_dir = global_skills_dir();
|
let global_skills_dir = global_skills_dir();
|
||||||
let global_skills_fs = fs.clone();
|
let global_skills_fs = fs.clone();
|
||||||
cx.background_spawn(async move {
|
cx.background_spawn(async move {
|
||||||
|
|
@ -838,8 +829,6 @@ impl NativeAgent {
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
Task::ready(Vec::new())
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Load project-local skills, but only from worktrees the user has
|
// Load project-local skills, but only from worktrees the user has
|
||||||
|
|
@ -852,7 +841,7 @@ impl NativeAgent {
|
||||||
// worktrees pick up their skills without restarting.
|
// worktrees pick up their skills without restarting.
|
||||||
let trusted_worktrees = TrustedWorktrees::try_get_global(cx);
|
let trusted_worktrees = TrustedWorktrees::try_get_global(cx);
|
||||||
let worktree_store = project.read(cx).worktree_store();
|
let worktree_store = project.read(cx).worktree_store();
|
||||||
let project_skills_task = if skills_enabled {
|
let project_skills_task = {
|
||||||
let project_skills_futures: Vec<
|
let project_skills_futures: Vec<
|
||||||
futures::future::BoxFuture<'static, Vec<Result<Skill, SkillLoadError>>>,
|
futures::future::BoxFuture<'static, Vec<Result<Skill, SkillLoadError>>>,
|
||||||
> = worktrees
|
> = worktrees
|
||||||
|
|
@ -897,8 +886,6 @@ impl NativeAgent {
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
cx.background_spawn(async move { future::join_all(project_skills_futures).await })
|
cx.background_spawn(async move { future::join_all(project_skills_futures).await })
|
||||||
} else {
|
|
||||||
Task::ready(Vec::new())
|
|
||||||
};
|
};
|
||||||
let default_user_rules_task = if let Some(prompt_store) = prompt_store.as_ref() {
|
let default_user_rules_task = if let Some(prompt_store) = prompt_store.as_ref() {
|
||||||
prompt_store.read_with(cx, |prompt_store, cx| {
|
prompt_store.read_with(cx, |prompt_store, cx| {
|
||||||
|
|
@ -1107,7 +1094,7 @@ impl NativeAgent {
|
||||||
&mut self,
|
&mut self,
|
||||||
project: Entity<Project>,
|
project: Entity<Project>,
|
||||||
event: &project::Event,
|
event: &project::Event,
|
||||||
cx: &mut Context<Self>,
|
_cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
let project_id = project.entity_id();
|
let project_id = project.entity_id();
|
||||||
let Some(state) = self.projects.get_mut(&project_id) else {
|
let Some(state) = self.projects.get_mut(&project_id) else {
|
||||||
|
|
@ -1118,16 +1105,14 @@ impl NativeAgent {
|
||||||
state.project_context_needs_refresh.send(()).ok();
|
state.project_context_needs_refresh.send(()).ok();
|
||||||
}
|
}
|
||||||
project::Event::WorktreeUpdatedEntries(_, items) => {
|
project::Event::WorktreeUpdatedEntries(_, items) => {
|
||||||
let skills_enabled = cx.has_flag::<SkillsFeatureFlag>();
|
|
||||||
if items.iter().any(|(path, _, _)| {
|
if items.iter().any(|(path, _, _)| {
|
||||||
let path_ref = path.as_ref();
|
let path_ref = path.as_ref();
|
||||||
RULES_FILE_REL_PATHS
|
RULES_FILE_REL_PATHS
|
||||||
.iter()
|
.iter()
|
||||||
.any(|rules_path| path_ref == rules_path.as_ref())
|
.any(|rules_path| path_ref == rules_path.as_ref())
|
||||||
|| (skills_enabled
|
|| SKILLS_PREFIX
|
||||||
&& SKILLS_PREFIX
|
.as_ref()
|
||||||
.as_ref()
|
.is_some_and(|prefix| path_ref.starts_with(prefix))
|
||||||
.is_some_and(|prefix| path_ref.starts_with(prefix)))
|
|
||||||
}) {
|
}) {
|
||||||
state.project_context_needs_refresh.send(()).ok();
|
state.project_context_needs_refresh.send(()).ok();
|
||||||
}
|
}
|
||||||
|
|
@ -3583,9 +3568,6 @@ mod internal_tests {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_global_skills_load_and_reload(cx: &mut TestAppContext) {
|
async fn test_global_skills_load_and_reload(cx: &mut TestAppContext) {
|
||||||
init_test(cx);
|
init_test(cx);
|
||||||
cx.update(|cx| {
|
|
||||||
cx.update_flags(true, vec!["skills".to_string()]);
|
|
||||||
});
|
|
||||||
let fs = FakeFs::new(cx.executor());
|
let fs = FakeFs::new(cx.executor());
|
||||||
let skills_dir = global_skills_dir();
|
let skills_dir = global_skills_dir();
|
||||||
let initial_skill_dir = skills_dir.join("my-skill");
|
let initial_skill_dir = skills_dir.join("my-skill");
|
||||||
|
|
@ -3651,9 +3633,6 @@ mod internal_tests {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_global_skills_dir_created_after_startup(cx: &mut TestAppContext) {
|
async fn test_global_skills_dir_created_after_startup(cx: &mut TestAppContext) {
|
||||||
init_test(cx);
|
init_test(cx);
|
||||||
cx.update(|cx| {
|
|
||||||
cx.update_flags(true, vec!["skills".to_string()]);
|
|
||||||
});
|
|
||||||
let fs = FakeFs::new(cx.executor());
|
let fs = FakeFs::new(cx.executor());
|
||||||
let skills_dir = global_skills_dir();
|
let skills_dir = global_skills_dir();
|
||||||
|
|
||||||
|
|
@ -3735,9 +3714,6 @@ mod internal_tests {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_skills_added_after_session_visible_to_skill_tool(cx: &mut TestAppContext) {
|
async fn test_skills_added_after_session_visible_to_skill_tool(cx: &mut TestAppContext) {
|
||||||
init_test(cx);
|
init_test(cx);
|
||||||
cx.update(|cx| {
|
|
||||||
cx.update_flags(true, vec!["skills".to_string()]);
|
|
||||||
});
|
|
||||||
let fs = FakeFs::new(cx.executor());
|
let fs = FakeFs::new(cx.executor());
|
||||||
let skills_dir = global_skills_dir();
|
let skills_dir = global_skills_dir();
|
||||||
|
|
||||||
|
|
@ -3879,9 +3855,6 @@ mod internal_tests {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_subagent_skills_lookup_matches_parent(cx: &mut TestAppContext) {
|
async fn test_subagent_skills_lookup_matches_parent(cx: &mut TestAppContext) {
|
||||||
init_test(cx);
|
init_test(cx);
|
||||||
cx.update(|cx| {
|
|
||||||
cx.update_flags(true, vec!["skills".to_string()]);
|
|
||||||
});
|
|
||||||
let fs = FakeFs::new(cx.executor());
|
let fs = FakeFs::new(cx.executor());
|
||||||
let skills_dir = global_skills_dir();
|
let skills_dir = global_skills_dir();
|
||||||
let skill_dir = skills_dir.join("shared-skill");
|
let skill_dir = skills_dir.join("shared-skill");
|
||||||
|
|
@ -3982,9 +3955,6 @@ mod internal_tests {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_skills_appear_as_available_skills(cx: &mut TestAppContext) {
|
async fn test_skills_appear_as_available_skills(cx: &mut TestAppContext) {
|
||||||
init_test(cx);
|
init_test(cx);
|
||||||
cx.update(|cx| {
|
|
||||||
cx.update_flags(true, vec!["skills".to_string()]);
|
|
||||||
});
|
|
||||||
let fs = FakeFs::new(cx.executor());
|
let fs = FakeFs::new(cx.executor());
|
||||||
let skills_dir = global_skills_dir();
|
let skills_dir = global_skills_dir();
|
||||||
|
|
||||||
|
|
@ -4087,7 +4057,6 @@ mod internal_tests {
|
||||||
|
|
||||||
init_test(cx);
|
init_test(cx);
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
cx.update_flags(true, vec!["skills".to_string()]);
|
|
||||||
// The trust global isn't created by `init_test`. We need it
|
// The trust global isn't created by `init_test`. We need it
|
||||||
// for `Project::test_with_worktree_trust` to actually wire up
|
// for `Project::test_with_worktree_trust` to actually wire up
|
||||||
// trust tracking and for our subscription in
|
// trust tracking and for our subscription in
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ use collections::HashMap;
|
||||||
use editor::{Editor, MultiBuffer};
|
use editor::{Editor, MultiBuffer};
|
||||||
use extension::ExtensionEvents;
|
use extension::ExtensionEvents;
|
||||||
use extension_host::ExtensionStore;
|
use extension_host::ExtensionStore;
|
||||||
use feature_flags::{FeatureFlagAppExt as _, SkillsFeatureFlag};
|
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
Action, Anchor, Animation, AnimationExt, AnyElement, App, AsyncWindowContext, ClipboardItem,
|
Action, Anchor, Animation, AnimationExt, AnyElement, App, AsyncWindowContext, ClipboardItem,
|
||||||
|
|
@ -4714,12 +4714,6 @@ impl AgentPanel {
|
||||||
.with_handle(self.agent_panel_menu_handle.clone())
|
.with_handle(self.agent_panel_menu_handle.clone())
|
||||||
.menu({
|
.menu({
|
||||||
move |window, cx| {
|
move |window, cx| {
|
||||||
// When the Skills feature flag is on, hide the legacy Rules menu entry.
|
|
||||||
// The flag is read from a global store populated asynchronously, and
|
|
||||||
// this menu builder runs on every open, so the latest resolved value is
|
|
||||||
// reflected when the user clicks the ellipsis.
|
|
||||||
let skills_enabled = cx.has_flag::<SkillsFeatureFlag>();
|
|
||||||
|
|
||||||
Some(ContextMenu::build(window, cx, |mut menu, _window, _| {
|
Some(ContextMenu::build(window, cx, |mut menu, _window, _| {
|
||||||
menu = menu.context(focus_handle.clone());
|
menu = menu.context(focus_handle.clone());
|
||||||
|
|
||||||
|
|
@ -4756,10 +4750,6 @@ impl AgentPanel {
|
||||||
.action("Add Custom Server…", Box::new(AddContextServer))
|
.action("Add Custom Server…", Box::new(AddContextServer))
|
||||||
.separator();
|
.separator();
|
||||||
|
|
||||||
if !skills_enabled {
|
|
||||||
menu = menu.action("Rules", Box::new(OpenRulesLibrary::default()));
|
|
||||||
}
|
|
||||||
|
|
||||||
menu = menu.action("Profiles", Box::new(ManageProfiles::default()));
|
menu = menu.action("Profiles", Box::new(ManageProfiles::default()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ use ::ui::IconName;
|
||||||
use agent_client_protocol::schema as acp;
|
use agent_client_protocol::schema as acp;
|
||||||
use agent_settings::{AgentProfileId, AgentSettings};
|
use agent_settings::{AgentProfileId, AgentSettings};
|
||||||
use command_palette_hooks::CommandPaletteFilter;
|
use command_palette_hooks::CommandPaletteFilter;
|
||||||
use feature_flags::{FeatureFlagAppExt as _, SkillsFeatureFlag};
|
use feature_flags::FeatureFlagAppExt as _;
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
Action, App, Context, Entity, ImageSource, Resource, SharedString, SharedUri, Window, actions,
|
Action, App, Context, Entity, ImageSource, Resource, SharedString, SharedUri, Window, actions,
|
||||||
|
|
@ -560,21 +560,7 @@ pub fn init(
|
||||||
_: &zed_actions::agent::OpenRulesToSkillsMigrationInfo,
|
_: &zed_actions::agent::OpenRulesToSkillsMigrationInfo,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Workspace>| {
|
cx: &mut Context<Workspace>| {
|
||||||
// The banner is the only intended entry point and is
|
crate::ui::RulesToSkillsModal::toggle(workspace, window, cx);
|
||||||
// gated on the skills flag, but dispatch from the
|
|
||||||
// command palette or a keybind is still possible — only
|
|
||||||
// open the explainer if the flag is enabled so it never
|
|
||||||
// surfaces outside its intended audience.
|
|
||||||
//
|
|
||||||
// Race note: `has_flag` returns false before server
|
|
||||||
// flags are received, so a dispatch during that brief
|
|
||||||
// window is a no-op even for users who genuinely have
|
|
||||||
// the flag. The banner itself has the same race — it
|
|
||||||
// stays hidden until flags arrive — so a user who can
|
|
||||||
// see the banner has, by definition, already passed it.
|
|
||||||
if cx.has_flag::<SkillsFeatureFlag>() {
|
|
||||||
crate::ui::RulesToSkillsModal::toggle(workspace, window, cx);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|
@ -608,10 +594,16 @@ pub fn init(
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
// Once the `skills` feature flag has resolved, kick off the one-time
|
// Kick off the one-time migration of non-Default Rules to global
|
||||||
// migration of non-Default Rules to global Skills. Idempotent and
|
// Skills, deferred until server feature flags arrive.
|
||||||
// self-gated on the flag, so it's safe to call on every flag-ready
|
//
|
||||||
// notification (and a no-op for users without the flag).
|
// The migration itself is idempotent and no longer gated on a flag,
|
||||||
|
// but the deferral via `on_flags_ready` still matters: the migration
|
||||||
|
// writes to the on-disk `GlobalKeyValueStore`, which dispatches work
|
||||||
|
// on the `sqlezWorker` background thread. In `gpui::test` contexts,
|
||||||
|
// server flags are never received, so this callback never fires —
|
||||||
|
// which keeps that sqlite worker activity from racing with the
|
||||||
|
// `TestScheduler` and tripping its non-determinism panic.
|
||||||
{
|
{
|
||||||
let fs = fs.clone();
|
let fs = fs.clone();
|
||||||
cx.on_flags_ready(move |_, cx| {
|
cx.on_flags_ready(move |_, cx| {
|
||||||
|
|
@ -653,12 +645,6 @@ fn update_command_palette_filter(cx: &mut App) {
|
||||||
.edit_predictions
|
.edit_predictions
|
||||||
.provider;
|
.provider;
|
||||||
|
|
||||||
// The Skills feature flag is loaded asynchronously, so this value may
|
|
||||||
// be `false` before flags resolve. `update_command_palette_filter`
|
|
||||||
// gets re-run from `cx.on_flags_ready` (see `init`), which means the
|
|
||||||
// filter is reapplied with the correct value once flags arrive.
|
|
||||||
let skills_enabled = cx.has_flag::<SkillsFeatureFlag>();
|
|
||||||
|
|
||||||
CommandPaletteFilter::update_global(cx, |filter, _| {
|
CommandPaletteFilter::update_global(cx, |filter, _| {
|
||||||
use editor::actions::{
|
use editor::actions::{
|
||||||
AcceptEditPrediction, AcceptNextLineEditPrediction, AcceptNextWordEditPrediction,
|
AcceptEditPrediction, AcceptNextLineEditPrediction, AcceptNextWordEditPrediction,
|
||||||
|
|
@ -726,12 +712,12 @@ fn update_command_palette_filter(cx: &mut App) {
|
||||||
filter.show_namespace("multi_workspace");
|
filter.show_namespace("multi_workspace");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hide `assistant: open rules library` when Skills are enabled —
|
// Hide `assistant: open rules library` — Rules are surfaced
|
||||||
// Rules are surfaced through the Skills UI in that case. Applied
|
// through the Skills UI now. Applied after the disable-ai /
|
||||||
// after the disable-ai / agent-enabled branches so it overrides
|
// agent-enabled branches so it overrides the
|
||||||
// the `show_namespace("assistant")` call above without affecting
|
// `show_namespace("assistant")` call above without affecting the
|
||||||
// the rest of that namespace's actions.
|
// rest of that namespace's actions.
|
||||||
if !disable_ai && skills_enabled {
|
if !disable_ai {
|
||||||
filter.hide_action_types(&open_rules_library_action);
|
filter.hide_action_types(&open_rules_library_action);
|
||||||
} else {
|
} else {
|
||||||
filter.show_action_types(open_rules_library_action.iter());
|
filter.show_action_types(open_rules_library_action.iter());
|
||||||
|
|
|
||||||
|
|
@ -123,15 +123,3 @@ impl FeatureFlag for AutoWatchFeatureFlag {
|
||||||
type Value = PresenceFlag;
|
type Value = PresenceFlag;
|
||||||
}
|
}
|
||||||
register_feature_flag!(AutoWatchFeatureFlag);
|
register_feature_flag!(AutoWatchFeatureFlag);
|
||||||
|
|
||||||
pub struct SkillsFeatureFlag;
|
|
||||||
|
|
||||||
impl FeatureFlag for SkillsFeatureFlag {
|
|
||||||
const NAME: &'static str = "skills";
|
|
||||||
type Value = PresenceFlag;
|
|
||||||
|
|
||||||
fn enabled_for_staff() -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
register_feature_flag!(SkillsFeatureFlag);
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ assets.workspace = true
|
||||||
chrono.workspace = true
|
chrono.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
db.workspace = true
|
db.workspace = true
|
||||||
feature_flags.workspace = true
|
|
||||||
fs.workspace = true
|
fs.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
fuzzy.workspace = true
|
fuzzy.workspace = true
|
||||||
|
|
|
||||||
|
|
@ -24,14 +24,10 @@
|
||||||
//! (still using Zed's shipped default content) are skipped so we don't
|
//! (still using Zed's shipped default content) are skipped so we don't
|
||||||
//! pollute AGENTS.md with text the user never wrote.
|
//! pollute AGENTS.md with text the user never wrote.
|
||||||
//!
|
//!
|
||||||
//! Both migrations are gated by:
|
//! Both migrations are gated by a single global "migration already ran"
|
||||||
//!
|
//! flag persisted in [`GlobalKeyValueStore`] — keyed by
|
||||||
//! * the `skills` feature flag — users without it never have their Rules
|
//! [`MIGRATION_DONE_KEY`], so a shared home directory only gets
|
||||||
//! touched in any way;
|
//! populated once per machine even across release channels.
|
||||||
//! * a single global "migration already ran" flag persisted in
|
|
||||||
//! [`GlobalKeyValueStore`] — keyed by [`MIGRATION_DONE_KEY`], so a
|
|
||||||
//! shared home directory only gets populated once per machine even
|
|
||||||
//! across release channels.
|
|
||||||
//!
|
//!
|
||||||
//! The migration is intentionally non-destructive: rule rows in the LMDB
|
//! The migration is intentionally non-destructive: rule rows in the LMDB
|
||||||
//! database are left in place after the migration. That way users can
|
//! database are left in place after the migration. That way users can
|
||||||
|
|
@ -45,7 +41,6 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use agent_skills::{SKILL_FILE_NAME, global_skills_dir, slugify_skill_name};
|
use agent_skills::{SKILL_FILE_NAME, global_skills_dir, slugify_skill_name};
|
||||||
use anyhow::{Context as _, Result};
|
use anyhow::{Context as _, Result};
|
||||||
use db::kvp::GlobalKeyValueStore;
|
use db::kvp::GlobalKeyValueStore;
|
||||||
use feature_flags::{FeatureFlagAppExt as _, SkillsFeatureFlag};
|
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use gpui::{App, AsyncApp, Entity, TaskExt as _};
|
use gpui::{App, AsyncApp, Entity, TaskExt as _};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
@ -151,13 +146,9 @@ static MIGRATION_TASK_SPAWNED: AtomicBool = AtomicBool::new(false);
|
||||||
/// Migrate non-Default user rules to global Skills, if not already done.
|
/// Migrate non-Default user rules to global Skills, if not already done.
|
||||||
///
|
///
|
||||||
/// Safe to call on every startup — short-circuits immediately when the
|
/// Safe to call on every startup — short-circuits immediately when the
|
||||||
/// migration has already run, when another invocation in this process
|
/// migration has already run or when another invocation in this process
|
||||||
/// has already started it, or when the user doesn't have the `skills`
|
/// has already started it.
|
||||||
/// feature flag enabled.
|
|
||||||
pub fn migrate_rules_to_skills_if_needed(fs: Arc<dyn Fs>, cx: &mut App) {
|
pub fn migrate_rules_to_skills_if_needed(fs: Arc<dyn Fs>, cx: &mut App) {
|
||||||
if !cx.has_flag::<SkillsFeatureFlag>() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if migration_done() {
|
if migration_done() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ chrono.workspace = true
|
||||||
client.workspace = true
|
client.workspace = true
|
||||||
cloud_api_types.workspace = true
|
cloud_api_types.workspace = true
|
||||||
db.workspace = true
|
db.workspace = true
|
||||||
feature_flags.workspace = true
|
|
||||||
git_ui.workspace = true
|
git_ui.workspace = true
|
||||||
gpui = { workspace = true, features = ["screen-capture"] }
|
gpui = { workspace = true, features = ["screen-capture"] }
|
||||||
icons.workspace = true
|
icons.workspace = true
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ use auto_update::AutoUpdateStatus;
|
||||||
use call::ActiveCall;
|
use call::ActiveCall;
|
||||||
use client::{Client, UserStore, zed_urls};
|
use client::{Client, UserStore, zed_urls};
|
||||||
use cloud_api_types::Plan;
|
use cloud_api_types::Plan;
|
||||||
use feature_flags::{FeatureFlagAppExt as _, SkillsFeatureFlag};
|
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
Action, Anchor, Animation, AnimationExt, AnyElement, App, Context, Element, Entity, Focusable,
|
Action, Anchor, Animation, AnimationExt, AnyElement, App, Context, Element, Entity, Focusable,
|
||||||
|
|
@ -469,7 +468,6 @@ impl TitleBar {
|
||||||
zed_actions::agent::OpenRulesToSkillsMigrationInfo.boxed_clone(),
|
zed_actions::agent::OpenRulesToSkillsMigrationInfo.boxed_clone(),
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
.visible_when(|cx| cx.has_flag::<SkillsFeatureFlag>())
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue