diff --git a/app/resources/tab_configs/default_worktree.toml b/app/resources/tab_configs/default_worktree.toml index 7cb5f4be..b5da261c 100644 --- a/app/resources/tab_configs/default_worktree.toml +++ b/app/resources/tab_configs/default_worktree.toml @@ -1,9 +1,9 @@ # Default worktree tab config -# Stored in ~/.warp/default_tab_configs/ — you can customize this file! +# Stored in ~/.warp-core/default_tab_configs/ — you can customize this file! # Any customizations will apply for any new worktree configs going forward that are created from the "New worktree config" flow # # This config is used by the "Worktree in" submenu to quickly create -# worktrees under ~/.warp/worktrees// for your +# worktrees under ~/.warp-core/worktrees// for your # known repositories. name = "Worktree" diff --git a/app/src/ai/attachment_utils.rs b/app/src/ai/attachment_utils.rs index d45428c7..f2f956de 100644 --- a/app/src/ai/attachment_utils.rs +++ b/app/src/ai/attachment_utils.rs @@ -12,7 +12,7 @@ use crate::ai::agent::AIAgentAttachment; /// based on the agent's working directory. #[cfg_attr(target_family = "wasm", allow(dead_code))] pub(crate) fn attachments_download_dir(working_dir: &Path) -> PathBuf { - working_dir.join(".warp").join("attachments") + working_dir.join(".warp-core").join("attachments") } /// Extracts the filename component from a path, stripping any directory prefixes. diff --git a/app/src/ai/blocklist/controller.rs b/app/src/ai/blocklist/controller.rs index 75809b95..5ddd07ac 100644 --- a/app/src/ai/blocklist/controller.rs +++ b/app/src/ai/blocklist/controller.rs @@ -307,7 +307,7 @@ pub struct BlocklistAIController { ambient_agent_task_id: Option, /// Per-session directory for downloading file attachments. - /// Set by the agent driver based on the workspace directory (e.g. `{working_dir}/.warp/attachments`). + /// Set by the agent driver based on the workspace directory (e.g. `{working_dir}/.warp-core/attachments`). attachments_download_dir: Option, /// Pending auto-resume tasks that are waiting for network connectivity. diff --git a/app/src/ai/mcp/file_based_manager.rs b/app/src/ai/mcp/file_based_manager.rs index 9181444a..56ce2fb1 100644 --- a/app/src/ai/mcp/file_based_manager.rs +++ b/app/src/ai/mcp/file_based_manager.rs @@ -235,7 +235,7 @@ impl FileBasedMCPManager { /// config location. /// /// "Global" means the installation was detected outside of a user repository: - /// - For `MCPProvider::Warp`: `warp_data_dir()` (i.e. `~/.warp/.mcp.json`). + /// - For `MCPProvider::Warp`: `warp_data_dir()` (i.e. `~/.warp-core/.mcp.json`). /// - For any other provider: the user's home directory (e.g. `~/.claude.json`). /// /// Project-scoped installations (those detected inside a repo) are not considered @@ -409,11 +409,11 @@ impl FileBasedMCPManager { /// when its config does not specify `working_directory`. /// /// The spawn root is the directory the config was discovered in, with one - /// exception: global Warp installs are discovered in `~/.warp/` (Warp's data + /// exception: global Warp installs are discovered in `~/.warp-core/` (Warp's data /// dir) which isn't a useful cwd for spawned processes, so they are remapped /// to the home directory instead. /// - Project-scoped installations: the repo root. - /// - Global installations (`~/.warp/.mcp.json`, `~/.claude.json`, etc.): the + /// - Global installations (`~/.warp-core/.mcp.json`, `~/.claude.json`, etc.): the /// home directory. /// /// If the installation is referenced from multiple roots, the lexicographically @@ -429,7 +429,7 @@ impl FileBasedMCPManager { .sorted() .next()?; - // Global Warp installs live under `~/.warp/`, which is internal Warp state + // Global Warp installs live under `~/.warp-core/`, which is internal Warp state // rather than a meaningful working directory. Map them to the home dir so // all global installs (Warp and third-party) share a consistent cwd. if discovery_root == warp_data_dir() { diff --git a/app/src/ai/mcp/file_mcp_watcher.rs b/app/src/ai/mcp/file_mcp_watcher.rs index 003188d5..d8c9daef 100644 --- a/app/src/ai/mcp/file_mcp_watcher.rs +++ b/app/src/ai/mcp/file_mcp_watcher.rs @@ -29,13 +29,13 @@ static ENV_VAR_REGEX: LazyLock = LazyLock::new(|| Regex::new(r"\$\{([^}]+)\}").expect("Regex is valid")); /// Matches home config paths that are exactly one directory deep (e.g. `.codex/config.toml`, -/// `.warp/.mcp.json`), capturing the parent directory component. +/// `.warp-core/.mcp.json`), capturing the parent directory component. static HOME_SUBDIR_REGEX: LazyLock = LazyLock::new(|| Regex::new(r"^([^/]+)/[^/]+$").expect("Regex is valid")); /// Returns the subdirectory under the home directory that needs its own [`DirectoryWatcher`], /// inferred from the provider's home config path. Matches paths that are exactly one directory -/// deep (e.g. `.codex/config.toml` → `.codex`, `.warp/.mcp.json` → `.warp`). Returns `None` +/// deep (e.g. `.codex/config.toml` → `.codex`, `.warp-core/.mcp.json` → `.warp-core`). Returns `None` /// when the config file lives directly in the home dir (e.g. `.claude.json`) and is already /// covered by `HomeDirectoryWatcher`. fn home_subdir_to_watch(provider: MCPProvider) -> Option { diff --git a/app/src/ai/mcp/mod.rs b/app/src/ai/mcp/mod.rs index 74580795..872a7284 100644 --- a/app/src/ai/mcp/mod.rs +++ b/app/src/ai/mcp/mod.rs @@ -92,7 +92,7 @@ impl MCPProvider { /// Returns the path of the provider's config file relative to the home directory. pub fn home_config_path(&self) -> &'static Path { match self { - MCPProvider::Warp => Path::new(".warp/.mcp.json"), + MCPProvider::Warp => Path::new(".warp-core/.mcp.json"), MCPProvider::Claude => Path::new(".claude.json"), MCPProvider::Codex => Path::new(".codex/config.toml"), MCPProvider::Agents => Path::new(".agents/.mcp.json"), @@ -102,7 +102,7 @@ impl MCPProvider { /// Returns the path of the provider's config file relative to a project root. pub fn project_config_path(&self) -> &'static Path { match self { - MCPProvider::Warp => Path::new(".warp/.mcp.json"), + MCPProvider::Warp => Path::new(".warp-core/.mcp.json"), MCPProvider::Claude => Path::new(".mcp.json"), MCPProvider::Codex => Path::new(".codex/config.toml"), MCPProvider::Agents => Path::new(".agents/.mcp.json"), diff --git a/app/src/ai/mcp/templatable_manager/native.rs b/app/src/ai/mcp/templatable_manager/native.rs index 10d034ef..65becf9a 100644 --- a/app/src/ai/mcp/templatable_manager/native.rs +++ b/app/src/ai/mcp/templatable_manager/native.rs @@ -785,7 +785,7 @@ impl TemplatableMCPServerManager { // For file-based MCP installations without an explicit `working_directory`, // default the spawn cwd to the directory the config was discovered in - // (repo root for project-scoped configs, ~/.warp/ or ~ for globals). This + // (repo root for project-scoped configs, ~/.warp-core/ or ~ for globals). This // matches user expectations for repo-relative commands in `.mcp.json`. // Cloud-templated installations (lookup returns None) are unaffected and // continue to inherit Warp's process cwd. diff --git a/app/src/ai/skills/file_watchers/utils_tests.rs b/app/src/ai/skills/file_watchers/utils_tests.rs index 0efbeca2..ac34dc4d 100644 --- a/app/src/ai/skills/file_watchers/utils_tests.rs +++ b/app/src/ai/skills/file_watchers/utils_tests.rs @@ -197,7 +197,7 @@ fn extract_skill_parent_directory_different_providers() { return; }; let repo = home_dir.join("repo"); - let providers = [".warp", ".claude", ".codex", ".cursor", ".gemini"]; + let providers = [".warp-core", ".claude", ".codex", ".cursor", ".gemini"]; for provider in providers { let path = repo .join(provider) diff --git a/app/src/ai/skills/resolve_skill_spec_tests.rs b/app/src/ai/skills/resolve_skill_spec_tests.rs index 40bda3e3..ef881ade 100644 --- a/app/src/ai/skills/resolve_skill_spec_tests.rs +++ b/app/src/ai/skills/resolve_skill_spec_tests.rs @@ -21,7 +21,7 @@ fn write_skill_file(path: &Path, name: &str, description: &str, body: &str) -> R #[test] fn resolve_from_skill_dirs_by_directory_scan_resolves_home_skill_dir() -> Result<()> { let temp_dir = tempfile::TempDir::new().context("Failed to create temp dir")?; - let skill_dir = temp_dir.path().join(".warp").join("skills"); + let skill_dir = temp_dir.path().join(".warp-core").join("skills"); let skill_path = skill_dir.join("my-skill").join("SKILL.md"); write_skill_file( diff --git a/app/src/ai/skills/skill_utils.rs b/app/src/ai/skills/skill_utils.rs index 6ec646f9..f4777721 100644 --- a/app/src/ai/skills/skill_utils.rs +++ b/app/src/ai/skills/skill_utils.rs @@ -60,7 +60,7 @@ fn try_insert_skill( /// /// Two skills are considered duplicates only when they share the same owning directory /// **and** identical content — which is the common case when a tool like `npx skills` -/// symlinks the same skill under `~/.agents/skills/`, `~/.warp/skills/`, `~/.claude/skills/`, etc. +/// symlinks the same skill under `~/.agents/skills/`, `~/.warp-core/skills/`, `~/.claude/skills/`, etc. /// /// Each element of `skill_paths` is a `(dir_path, skill_file_path)` tuple where /// `dir_path` is the directory that owns the skill. diff --git a/app/src/plugin/host/native/mod.rs b/app/src/plugin/host/native/mod.rs index 8d11181e..96b7b959 100644 --- a/app/src/plugin/host/native/mod.rs +++ b/app/src/plugin/host/native/mod.rs @@ -93,9 +93,9 @@ pub fn run() -> Result<()> { /// Returns a vector of validated plugin directory paths in the plugins directory. /// -/// This assumes that all plugins are located in ~/.warp/plugins. +/// This assumes that all plugins are located in ~/.warp-core/plugins. fn plugin_paths() -> Vec { - const PLUGIN_PATH_SUFFIX: &str = ".warp/plugins"; + const PLUGIN_PATH_SUFFIX: &str = ".warp-core/plugins"; dirs::home_dir() .map(|home_dir| home_dir.join(PLUGIN_PATH_SUFFIX)) diff --git a/app/src/preview_config_migration.rs b/app/src/preview_config_migration.rs index def15b7d..547b315c 100644 --- a/app/src/preview_config_migration.rs +++ b/app/src/preview_config_migration.rs @@ -1,9 +1,9 @@ //! One-time migration that gives the Preview channel its own config -//! directory (`~/.warp-preview`) on macOS. +//! directory (`~/.warp-core-preview`) on macOS. //! -//! Historically, Stable and Preview shared `~/.warp` on macOS. To give +//! Historically, Stable and Preview shared `~/.warp-core` on macOS. To give //! Preview its own directory without breaking existing users, this migration -//! symlinks each top-level entry from `~/.warp` into `~/.warp-preview` on +//! symlinks each top-level entry from `~/.warp-core` into `~/.warp-core-preview` on //! first launch, so existing configuration (keybindings, themes, workflows, //! etc.) remains available to Preview. //! diff --git a/app/src/preview_config_migration_tests.rs b/app/src/preview_config_migration_tests.rs index 6cf92e84..521eae4f 100644 --- a/app/src/preview_config_migration_tests.rs +++ b/app/src/preview_config_migration_tests.rs @@ -13,8 +13,8 @@ fn is_symlink_to(link: &Path, expected_target: &Path) -> bool { #[test] fn creates_symlinks_for_top_level_entries() { let tmp = tempfile::tempdir().unwrap(); - let old_dir = tmp.path().join(".warp"); - let new_dir = tmp.path().join(".warp-preview"); + let old_dir = tmp.path().join(".warp-core"); + let new_dir = tmp.path().join(".warp-core-preview"); fs::create_dir(&old_dir).unwrap(); fs::write(old_dir.join("keybindings.yaml"), "bindings").unwrap(); @@ -42,8 +42,8 @@ fn creates_symlinks_for_top_level_entries() { #[test] fn skips_when_new_dir_already_exists() { let tmp = tempfile::tempdir().unwrap(); - let old_dir = tmp.path().join(".warp"); - let new_dir = tmp.path().join(".warp-preview"); + let old_dir = tmp.path().join(".warp-core"); + let new_dir = tmp.path().join(".warp-core-preview"); fs::create_dir(&old_dir).unwrap(); fs::write(old_dir.join("keybindings.yaml"), "bindings").unwrap(); @@ -58,8 +58,8 @@ fn skips_when_new_dir_already_exists() { #[test] fn skips_when_old_dir_does_not_exist() { let tmp = tempfile::tempdir().unwrap(); - let old_dir = tmp.path().join(".warp"); - let new_dir = tmp.path().join(".warp-preview"); + let old_dir = tmp.path().join(".warp-core"); + let new_dir = tmp.path().join(".warp-core-preview"); // old_dir intentionally not created. migrate_config_dir_via_symlinks(&old_dir, &new_dir); @@ -70,8 +70,8 @@ fn skips_when_old_dir_does_not_exist() { #[test] fn skips_ds_store_and_dot_underscore_files() { let tmp = tempfile::tempdir().unwrap(); - let old_dir = tmp.path().join(".warp"); - let new_dir = tmp.path().join(".warp-preview"); + let old_dir = tmp.path().join(".warp-core"); + let new_dir = tmp.path().join(".warp-core-preview"); fs::create_dir(&old_dir).unwrap(); fs::write(old_dir.join(".DS_Store"), "metadata").unwrap(); @@ -88,8 +88,8 @@ fn skips_ds_store_and_dot_underscore_files() { #[test] fn skips_excluded_files() { let tmp = tempfile::tempdir().unwrap(); - let old_dir = tmp.path().join(".warp"); - let new_dir = tmp.path().join(".warp-preview"); + let old_dir = tmp.path().join(".warp-core"); + let new_dir = tmp.path().join(".warp-core-preview"); fs::create_dir(&old_dir).unwrap(); fs::write(old_dir.join("settings.toml"), "[settings]").unwrap(); @@ -104,8 +104,8 @@ fn skips_excluded_files() { #[test] fn is_idempotent() { let tmp = tempfile::tempdir().unwrap(); - let old_dir = tmp.path().join(".warp"); - let new_dir = tmp.path().join(".warp-preview"); + let old_dir = tmp.path().join(".warp-core"); + let new_dir = tmp.path().join(".warp-core-preview"); fs::create_dir(&old_dir).unwrap(); fs::write(old_dir.join("keybindings.yaml"), "bindings").unwrap(); diff --git a/app/src/settings/ai.rs b/app/src/settings/ai.rs index fb5910a4..5b35893b 100644 --- a/app/src/settings/ai.rs +++ b/app/src/settings/ai.rs @@ -1373,7 +1373,7 @@ define_settings_group!(AISettings, settings: [ } // Whether file-based MCP servers from third-party AI tools (e.g. Claude, Codex) should - // be automatically detected and spawned. Warp-native config files (.warp/.mcp.json) are + // be automatically detected and spawned. Warp-native config files (.warp-core/.mcp.json) are // always detected and spawned, regardless of this setting. file_based_mcp_enabled: FileBasedMcpEnabled { type: bool, diff --git a/app/src/tab_configs/tab_config.rs b/app/src/tab_configs/tab_config.rs index 57ffec97..6cd46a8b 100644 --- a/app/src/tab_configs/tab_config.rs +++ b/app/src/tab_configs/tab_config.rs @@ -135,7 +135,7 @@ pub struct TabConfigPaneNode { // ── TabConfig ─────────────────────────────────────────────────────── -/// A tab config loaded from a `.toml` file in `~/.warp/tab_configs/`. +/// A tab config loaded from a `.toml` file in `~/.warp-core/tab_configs/`. /// /// Pane layout is defined with a flat `[[panes]]` array where the first entry /// is the root and splits reference children by ID. diff --git a/app/src/user_config/mod.rs b/app/src/user_config/mod.rs index 74efc952..e32e187e 100644 --- a/app/src/user_config/mod.rs +++ b/app/src/user_config/mod.rs @@ -218,7 +218,7 @@ pub fn is_tab_config_toml(path: &Path) -> bool { .any(|dir| path.starts_with(dir)) } -/// Ensures `~/.warp/default_tab_configs/worktree.toml` exists, creating it +/// Ensures `~/.warp-core/default_tab_configs/worktree.toml` exists, creating it /// from the embedded template if missing. Returns the path to the file. #[cfg(feature = "local_fs")] pub(crate) fn ensure_default_worktree_config() -> PathBuf { diff --git a/app/src/workflows/local_workflows.rs b/app/src/workflows/local_workflows.rs index 825dc6c1..f8cb69b7 100644 --- a/app/src/workflows/local_workflows.rs +++ b/app/src/workflows/local_workflows.rs @@ -82,7 +82,7 @@ impl LocalWorkflows { }) } - /// Returns an iterator over file-based workflows loaded from the `.warp/workflows` directory in + /// Returns an iterator over file-based workflows loaded from the `.warp-core/workflows` directory in /// the `working_directory`. /// /// The loaded workflows vector is cached. diff --git a/app/src/workspace/view.rs b/app/src/workspace/view.rs index 6af9ef53..dbf0e189 100644 --- a/app/src/workspace/view.rs +++ b/app/src/workspace/view.rs @@ -6348,7 +6348,7 @@ impl Workspace { } } - /// Writes the default tab config template to an unused path in `~/.warp/tab_configs/` + /// Writes the default tab config template to an unused path in `~/.warp-core/tab_configs/` /// and opens it respecting the user's configured editor setting. #[cfg(feature = "local_fs")] fn create_and_open_new_tab_config(&mut self, ctx: &mut ViewContext) { @@ -6387,7 +6387,7 @@ impl Workspace { } /// Snapshots the given tab's pane layout and writes it as a new tab config - /// TOML to `~/.warp/tab_configs/`, then opens the file in the user's editor. + /// TOML to `~/.warp-core/tab_configs/`, then opens the file in the user's editor. #[cfg(feature = "local_fs")] fn save_current_tab_as_new_config(&mut self, tab_index: usize, ctx: &mut ViewContext) { use crate::tab_configs::session_config::{tab_config_from_pane_snapshot, write_tab_config}; @@ -9234,7 +9234,7 @@ impl Workspace { } /// Opens a worktree in the given repo using the default worktree tab config, - /// saving the materialized config to `~/.warp/tab_configs/` first. + /// saving the materialized config to `~/.warp-core/tab_configs/` first. /// The branch name is auto-generated. #[cfg(feature = "local_fs")] fn open_worktree_in_repo(&mut self, repo_path: String, ctx: &mut ViewContext) { diff --git a/crates/ai/src/skills/skill_provider.rs b/crates/ai/src/skills/skill_provider.rs index fff53fb4..7c084148 100644 --- a/crates/ai/src/skills/skill_provider.rs +++ b/crates/ai/src/skills/skill_provider.rs @@ -110,7 +110,7 @@ pub static SKILL_PROVIDER_DEFINITIONS: LazyLock> = }, SkillProviderDefinition { provider: SkillProvider::Warp, - skills_path: PathBuf::from(".warp").join("skills"), + skills_path: PathBuf::from(".warp-core").join("skills"), }, SkillProviderDefinition { provider: SkillProvider::Claude, diff --git a/crates/integration/src/test/preview_config_migration.rs b/crates/integration/src/test/preview_config_migration.rs index 97f5b3e9..379ceefa 100644 --- a/crates/integration/src/test/preview_config_migration.rs +++ b/crates/integration/src/test/preview_config_migration.rs @@ -47,21 +47,20 @@ pub fn test_preview_config_dir_migration() -> Builder { .add_named_assertion( "new directory exists and is a real directory", |_app, _window_id| { - let new_dir = home_dir().join(".warp-preview"); + let new_dir = home_dir().join(".warp-core-preview"); if !new_dir.is_dir() { return AssertionOutcome::failure( - ".warp-preview should be a directory".to_string(), + ".warp-core-preview should be a directory".to_string(), ); } - // It should be a real directory, not a symlink. let is_symlink = new_dir .symlink_metadata() .map(|m| m.file_type().is_symlink()) .unwrap_or(false); if is_symlink { return AssertionOutcome::failure( - ".warp-preview should not itself be a symlink".to_string(), + ".warp-core-preview should not itself be a symlink".to_string(), ); } AssertionOutcome::Success @@ -71,8 +70,8 @@ pub fn test_preview_config_dir_migration() -> Builder { "expected entries are symlinks pointing to old dir", |_app, _window_id| { let home = home_dir(); - let old_dir = home.join(".warp"); - let new_dir = home.join(".warp-preview"); + let old_dir = home.join(".warp-core"); + let new_dir = home.join(".warp-core-preview"); for name in ["keybindings.yaml", "themes", "workflows", ".mcp.json"] { let link = new_dir.join(name); @@ -110,7 +109,7 @@ pub fn test_preview_config_dir_migration() -> Builder { }, ) .add_named_assertion("excluded files were not symlinked", |_app, _window_id| { - let new_dir = home_dir().join(".warp-preview"); + let new_dir = home_dir().join(".warp-core-preview"); for name in [".DS_Store", "._somefile", "settings.toml"] { if new_dir.join(name).exists() { diff --git a/crates/lsp/src/language_server_candidate.rs b/crates/lsp/src/language_server_candidate.rs index f7f3709e..9b168e13 100644 --- a/crates/lsp/src/language_server_candidate.rs +++ b/crates/lsp/src/language_server_candidate.rs @@ -40,7 +40,7 @@ pub trait LanguageServerCandidate: Send + Sync { self.is_installed_on_path(executor).await } - /// Attempts to install the server into the `.warp/` directory. + /// Attempts to install the server into the `.warp-core/` directory. /// /// The executor provides the user's PATH environment variable, which may be needed /// for servers that rely on external tools (e.g. gopls needs `go`). diff --git a/crates/remote_server/src/setup.rs b/crates/remote_server/src/setup.rs index ed3e62db..6a9d624b 100644 --- a/crates/remote_server/src/setup.rs +++ b/crates/remote_server/src/setup.rs @@ -112,22 +112,20 @@ pub fn parse_uname_output(output: &str) -> Result { /// Returns the remote directory where the binary is installed, keyed by channel. /// -/// - stable: `~/.warp/remote-server` -/// - preview: `~/.warp-preview/remote-server` -/// - dev: `~/.warp-dev/remote-server` -/// - local: `~/.warp-local/remote-server` -/// - integration: `~/.warp-dev/remote-server` -/// - warp-oss: `~/.warp-oss/remote-server` +/// - stable: `~/.warp-core/remote-server` +/// - preview: `~/.warp-core-preview/remote-server` +/// - dev: `~/.warp-core-dev/remote-server` +/// - local: `~/.warp-core-local/remote-server` +/// - integration: `~/.warp-core-dev/remote-server` +/// - warp-core-oss: `~/.warp-core-oss/remote-server` pub fn remote_server_dir() -> String { let warp_dir = match ChannelState::channel() { - Channel::Stable => ".warp", - Channel::Preview => ".warp-preview", - Channel::Dev | Channel::Integration => ".warp-dev", - Channel::Local => ".warp-local", + Channel::Stable => ".warp-core", + Channel::Preview => ".warp-core-preview", + Channel::Dev | Channel::Integration => ".warp-core-dev", + Channel::Local => ".warp-core-local", Channel::Oss => { - // TODO(alokedesai): need to figure out how remote server works with warp-oss - // For now, return what Dev returns. - ".warp-dev" + ".warp-core-dev" } }; format!("~/{warp_dir}/remote-server") diff --git a/crates/warp_cli/src/agent.rs b/crates/warp_cli/src/agent.rs index 647ba763..13e521c2 100644 --- a/crates/warp_cli/src/agent.rs +++ b/crates/warp_cli/src/agent.rs @@ -224,7 +224,7 @@ pub struct RunAgentArgs { /// /// Format: `skill_name`, `repo:skill_name`, or `org/repo:skill_name` /// - /// Skills are searched in `.agents/skills/`, `.warp/skills/`, `.claude/skills/`, and `.codex/skills/` directories. + /// Skills are searched in `.agents/skills/`, `.warp-core/skills/`, `.claude/skills/`, and `.codex/skills/` directories. /// If a repo is specified, searches only that repo. If org is also specified, /// validates the repo's git remote matches the expected org. /// @@ -357,7 +357,7 @@ pub struct RunCloudArgs { /// /// Format: `skill_name`, `repo:skill_name`, or `org/repo:skill_name` /// - /// Skills are searched in `.agents/skills/`, `.warp/skills/`, `.claude/skills/`, and `.codex/skills/` directories. + /// Skills are searched in `.agents/skills/`, `.warp-core/skills/`, `.claude/skills/`, and `.codex/skills/` directories. /// If a repo is specified, searches only that repo. If org is also specified, /// validates the repo's git remote matches the expected org. /// diff --git a/crates/warp_cli/src/schedule.rs b/crates/warp_cli/src/schedule.rs index ec11e4a3..076a870b 100644 --- a/crates/warp_cli/src/schedule.rs +++ b/crates/warp_cli/src/schedule.rs @@ -111,7 +111,7 @@ pub struct CreateScheduleArgs { /// /// Format: `repo:skill_name` or `org/repo:skill_name` /// - /// Skills are searched in `.agents/skills/`, `.warp/skills/`, `.claude/skills/`, and `.codex/skills/` directories. + /// Skills are searched in `.agents/skills/`, `.warp-core/skills/`, `.claude/skills/`, and `.codex/skills/` directories. /// The skill is resolved at runtime in the agent's cloud environment. /// /// When used with --prompt, the skill provides the base context and the prompt is the user task. @@ -186,7 +186,7 @@ pub struct UpdateScheduleArgs { /// /// Format: `skill_name`, `repo:skill_name`, or `org/repo:skill_name` /// - /// Skills are searched in `.agents/skills/`, `.warp/skills/`, `.claude/skills/`, and `.codex/skills/` directories. + /// Skills are searched in `.agents/skills/`, `.warp-core/skills/`, `.claude/skills/`, and `.codex/skills/` directories. /// The skill is resolved at runtime in the agent's cloud environment. #[arg(long = "skill", value_name = "SPEC", conflicts_with = "remove_skill")] pub skill: Option, diff --git a/crates/warp_cli/src/skill.rs b/crates/warp_cli/src/skill.rs index 2ffb5807..03126093 100644 --- a/crates/warp_cli/src/skill.rs +++ b/crates/warp_cli/src/skill.rs @@ -4,7 +4,7 @@ use std::{fmt, str::FromStr}; /// A skill specifier that can reference a skill in a specific repo or search the current directory. /// /// The skill identifier (after the optional `repo:` or `org/repo:` prefix) can be either: -/// - A **simple skill name** - searched across skill directories with precedence (`.agents/skills/`, `.warp/skills/`, `.claude/skills/`, `.codex/skills/`) +/// - A **simple skill name** - searched across skill directories with precedence (`.agents/skills/`, `.warp-core/skills/`, `.claude/skills/`, `.codex/skills/`) /// - A **full path to SKILL.md** - resolved directly without precedence /// /// # Formats @@ -19,7 +19,7 @@ use std::{fmt, str::FromStr}; /// /// Simple skill names (searched with directory precedence): /// ```ignore -/// code-review // searches .agents/skills/, .warp/skills/, .claude/skills/, .codex/skills/ +/// code-review // searches .agents/skills/, .warp-core/skills/, .claude/skills/, .codex/skills/ /// warp-internal:code-review // searches in "warp-internal" repo /// warpdotdev/warp-internal:code-review // searches in specific org/repo /// ``` @@ -38,7 +38,7 @@ pub struct SkillSpec { pub repo: Option, /// The skill identifier - either a simple name or a full path to SKILL.md. /// - /// - **Simple name** (e.g., `"code-review"`): Searched across `.agents/skills/`, `.warp/skills/`, `.claude/skills/`, `.codex/skills/` + /// - **Simple name** (e.g., `"code-review"`): Searched across `.agents/skills/`, `.warp-core/skills/`, `.claude/skills/`, `.codex/skills/` /// in precedence order. The name is used to construct paths like `.claude/skills/code-review/SKILL.md`. /// /// - **Full path** (e.g., `".claude/skills/code-review/SKILL.md"`): Resolved directly without precedence. @@ -87,7 +87,7 @@ impl SkillSpec { /// - `deploy` /// /// Full paths are resolved directly, while simple names are searched across - /// skill directories in precedence order (`.agents/skills/`, `.warp/skills/`, `.claude/skills/`, `.codex/skills/`). + /// skill directories in precedence order (`.agents/skills/`, `.warp-core/skills/`, `.claude/skills/`, `.codex/skills/`). /// /// Uses cross-platform path semantics via [`std::path::Path`]. pub fn is_full_path(&self) -> bool { diff --git a/crates/warp_core/src/paths.rs b/crates/warp_core/src/paths.rs index c2f928b2..a410d086 100644 --- a/crates/warp_core/src/paths.rs +++ b/crates/warp_core/src/paths.rs @@ -24,11 +24,15 @@ use crate::{ AppId, }; -/// The name of the directory in which to put non-global Warp-specific files. +/// The name of the directory in which to put non-global Warp Core-specific files. /// /// This should be used, for example, as the base directory under which -/// repository workflows would be stored (in "./.warp/workflows"). -pub const WARP_CONFIG_DIR: &str = ".warp"; +/// repository workflows would be stored (in "./.warp-core/workflows"). +pub const WARP_CONFIG_DIR: &str = ".warp-core"; + +/// The legacy config directory name used by Warp before the rename to Warp Core. +/// Used for auto-migration on first launch. +pub const LEGACY_WARP_CONFIG_DIR: &str = ".warp"; /// The name of the folder that stores Warp execution logs and network logs. /// This is currently only used on Windows to maintain backwards compatibility. @@ -59,15 +63,142 @@ pub fn warp_home_config_dir_name() -> String { } } -/// Returns the home-relative Warp config directory for the current channel and data profile. +/// Returns the home-relative Warp Core config directory for the current channel and data profile. /// /// Unlike [`data_dir`] and [`config_local_dir`] on non-macOS platforms, this intentionally keeps -/// Warp-authored, user-facing config under a `.warp*` directory in the home directory instead of +/// user-facing config under a `.warp-core*` directory in the home directory instead of /// using the platform XDG/AppData project directories. pub fn warp_home_config_dir() -> Option { dirs::home_dir().map(|home_dir| home_dir.join(warp_home_config_dir_name())) } +/// Returns the legacy `~/.warp*` config directory path for the current channel, +/// used to detect and migrate data from a previous Warp installation. +pub fn legacy_warp_home_config_dir() -> Option { + let base = LEGACY_WARP_CONFIG_DIR; + let dir_name = match ChannelState::channel() { + Channel::Stable | Channel::Preview => base.to_owned(), + Channel::Oss => format!("{base}-oss"), + Channel::Dev => format!("{base}-dev"), + Channel::Integration => format!("{base}-integration"), + Channel::Local => format!("{base}-local"), + }; + let dir_name = if let Some(data_profile) = ChannelState::data_profile() { + format!("{dir_name}-{data_profile}") + } else { + dir_name + }; + dirs::home_dir().map(|home_dir| home_dir.join(dir_name)) +} + +/// Migrates the legacy `~/.warp*` config directory to `~/.warp-core*` if needed. +/// +/// This runs once on first launch after the rename. It creates symlinks from the +/// old directory entries into the new directory, preserving access to existing +/// configuration (keybindings, themes, workflows, skills, etc.). +/// +/// This is a no-op if: +/// - The new directory already exists. +/// - The old directory does not exist. +pub fn migrate_legacy_config_dir_if_needed() { + let Some(old_dir) = legacy_warp_home_config_dir() else { + return; + }; + let Some(new_dir) = warp_home_config_dir() else { + return; + }; + + if new_dir.exists() || !old_dir.exists() { + return; + } + + #[cfg(unix)] + { + use std::os::unix::fs::symlink; + + if let Err(err) = std::fs::create_dir(&new_dir) { + if err.kind() != std::io::ErrorKind::AlreadyExists { + log::warn!( + "Failed to create config directory {}: {err}", + new_dir.display() + ); + return; + } + } + + let entries = match std::fs::read_dir(&old_dir) { + Ok(entries) => entries, + Err(err) => { + log::warn!( + "Failed to read legacy config directory {}: {err}", + old_dir.display() + ); + return; + } + }; + + let mut migrated = 0u32; + for entry in entries.flatten() { + let name = entry.file_name(); + let name_str = name.to_string_lossy(); + + if name_str == ".DS_Store" || name_str.starts_with("._") { + continue; + } + + let target = old_dir.join(&name); + let link = new_dir.join(&name); + + if let Err(err) = symlink(&target, &link) { + log::warn!( + "Failed to symlink {} -> {}: {err}", + link.display(), + target.display() + ); + } else { + migrated += 1; + } + } + + log::info!( + "Migrated legacy config directory: created {migrated} symlinks in {}", + new_dir.display() + ); + } + + #[cfg(windows)] + { + fn copy_dir_recursive(src: &Path, dst: &Path) -> std::io::Result<()> { + std::fs::create_dir_all(dst)?; + for entry in std::fs::read_dir(src)? { + let entry = entry?; + let ty = entry.file_type()?; + let dest = dst.join(entry.file_name()); + if ty.is_dir() { + copy_dir_recursive(&entry.path(), &dest)?; + } else { + std::fs::copy(entry.path(), dest)?; + } + } + Ok(()) + } + + if let Err(err) = copy_dir_recursive(&old_dir, &new_dir) { + log::warn!( + "Failed to copy legacy config directory {} to {}: {err}", + old_dir.display(), + new_dir.display() + ); + } else { + log::info!( + "Migrated legacy config directory {} to {}", + old_dir.display(), + new_dir.display() + ); + } + } +} + pub fn warp_home_skills_dir() -> Option { warp_home_config_dir().map(|warp_config_dir| warp_config_dir.join("skills")) } @@ -78,8 +209,8 @@ pub fn warp_home_mcp_config_file_path() -> Option { /// Returns the macOS config directory name for the current channel. /// -/// Stable uses `.warp`, while other channels include a channel suffix -/// (e.g., `.warp-dev`, `.warp-local`). +/// Stable uses `.warp-core`, while other channels include a channel suffix +/// (e.g., `.warp-core-dev`, `.warp-core-local`). /// /// These suffixes are persisted on disk as directory names and must not be /// changed once established, or existing user data will be orphaned. @@ -275,7 +406,7 @@ pub fn app_group_container_path() -> Option { let fm = NSFileManager::defaultManager(); // Keep in sync with Entitlements.plist - let group_id = format!("{}.dev.warp", crate::macos::APPLE_TEAM_ID); + let group_id = format!("{}.dev.warpcore", crate::macos::APPLE_TEAM_ID); let group_id = NSString::from_str(&group_id); // containerURLForSecurityApplicationGroupIdentifier always returns a value on macOS (unlike iOS). // We have to double-check that the path points to a directory we can actually use. In addition to diff --git a/crates/warp_core/src/paths_tests.rs b/crates/warp_core/src/paths_tests.rs index 91b445b6..174b9745 100644 --- a/crates/warp_core/src/paths_tests.rs +++ b/crates/warp_core/src/paths_tests.rs @@ -8,7 +8,7 @@ fn test_data_dir_path() { // ChannelState, by default, is configured for Channel::Oss. cfg_if::cfg_if! { if #[cfg(target_os = "macos")] { - assert_eq!(data_dir(), home_dir.join(".warp-oss")); + assert_eq!(data_dir(), home_dir.join(".warp-core-oss")); } else if #[cfg(target_os = "linux")] { assert_eq!(data_dir(), home_dir.join(".local/share/warp-oss")); } else if #[cfg(windows)] { @@ -25,7 +25,7 @@ fn test_config_local_dir_path() { // ChannelState, by default, is configured for Channel::Oss. cfg_if::cfg_if! { if #[cfg(target_os = "macos")] { - assert_eq!(config_local_dir(), home_dir.join(".warp-oss")); + assert_eq!(config_local_dir(), home_dir.join(".warp-core-oss")); } else if #[cfg(target_os = "linux")] { assert_eq!(config_local_dir(), home_dir.join(".config/warp-oss")); } else if #[cfg(windows)] { @@ -40,8 +40,8 @@ fn test_config_local_dir_path() { fn test_warp_home_config_dir_path() { let home_dir = home_dir().expect("Should be able to compute home directory"); let expected_dir_name = match ChannelState::data_profile() { - Some(data_profile) => format!(".warp-oss-{data_profile}"), - None => ".warp-oss".to_string(), + Some(data_profile) => format!(".warp-core-oss-{data_profile}"), + None => ".warp-core-oss".to_string(), }; assert_eq!( diff --git a/crates/warp_features/src/lib.rs b/crates/warp_features/src/lib.rs index cfc8ed09..57717e77 100644 --- a/crates/warp_features/src/lib.rs +++ b/crates/warp_features/src/lib.rs @@ -654,7 +654,7 @@ pub enum FeatureFlag { /// Enables platform skills support (--skill flag) for agent runs. /// - /// Skills are loaded from `.agents/skills/`, `.warp/skills/`, `.claude/skills/`, and `.codex/skills/` + /// Skills are loaded from `.agents/skills/`, `.warp-core/skills/`, `.claude/skills/`, and `.codex/skills/` /// directories to provide base prompts for agent runs. OzPlatformSkills, /// Enables Oz identity federation commands.