History

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. ---------

Richard Feldman committed 5 months ago

Commit 2e700591 parent 952119d9 files · +43 −123Browse files

Unified · Split

9 files
  1. Cargo.lock +0 −2
  2. crates/agent/src/agent.rs +16 −47
  3. crates/agent_ui/src/agent_panel.rs +1 −11
  4. crates/agent_ui/src/agent_ui.rs +18 −32
  5. crates/feature_flags/src/flags.rs +0 −12
  6. crates/prompt_store/Cargo.toml +1 −1
  7. crates/prompt_store/src/rules_to_skills_migration.rs +6 −15
  8. crates/title_bar/Cargo.toml +1 −1
  9. crates/title_bar/src/title_bar.rs +0 −2
Cargo.lock+0−2
@@ -13688,7 +13688,6 @@ dependencies = [ · 13687 unmodified lines
13688 "chrono",13688 "chrono",
13689 "collections",13689 "collections",
13690 "db",13690 "db",
13691 "feature_flags",
13692 "fs",13691 "fs",
13693 "futures 0.3.32",13692 "futures 0.3.32",
13694 "fuzzy",13693 "fuzzy",
@@ -18251,7 +18250,6 @@ dependencies = [ · 4556 unmodified lines
18251 "client",18250 "client",
18252 "cloud_api_types",18251 "cloud_api_types",
18253 "db",18252 "db",
18254 "feature_flags",
18255 "fs",18253 "fs",
18256 "git_ui",18254 "git_ui",
18257 "gpui",18255 "gpui",
crates/agent/src/agent.rs+16−47
@@ -38,7 +38,7 @@ use agent_skills::{ · 37 unmodified lines
38use anyhow::{Context as _, Result, anyhow};38use anyhow::{Context as _, Result, anyhow};
39use chrono::{DateTime, Utc};39use chrono::{DateTime, Utc};
40use collections::{HashMap, HashSet, IndexMap};40use collections::{HashMap, HashSet, IndexMap};
41use feature_flags::{FeatureFlagAppExt as _, SkillsFeatureFlag};41
42use fs::Fs;42use fs::Fs;
43use futures::channel::{mpsc, oneshot};43use futures::channel::{mpsc, oneshot};
44use futures::future::Shared;44use futures::future::Shared;
@@ -392,11 +392,11 @@ impl NativeAgent { · 347 unmodified lines
392 /// Kicks off a one-time scan of the global skills directory if one392 /// Kicks off a one-time scan of the global skills directory if one
393 /// isn't already in progress and a watch isn't already active.393 /// isn't already in progress and a watch isn't already active.
394 ///394 ///
395 /// Idempotent and cheap: returns immediately if the user lacks the395 /// Idempotent and cheap: returns immediately if a scan or watch is
396 /// skills feature flag, or if a scan or watch is already running.396 /// already running. The expected callers are user-interaction events
397 /// The expected callers are user-interaction events from the agent397 /// from the agent panel (input focus, slash autocomplete, conversation
398 /// panel (input focus, slash autocomplete, conversation submit);398 /// submit); firing this from any of them is equivalent and safe to
399 /// firing this from any of them is equivalent and safe to repeat.399 /// repeat.
400 ///400 ///
401 /// The scan itself runs detached on the foreground executor. If401 /// The scan itself runs detached on the foreground executor. If
402 /// `~/.agents/skills/` exists it transitions state to402 /// `~/.agents/skills/` exists it transitions state to
@@ -405,9 +405,6 @@ impl NativeAgent { · 2 unmodified lines
405 /// next trigger retries (covering the case where the user creates405 /// next trigger retries (covering the case where the user creates
406 /// the directory after the first scan).406 /// the directory after the first scan).
407 pub fn ensure_skills_scan_started(&mut self, cx: &mut Context<Self>) {407 pub fn ensure_skills_scan_started(&mut self, cx: &mut Context<Self>) {
408 if !cx.has_flag::<SkillsFeatureFlag>() {
409 return;
410 }
411 if !matches!(self.skills_state, SkillsState::Idle) {408 if !matches!(self.skills_state, SkillsState::Idle) {
412 return;409 return;
413 }410 }
@@ -598,12 +595,10 @@ impl NativeAgent { · 184 unmodified lines
598 // after the thread is constructed are still visible to the595 // after the thread is constructed are still visible to the
599 // model — without this, the catalog and tool would drift out596 // model — without this, the catalog and tool would drift out
600 // of sync until the session was reopened.597 // of sync until the session was reopened.
601 if cx.has_flag::<SkillsFeatureFlag>() {598 thread.add_tool(SkillTool::new(
602 thread.add_tool(SkillTool::new(599 skills_resolver_for_project(weak.clone(), project_id),
603 skills_resolver_for_project(weak.clone(), project_id),600 self.fs.clone(),
604 self.fs.clone(),601 ));
605 ));
606 }
607 });602 });
608 603
609 let subscriptions = vec![604 let subscriptions = vec![
@@ -822,12 +817,8 @@ impl NativeAgent { · 212 unmodified lines
822 })817 })
823 .collect::<Vec<_>>();818 .collect::<Vec<_>>();
824 819
825 // Skills are gated behind the "skills" feature flag. Without it we
826 // skip all on-disk lookups so users see no behavior change.
827 let skills_enabled = cx.has_flag::<SkillsFeatureFlag>();
828
829 // Load global skills820 // Load global skills
830 let global_skills_task = if skills_enabled {821 let global_skills_task = {
831 let global_skills_dir = global_skills_dir();822 let global_skills_dir = global_skills_dir();
832 let global_skills_fs = fs.clone();823 let global_skills_fs = fs.clone();
833 cx.background_spawn(async move {824 cx.background_spawn(async move {
@@ -838,8 +829,6 @@ impl NativeAgent { · 4 unmodified lines
838 )829 )
839 .await830 .await
840 })831 })
841 } else {
842 Task::ready(Vec::new())
843 };832 };
844 833
845 // Load project-local skills, but only from worktrees the user has834 // Load project-local skills, but only from worktrees the user has
@@ -852,7 +841,7 @@ impl NativeAgent { · 6 unmodified lines
852 // worktrees pick up their skills without restarting.841 // worktrees pick up their skills without restarting.
853 let trusted_worktrees = TrustedWorktrees::try_get_global(cx);842 let trusted_worktrees = TrustedWorktrees::try_get_global(cx);
854 let worktree_store = project.read(cx).worktree_store();843 let worktree_store = project.read(cx).worktree_store();
855 let project_skills_task = if skills_enabled {844 let project_skills_task = {
856 let project_skills_futures: Vec<845 let project_skills_futures: Vec<
857 futures::future::BoxFuture<'static, Vec<Result<Skill, SkillLoadError>>>,846 futures::future::BoxFuture<'static, Vec<Result<Skill, SkillLoadError>>>,
858 > = worktrees847 > = worktrees
@@ -897,8 +886,6 @@ impl NativeAgent { · 38 unmodified lines
897 })886 })
898 .collect();887 .collect();
899 cx.background_spawn(async move { future::join_all(project_skills_futures).await })888 cx.background_spawn(async move { future::join_all(project_skills_futures).await })
900 } else {
901 Task::ready(Vec::new())
902 };889 };
903 let default_user_rules_task = if let Some(prompt_store) = prompt_store.as_ref() {890 let default_user_rules_task = if let Some(prompt_store) = prompt_store.as_ref() {
904 prompt_store.read_with(cx, |prompt_store, cx| {891 prompt_store.read_with(cx, |prompt_store, cx| {
@@ -1107,7 +1094,7 @@ impl NativeAgent { · 202 unmodified lines
1107 &mut self,1094 &mut self,
1108 project: Entity<Project>,1095 project: Entity<Project>,
1109 event: &project::Event,1096 event: &project::Event,
1110 cx: &mut Context<Self>,1097 _cx: &mut Context<Self>,
1111 ) {1098 ) {
1112 let project_id = project.entity_id();1099 let project_id = project.entity_id();
1113 let Some(state) = self.projects.get_mut(&project_id) else {1100 let Some(state) = self.projects.get_mut(&project_id) else {
@@ -1118,16 +1105,14 @@ impl NativeAgent { · 4 unmodified lines
1118 state.project_context_needs_refresh.send(()).ok();1105 state.project_context_needs_refresh.send(()).ok();
1119 }1106 }
1120 project::Event::WorktreeUpdatedEntries(_, items) => {1107 project::Event::WorktreeUpdatedEntries(_, items) => {
1121 let skills_enabled = cx.has_flag::<SkillsFeatureFlag>();
1122 if items.iter().any(|(path, _, _)| {1108 if items.iter().any(|(path, _, _)| {
1123 let path_ref = path.as_ref();1109 let path_ref = path.as_ref();
1124 RULES_FILE_REL_PATHS1110 RULES_FILE_REL_PATHS
1125 .iter()1111 .iter()
1126 .any(|rules_path| path_ref == rules_path.as_ref())1112 .any(|rules_path| path_ref == rules_path.as_ref())
1127 || (skills_enabled1113 || SKILLS_PREFIX
1128 && SKILLS_PREFIX1114 .as_ref()
1129 .as_ref()1115 .is_some_and(|prefix| path_ref.starts_with(prefix))
1130 .is_some_and(|prefix| path_ref.starts_with(prefix)))
1131 }) {1116 }) {
1132 state.project_context_needs_refresh.send(()).ok();1117 state.project_context_needs_refresh.send(()).ok();
1133 }1118 }
@@ -3583,9 +3568,6 @@ mod internal_tests { · 2449 unmodified lines
3583 #[gpui::test]3568 #[gpui::test]
3584 async fn test_global_skills_load_and_reload(cx: &mut TestAppContext) {3569 async fn test_global_skills_load_and_reload(cx: &mut TestAppContext) {
3585 init_test(cx);3570 init_test(cx);
3586 cx.update(|cx| {
3587 cx.update_flags(true, vec!["skills".to_string()]);
3588 });
3589 let fs = FakeFs::new(cx.executor());3571 let fs = FakeFs::new(cx.executor());
3590 let skills_dir = global_skills_dir();3572 let skills_dir = global_skills_dir();
3591 let initial_skill_dir = skills_dir.join("my-skill");3573 let initial_skill_dir = skills_dir.join("my-skill");
@@ -3651,9 +3633,6 @@ mod internal_tests { · 59 unmodified lines
3651 #[gpui::test]3633 #[gpui::test]
3652 async fn test_global_skills_dir_created_after_startup(cx: &mut TestAppContext) {3634 async fn test_global_skills_dir_created_after_startup(cx: &mut TestAppContext) {
3653 init_test(cx);3635 init_test(cx);
3654 cx.update(|cx| {
3655 cx.update_flags(true, vec!["skills".to_string()]);
3656 });
3657 let fs = FakeFs::new(cx.executor());3636 let fs = FakeFs::new(cx.executor());
3658 let skills_dir = global_skills_dir();3637 let skills_dir = global_skills_dir();
3659 3638
@@ -3735,9 +3714,6 @@ mod internal_tests { · 75 unmodified lines
3735 #[gpui::test]3714 #[gpui::test]
3736 async fn test_skills_added_after_session_visible_to_skill_tool(cx: &mut TestAppContext) {3715 async fn test_skills_added_after_session_visible_to_skill_tool(cx: &mut TestAppContext) {
3737 init_test(cx);3716 init_test(cx);
3738 cx.update(|cx| {
3739 cx.update_flags(true, vec!["skills".to_string()]);
3740 });
3741 let fs = FakeFs::new(cx.executor());3717 let fs = FakeFs::new(cx.executor());
3742 let skills_dir = global_skills_dir();3718 let skills_dir = global_skills_dir();
3743 3719
@@ -3879,9 +3855,6 @@ mod internal_tests { · 135 unmodified lines
3879 #[gpui::test]3855 #[gpui::test]
3880 async fn test_subagent_skills_lookup_matches_parent(cx: &mut TestAppContext) {3856 async fn test_subagent_skills_lookup_matches_parent(cx: &mut TestAppContext) {
3881 init_test(cx);3857 init_test(cx);
3882 cx.update(|cx| {
3883 cx.update_flags(true, vec!["skills".to_string()]);
3884 });
3885 let fs = FakeFs::new(cx.executor());3858 let fs = FakeFs::new(cx.executor());
3886 let skills_dir = global_skills_dir();3859 let skills_dir = global_skills_dir();
3887 let skill_dir = skills_dir.join("shared-skill");3860 let skill_dir = skills_dir.join("shared-skill");
@@ -3982,9 +3955,6 @@ mod internal_tests { · 94 unmodified lines
3982 #[gpui::test]3955 #[gpui::test]
3983 async fn test_skills_appear_as_available_skills(cx: &mut TestAppContext) {3956 async fn test_skills_appear_as_available_skills(cx: &mut TestAppContext) {
3984 init_test(cx);3957 init_test(cx);
3985 cx.update(|cx| {
3986 cx.update_flags(true, vec!["skills".to_string()]);
3987 });
3988 let fs = FakeFs::new(cx.executor());3958 let fs = FakeFs::new(cx.executor());
3989 let skills_dir = global_skills_dir();3959 let skills_dir = global_skills_dir();
3990 3960
@@ -4087,7 +4057,6 @@ mod internal_tests { · 96 unmodified lines
4087 4057
4088 init_test(cx);4058 init_test(cx);
4089 cx.update(|cx| {4059 cx.update(|cx| {
4090 cx.update_flags(true, vec!["skills".to_string()]);
4091 // The trust global isn't created by `init_test`. We need it4060 // The trust global isn't created by `init_test`. We need it
4092 // for `Project::test_with_worktree_trust` to actually wire up4061 // for `Project::test_with_worktree_trust` to actually wire up
4093 // trust tracking and for our subscription in4062 // trust tracking and for our subscription in
crates/agent_ui/src/agent_panel.rs+1−11
@@ -61,7 +61,7 @@ use collections::HashMap; · 60 unmodified lines
61use editor::{Editor, MultiBuffer};61use editor::{Editor, MultiBuffer};
62use extension::ExtensionEvents;62use extension::ExtensionEvents;
63use extension_host::ExtensionStore;63use extension_host::ExtensionStore;
64use feature_flags::{FeatureFlagAppExt as _, SkillsFeatureFlag};64
65use fs::Fs;65use fs::Fs;
66use gpui::{66use gpui::{
67 Action, Anchor, Animation, AnimationExt, AnyElement, App, AsyncWindowContext, ClipboardItem,67 Action, Anchor, Animation, AnimationExt, AnyElement, App, AsyncWindowContext, ClipboardItem,
@@ -4714,12 +4714,6 @@ impl AgentPanel { · 4646 unmodified lines
4714 .with_handle(self.agent_panel_menu_handle.clone())4714 .with_handle(self.agent_panel_menu_handle.clone())
4715 .menu({4715 .menu({
4716 move |window, cx| {4716 move |window, cx| {
4717 // When the Skills feature flag is on, hide the legacy Rules menu entry.
4718 // The flag is read from a global store populated asynchronously, and
4719 // this menu builder runs on every open, so the latest resolved value is
4720 // reflected when the user clicks the ellipsis.
4721 let skills_enabled = cx.has_flag::<SkillsFeatureFlag>();
4722
4723 Some(ContextMenu::build(window, cx, |mut menu, _window, _| {4717 Some(ContextMenu::build(window, cx, |mut menu, _window, _| {
4724 menu = menu.context(focus_handle.clone());4718 menu = menu.context(focus_handle.clone());
4725 4719
@@ -4756,10 +4750,6 @@ impl AgentPanel { · 30 unmodified lines
4756 .action("Add Custom Server…", Box::new(AddContextServer))4750 .action("Add Custom Server…", Box::new(AddContextServer))
4757 .separator();4751 .separator();
4758 4752
4759 if !skills_enabled {
4760 menu = menu.action("Rules", Box::new(OpenRulesLibrary::default()));
4761 }
4762
4763 menu = menu.action("Profiles", Box::new(ManageProfiles::default()));4753 menu = menu.action("Profiles", Box::new(ManageProfiles::default()));
4764 }4754 }
4765 4755
crates/agent_ui/src/agent_ui.rs+18−32
@@ -43,7 +43,7 @@ use ::ui::IconName; · 42 unmodified lines
43use agent_client_protocol::schema as acp;43use agent_client_protocol::schema as acp;
44use agent_settings::{AgentProfileId, AgentSettings};44use agent_settings::{AgentProfileId, AgentSettings};
45use command_palette_hooks::CommandPaletteFilter;45use command_palette_hooks::CommandPaletteFilter;
46use feature_flags::{FeatureFlagAppExt as _, SkillsFeatureFlag};46use feature_flags::FeatureFlagAppExt as _;
47use fs::Fs;47use fs::Fs;
48use gpui::{48use gpui::{
49 Action, App, Context, Entity, ImageSource, Resource, SharedString, SharedUri, Window, actions,49 Action, App, Context, Entity, ImageSource, Resource, SharedString, SharedUri, Window, actions,
@@ -560,21 +560,7 @@ pub fn init( · 510 unmodified lines
560 _: &zed_actions::agent::OpenRulesToSkillsMigrationInfo,560 _: &zed_actions::agent::OpenRulesToSkillsMigrationInfo,
561 window: &mut Window,561 window: &mut Window,
562 cx: &mut Context<Workspace>| {562 cx: &mut Context<Workspace>| {
563 // The banner is the only intended entry point and is563 crate::ui::RulesToSkillsModal::toggle(workspace, window, cx);
564 // gated on the skills flag, but dispatch from the
565 // command palette or a keybind is still possible — only
566 // open the explainer if the flag is enabled so it never
567 // surfaces outside its intended audience.
568 //
569 // Race note: `has_flag` returns false before server
570 // flags are received, so a dispatch during that brief
571 // window is a no-op even for users who genuinely have
572 // the flag. The banner itself has the same race — it
573 // stays hidden until flags arrive — so a user who can
574 // see the banner has, by definition, already passed it.
575 if cx.has_flag::<SkillsFeatureFlag>() {
576 crate::ui::RulesToSkillsModal::toggle(workspace, window, cx);
577 }
578 },564 },
579 );565 );
580 })566 })
@@ -608,10 +594,16 @@ pub fn init( · 27 unmodified lines
608 })594 })
609 .detach();595 .detach();
610 596
611 // Once the `skills` feature flag has resolved, kick off the one-time597 // Kick off the one-time migration of non-Default Rules to global
612 // migration of non-Default Rules to global Skills. Idempotent and598 // Skills, deferred until server feature flags arrive.
613 // self-gated on the flag, so it's safe to call on every flag-ready599 //
614 // notification (and a no-op for users without the flag).600 // The migration itself is idempotent and no longer gated on a flag,
601 // but the deferral via `on_flags_ready` still matters: the migration
602 // writes to the on-disk `GlobalKeyValueStore`, which dispatches work
603 // on the `sqlezWorker` background thread. In `gpui::test` contexts,
604 // server flags are never received, so this callback never fires —
605 // which keeps that sqlite worker activity from racing with the
606 // `TestScheduler` and tripping its non-determinism panic.
615 {607 {
616 let fs = fs.clone();608 let fs = fs.clone();
617 cx.on_flags_ready(move |_, cx| {609 cx.on_flags_ready(move |_, cx| {
@@ -653,12 +645,6 @@ fn update_command_palette_filter(cx: &mut App) { · 35 unmodified lines
653 .edit_predictions645 .edit_predictions
654 .provider;646 .provider;
655 647
656 // The Skills feature flag is loaded asynchronously, so this value may
657 // be `false` before flags resolve. `update_command_palette_filter`
658 // gets re-run from `cx.on_flags_ready` (see `init`), which means the
659 // filter is reapplied with the correct value once flags arrive.
660 let skills_enabled = cx.has_flag::<SkillsFeatureFlag>();
661
662 CommandPaletteFilter::update_global(cx, |filter, _| {648 CommandPaletteFilter::update_global(cx, |filter, _| {
663 use editor::actions::{649 use editor::actions::{
664 AcceptEditPrediction, AcceptNextLineEditPrediction, AcceptNextWordEditPrediction,650 AcceptEditPrediction, AcceptNextLineEditPrediction, AcceptNextWordEditPrediction,
@@ -726,12 +712,12 @@ fn update_command_palette_filter(cx: &mut App) { · 61 unmodified lines
726 filter.show_namespace("multi_workspace");712 filter.show_namespace("multi_workspace");
727 }713 }
728 714
729 // Hide `assistant: open rules library` when Skills are enabled —715 // Hide `assistant: open rules library` — Rules are surfaced
730 // Rules are surfaced through the Skills UI in that case. Applied716 // through the Skills UI now. Applied after the disable-ai /
731 // after the disable-ai / agent-enabled branches so it overrides717 // agent-enabled branches so it overrides the
732 // the `show_namespace("assistant")` call above without affecting718 // `show_namespace("assistant")` call above without affecting the
733 // the rest of that namespace's actions.719 // rest of that namespace's actions.
734 if !disable_ai && skills_enabled {720 if !disable_ai {
735 filter.hide_action_types(&open_rules_library_action);721 filter.hide_action_types(&open_rules_library_action);
736 } else {722 } else {
737 filter.show_action_types(open_rules_library_action.iter());723 filter.show_action_types(open_rules_library_action.iter());
crates/feature_flags/src/flags.rs+0−12
@@ -123,15 +123,3 @@ impl FeatureFlag for AutoWatchFeatureFlag { · 122 unmodified lines
123 type Value = PresenceFlag;123 type Value = PresenceFlag;
124}124}
125register_feature_flag!(AutoWatchFeatureFlag);125register_feature_flag!(AutoWatchFeatureFlag);
126
127pub struct SkillsFeatureFlag;
128
129impl FeatureFlag for SkillsFeatureFlag {
130 const NAME: &'static str = "skills";
131 type Value = PresenceFlag;
132
133 fn enabled_for_staff() -> bool {
134 true
135 }
136}
137register_feature_flag!(SkillsFeatureFlag);
crates/prompt_store/Cargo.toml+1−1
@@ -18,7 +18,7 @@ assets.workspace = true · 17 unmodified lines
18chrono.workspace = true18chrono.workspace = true
19collections.workspace = true19collections.workspace = true
20db.workspace = true20db.workspace = true
21feature_flags.workspace = true21
22fs.workspace = true22fs.workspace = true
23futures.workspace = true23futures.workspace = true
24fuzzy.workspace = true24fuzzy.workspace = true
crates/prompt_store/src/rules_to_skills_migration.rs+6−15
@@ -24,14 +24,10 @@ · 23 unmodified lines
24//! (still using Zed's shipped default content) are skipped so we don't24//! (still using Zed's shipped default content) are skipped so we don't
25//! pollute AGENTS.md with text the user never wrote.25//! pollute AGENTS.md with text the user never wrote.
26//!26//!
27//! Both migrations are gated by:27//! Both migrations are gated by a single global "migration already ran"
28//!28//! flag persisted in [`GlobalKeyValueStore`] — keyed by
29//! * the `skills` feature flag — users without it never have their Rules29//! [`MIGRATION_DONE_KEY`], so a shared home directory only gets
30//! touched in any way;30//! populated once per machine even across release channels.
31//! * a single global "migration already ran" flag persisted in
32//! [`GlobalKeyValueStore`] — keyed by [`MIGRATION_DONE_KEY`], so a
33//! shared home directory only gets populated once per machine even
34//! across release channels.
35//!31//!
36//! The migration is intentionally non-destructive: rule rows in the LMDB32//! The migration is intentionally non-destructive: rule rows in the LMDB
37//! database are left in place after the migration. That way users can33//! database are left in place after the migration. That way users can
@@ -45,7 +41,6 @@ use std::sync::atomic::{AtomicBool, Ordering}; · 7 unmodified lines
45use agent_skills::{SKILL_FILE_NAME, global_skills_dir, slugify_skill_name};41use agent_skills::{SKILL_FILE_NAME, global_skills_dir, slugify_skill_name};
46use anyhow::{Context as _, Result};42use anyhow::{Context as _, Result};
47use db::kvp::GlobalKeyValueStore;43use db::kvp::GlobalKeyValueStore;
48use feature_flags::{FeatureFlagAppExt as _, SkillsFeatureFlag};
49use fs::Fs;44use fs::Fs;
50use gpui::{App, AsyncApp, Entity, TaskExt as _};45use gpui::{App, AsyncApp, Entity, TaskExt as _};
51use serde::{Deserialize, Serialize};46use serde::{Deserialize, Serialize};
@@ -151,13 +146,9 @@ static MIGRATION_TASK_SPAWNED: AtomicBool = AtomicBool::new(false); · 99 unmodified lines
151/// Migrate non-Default user rules to global Skills, if not already done.146/// Migrate non-Default user rules to global Skills, if not already done.
152///147///
153/// Safe to call on every startup — short-circuits immediately when the148/// Safe to call on every startup — short-circuits immediately when the
154/// migration has already run, when another invocation in this process149/// migration has already run or when another invocation in this process
155/// has already started it, or when the user doesn't have the `skills`150/// has already started it.
156/// feature flag enabled.
157pub fn migrate_rules_to_skills_if_needed(fs: Arc<dyn Fs>, cx: &mut App) {151pub fn migrate_rules_to_skills_if_needed(fs: Arc<dyn Fs>, cx: &mut App) {
158 if !cx.has_flag::<SkillsFeatureFlag>() {
159 return;
160 }
161 if migration_done() {152 if migration_done() {
162 return;153 return;
163 }154 }
crates/title_bar/Cargo.toml+1−1
@@ -40,7 +40,7 @@ chrono.workspace = true · 39 unmodified lines
40client.workspace = true40client.workspace = true
41cloud_api_types.workspace = true41cloud_api_types.workspace = true
42db.workspace = true42db.workspace = true
43feature_flags.workspace = true43
44git_ui.workspace = true44git_ui.workspace = true
45gpui = { workspace = true, features = ["screen-capture"] }45gpui = { workspace = true, features = ["screen-capture"] }
46icons.workspace = true46icons.workspace = true
crates/title_bar/src/title_bar.rs+0−2
@@ -25,7 +25,6 @@ use auto_update::AutoUpdateStatus; · 24 unmodified lines
25use call::ActiveCall;25use call::ActiveCall;
26use client::{Client, UserStore, zed_urls};26use client::{Client, UserStore, zed_urls};
27use cloud_api_types::Plan;27use cloud_api_types::Plan;
28use feature_flags::{FeatureFlagAppExt as _, SkillsFeatureFlag};
29 28
30use gpui::{29use gpui::{
31 Action, Anchor, Animation, AnimationExt, AnyElement, App, Context, Element, Entity, Focusable,30 Action, Anchor, Animation, AnimationExt, AnyElement, App, Context, Element, Entity, Focusable,
@@ -469,7 +468,6 @@ impl TitleBar { · 437 unmodified lines
469 zed_actions::agent::OpenRulesToSkillsMigrationInfo.boxed_clone(),468 zed_actions::agent::OpenRulesToSkillsMigrationInfo.boxed_clone(),
470 cx,469 cx,
471 )470 )
472 .visible_when(|cx| cx.has_flag::<SkillsFeatureFlag>())
473 }));471 }));
474 472
475 let mut this = Self {473 let mut this = Self {

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t