diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 9ee6e2fd..ffc78e66 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -1618,6 +1618,36 @@ pub fn ensure_state_dir(subdir: &str) -> Result { Ok(dir) } +/// Resolve a project-local state subdirectory, preferring `.codewhale/` +/// when it exists, falling back to `.deepseek/` for legacy projects. +/// +/// Returns `(true, path)` when the primary `.codewhale/` path is used, +/// `(false, path)` for the legacy fallback. The boolean helps callers +/// emit a deprecation notice on legacy paths. +pub fn resolve_project_state_dir( + workspace: &Path, + subdir: &str, +) -> (bool, PathBuf) { + let primary = workspace.join(CODEWHALE_APP_DIR).join(subdir); + if primary.exists() { + return (true, primary); + } + let legacy = workspace.join(LEGACY_APP_DIR).join(subdir); + (false, legacy) +} + +/// Ensure a project-local state subdirectory exists under `.codewhale/`, +/// creating it if necessary. Returns the directory path. +pub fn ensure_project_state_dir( + workspace: &Path, + subdir: &str, +) -> Result { + let dir = workspace.join(CODEWHALE_APP_DIR).join(subdir); + std::fs::create_dir_all(&dir) + .with_context(|| format!("failed to create {}/", dir.display()))?; + Ok(dir) +} + pub fn resolve_config_path(explicit: Option) -> Result { let path = if let Some(path) = explicit { path diff --git a/crates/tui/src/skill_state.rs b/crates/tui/src/skill_state.rs index 4816fa8e..245b51f3 100644 --- a/crates/tui/src/skill_state.rs +++ b/crates/tui/src/skill_state.rs @@ -5,7 +5,7 @@ //! filesystem-discovered `SkillRegistry`: the registry tells us which skills //! exist on disk, and this store tells API clients which ones are marked active. //! -//! Storage shape (TOML at `~/.deepseek/skills_state.toml`): +//! Storage shape (TOML at `~/.codewhale/skills_state.toml`, legacy `~/.deepseek/skills_state.toml`): //! //! ```toml //! disabled = ["skill-name-1", "skill-name-2"] @@ -104,10 +104,8 @@ impl SkillStateStore { } fn default_state_path() -> Result { - let home = dirs::home_dir().context("could not resolve $HOME for ~/.deepseek")?; - let dir = home.join(".deepseek"); - fs::create_dir_all(&dir) - .with_context(|| format!("create deepseek state dir at {}", dir.display()))?; + let dir = codewhale_config::ensure_state_dir(".") + .context("could not resolve or create CodeWhale state directory")?; Ok(dir.join(STATE_FILE_NAME)) } diff --git a/crates/tui/src/snapshot/paths.rs b/crates/tui/src/snapshot/paths.rs index 90d70091..d1ac8c78 100644 --- a/crates/tui/src/snapshot/paths.rs +++ b/crates/tui/src/snapshot/paths.rs @@ -1,18 +1,20 @@ //! Path resolution for the per-workspace snapshot side-repos. //! -//! Snapshots live in `~/.deepseek/snapshots///`. -//! The two-level hash split lets us snapshot multiple worktrees of the same -//! project independently — `git worktree list` users won't get cross-talk -//! between feature branches. +//! Snapshots live under the resolved state directory +//! (`~/.codewhale/snapshots` or legacy `~/.deepseek/snapshots`) with +//! a two-level hash split so we can snapshot multiple worktrees of the +//! same project independently — `git worktree list` users won't get +//! cross-talk between feature branches. use std::io; use std::path::{Path, PathBuf}; /// Compute the snapshot directory for a given workspace path. /// -/// Returns `~/.deepseek/snapshots///`. The -/// caller is responsible for creating it on disk; we purposefully don't -/// touch the filesystem here so this is cheap to call repeatedly. +/// Returns `$STATE_DIR/snapshots///` where +/// `$STATE_DIR` is resolved via `codewhale_config::resolve_state_dir`. +/// The caller is responsible for creating it on disk; we purposefully +/// don't touch the filesystem here so this is cheap to call repeatedly. /// /// The `project_hash` is derived from the canonicalized workspace path /// after stripping any `.worktrees/` suffix — multiple worktrees @@ -24,7 +26,7 @@ pub fn snapshot_dir_for(workspace: &Path) -> PathBuf { } /// Same as [`snapshot_dir_for`] but with an injectable home directory. -/// Used by tests so we never touch the user's real `~/.deepseek/`. +/// Used by tests so they never touch the user's real state directory. pub fn snapshot_dir_with_home(workspace: &Path, home: Option) -> PathBuf { let home = home.unwrap_or_else(|| PathBuf::from(".")); let canonical = workspace @@ -33,12 +35,21 @@ pub fn snapshot_dir_with_home(workspace: &Path, home: Option) -> PathBu let project_root = strip_worktree_suffix(&canonical); let project_hash = stable_hex(&project_root); let worktree_hash = stable_hex(&canonical); - home.join(".deepseek") - .join("snapshots") + snapshot_base_with_home(Some(home)) .join(project_hash) .join(worktree_hash) } +fn snapshot_base_with_home(home: Option) -> PathBuf { + let home = home.unwrap_or_else(|| PathBuf::from(".")); + // Prefer .codewhale, fall back to .deepseek + let primary = home.join(".codewhale").join("snapshots"); + if primary.exists() { + return primary; + } + home.join(".deepseek").join("snapshots") +} + /// Resolve the `.git` directory inside the snapshot dir. pub fn snapshot_git_dir(workspace: &Path) -> PathBuf { snapshot_dir_for(workspace).join(".git")