fix: use platform-appropriate config paths on Windows (fixes #283)

- Add _get_config_dir() and _get_progress_dir() helpers that return
  %APPDATA%/skill-seekers and %LOCALAPPDATA%/skill-seekers/progress on
  Windows instead of Unix-only ~/.config and ~/.local/share paths
- Recompute paths at instance creation in __init__ so they are always
  evaluated at runtime, not at class definition time
- Guard all chmod() calls with sys.platform != "win32" — chmod with
  Unix stat flags is a no-op on Windows which caused config to appear
  saved but be unreadable/unfindable on subsequent runs
- Fix should_show_welcome() and mark_welcome_shown() to use instance
  config_dir instead of stale class-level WELCOME_FLAG constant

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
yusyus
2026-02-21 23:01:38 +03:00
parent c996e88dac
commit a24ee8dd9d

View File

@@ -14,14 +14,28 @@ from pathlib import Path
from typing import Any
def _get_config_dir() -> Path:
"""Return platform-appropriate config directory."""
if sys.platform == "win32":
return Path(os.environ.get("APPDATA", Path.home())) / "skill-seekers"
return Path.home() / ".config" / "skill-seekers"
def _get_progress_dir() -> Path:
"""Return platform-appropriate progress/data directory."""
if sys.platform == "win32":
return Path(os.environ.get("LOCALAPPDATA", Path.home())) / "skill-seekers" / "progress"
return Path.home() / ".local" / "share" / "skill-seekers" / "progress"
class ConfigManager:
"""Manages Skill Seekers configuration with multi-token support."""
# Default paths
CONFIG_DIR = Path.home() / ".config" / "skill-seekers"
# Default paths (computed at runtime for cross-platform support)
CONFIG_DIR = _get_config_dir()
CONFIG_FILE = CONFIG_DIR / "config.json"
WELCOME_FLAG = CONFIG_DIR / ".welcomed"
PROGRESS_DIR = Path.home() / ".local" / "share" / "skill-seekers" / "progress"
PROGRESS_DIR = _get_progress_dir()
# Default configuration
DEFAULT_CONFIG = {
@@ -44,9 +58,9 @@ class ConfigManager:
def __init__(self):
"""Initialize configuration manager."""
self.config_dir = self.CONFIG_DIR
self.config_file = self.CONFIG_FILE
self.progress_dir = self.PROGRESS_DIR
self.config_dir = _get_config_dir()
self.config_file = self.config_dir / "config.json"
self.progress_dir = _get_progress_dir()
self._ensure_directories()
# Check if config file exists before loading
@@ -62,13 +76,15 @@ class ConfigManager:
# Create main config and progress directories
for directory in [self.config_dir, self.progress_dir]:
directory.mkdir(parents=True, exist_ok=True)
# Set directory permissions to 700 (rwx------)
directory.chmod(stat.S_IRWXU)
# Set directory permissions to 700 (rwx------) - Unix only
if sys.platform != "win32":
directory.chmod(stat.S_IRWXU)
# Also create configs subdirectory for user custom configs
configs_dir = self.config_dir / "configs"
configs_dir.mkdir(exist_ok=True)
configs_dir.chmod(stat.S_IRWXU)
if sys.platform != "win32":
configs_dir.chmod(stat.S_IRWXU)
def _load_config(self) -> dict[str, Any]:
"""Load configuration from file or create default."""
@@ -107,8 +123,9 @@ class ConfigManager:
with open(self.config_file, "w") as f:
json.dump(self.config, f, indent=2)
# Set file permissions to 600 (rw-------)
self.config_file.chmod(stat.S_IRUSR | stat.S_IWUSR)
# Set file permissions to 600 (rw-------) - Unix only
if sys.platform != "win32":
self.config_file.chmod(stat.S_IRUSR | stat.S_IWUSR)
except OSError as e:
print(f"❌ Error saving config: {e}")
@@ -319,8 +336,9 @@ class ConfigManager:
with open(progress_file, "w") as f:
json.dump(progress_data, f, indent=2)
# Set file permissions to 600
progress_file.chmod(stat.S_IRUSR | stat.S_IWUSR)
# Set file permissions to 600 - Unix only
if sys.platform != "win32":
progress_file.chmod(stat.S_IRUSR | stat.S_IWUSR)
def load_progress(self, job_id: str) -> dict[str, Any] | None:
"""Load progress for a job."""
@@ -433,12 +451,14 @@ class ConfigManager:
def should_show_welcome(self) -> bool:
"""Check if we should show welcome message."""
return not self.WELCOME_FLAG.exists()
return not (self.config_dir / ".welcomed").exists()
def mark_welcome_shown(self):
"""Mark welcome message as shown."""
self.WELCOME_FLAG.touch()
self.WELCOME_FLAG.chmod(stat.S_IRUSR | stat.S_IWUSR)
welcome_flag = self.config_dir / ".welcomed"
welcome_flag.touch()
if sys.platform != "win32":
welcome_flag.chmod(stat.S_IRUSR | stat.S_IWUSR)
# Display Helpers