#!/usr/bin/env python3 """ CODITECT Path Import Helper (ADR-114, ADR-120)
Standardized path import helper with graceful fallback. Use this in all hooks and scripts for consistent behavior.
Usage in hooks/scripts: from scripts.core.paths_helper import ( USER_DATA_LOC, CONTEXT_STORAGE, SESSION_LOGS, EXTENSIONS_DIR, get_user_data_dir, get_context_storage_dir, get_extensions_dir )
Created: 2026-01-27 ADR: ADR-120 Customer Extensions Architecture """
import sys from pathlib import Path from typing import Optional
Home directory
HOME = Path.home()
def _find_coditect_root() -> Optional[Path]: """ Find the coditect-core root directory.
Search strategies:
1. Relative to this file (scripts/core/paths_helper.py)
2. Via ~/.coditect symlink
3. Via ~/PROJECTS/.coditect symlink
4. Via protected location (macOS/Linux)
Returns:
Path: Root directory if found, None otherwise
"""
# Strategy 1: Relative to this file
this_file = Path(__file__).resolve()
# paths_helper.py is in scripts/core/, so parent.parent.parent is root
candidate = this_file.parent.parent.parent
if (candidate / "scripts" / "core" / "paths.py").exists():
return candidate
# Strategy 2: Via ~/.coditect symlink
coditect_symlink = HOME / ".coditect"
if coditect_symlink.exists():
resolved = coditect_symlink.resolve()
if (resolved / "scripts" / "core" / "paths.py").exists():
return resolved
# Strategy 3: Via ~/PROJECTS/.coditect symlink
projects_symlink = HOME / "PROJECTS" / ".coditect"
if projects_symlink.exists():
resolved = projects_symlink.resolve()
if (resolved / "scripts" / "core" / "paths.py").exists():
return resolved
# Strategy 4: Platform-specific protected location
if sys.platform == "darwin":
protected = HOME / "Library" / "Application Support" / "CODITECT" / "core"
elif sys.platform == "win32":
import os
local_app_data = os.environ.get("LOCALAPPDATA", str(HOME / "AppData" / "Local"))
protected = Path(local_app_data) / "CODITECT" / "core"
else:
# Linux
import os
xdg_data = os.environ.get("XDG_DATA_HOME", str(HOME / ".local" / "share"))
protected = Path(xdg_data) / "coditect" / "core"
if protected.exists() and (protected / "scripts" / "core" / "paths.py").exists():
return protected
return None
def ensure_paths_available() -> bool: """ Ensure the paths module is available by adding coditect-core to sys.path.
Returns:
bool: True if paths module is available, False otherwise
"""
root = _find_coditect_root()
if root is None:
return False
root_str = str(root)
if root_str not in sys.path:
sys.path.insert(0, root_str)
# Verify import works
try:
from scripts.core import paths
return True
except ImportError:
return False
Auto-run on import
PATHS_AVAILABLE = ensure_paths_available()
Re-export paths module if available, otherwise provide fallbacks
if PATHS_AVAILABLE: from scripts.core.paths import ( # Constants HOME, FRAMEWORK_LOC, PROJECTS_DIR, USER_DATA_LOC, CONTEXT_STORAGE, SESSION_LOGS, MACHINE_ID_FILE, BACKUPS_DIR, ORG_DB, SESSIONS_DB, CONTEXT_DB, # Functions discover_projects_dir, get_user_data_dir, get_framework_dir, get_context_storage_dir, get_session_logs_dir, get_machine_id_path, get_backups_dir, get_org_db_path, get_sessions_db_path, get_context_db_path, get_legacy_paths, check_migration_needed, )
# Add extensions directory (ADR-120)
def get_extensions_dir() -> Path:
"""Get the customer extensions directory (ADR-120)."""
return get_user_data_dir() / "extensions"
EXTENSIONS_DIR = get_extensions_dir()
else: # Hardcoded fallbacks for bootstrapping scenarios # These should only be used when paths.py is not available import warnings warnings.warn( "paths.py not found, using hardcoded fallbacks. " "Run CODITECT-CORE-INITIAL-SETUP.py to fix.", RuntimeWarning )
# Fallback constants
PROJECTS_DIR = HOME / "PROJECTS"
USER_DATA_LOC = PROJECTS_DIR / ".coditect-data"
CONTEXT_STORAGE = USER_DATA_LOC / "context-storage"
SESSION_LOGS = USER_DATA_LOC / "session-logs"
MACHINE_ID_FILE = USER_DATA_LOC / "machine-id.json"
BACKUPS_DIR = USER_DATA_LOC / "backups"
EXTENSIONS_DIR = USER_DATA_LOC / "extensions"
# Database paths (ADR-118 Four-Tier Architecture)
ORG_DB = CONTEXT_STORAGE / "org.db" # TIER 2: Irreplaceable
SESSIONS_DB = CONTEXT_STORAGE / "sessions.db" # TIER 3: Regenerable
# NOTE: context.db is DEPRECATED - NO FALLBACK per ADR-118
CONTEXT_DB = CONTEXT_STORAGE / "context.db" # LEGACY - DO NOT USE
# Framework location (best guess)
if sys.platform == "darwin":
FRAMEWORK_LOC = HOME / "Library" / "Application Support" / "CODITECT" / "core"
else:
FRAMEWORK_LOC = HOME / ".local" / "share" / "coditect" / "core"
# Fallback functions
def discover_projects_dir() -> Path:
return PROJECTS_DIR
def get_user_data_dir() -> Path:
return USER_DATA_LOC
def get_framework_dir() -> Path:
return FRAMEWORK_LOC
def get_context_storage_dir() -> Path:
return CONTEXT_STORAGE
def get_session_logs_dir() -> Path:
return SESSION_LOGS
def get_machine_id_path() -> Path:
return MACHINE_ID_FILE
def get_backups_dir() -> Path:
return BACKUPS_DIR
def get_extensions_dir() -> Path:
return EXTENSIONS_DIR
def get_org_db_path() -> Path:
return ORG_DB
def get_sessions_db_path() -> Path:
return SESSIONS_DB
def get_context_db_path() -> Path:
return CONTEXT_DB
def get_legacy_paths() -> dict:
return {}
def check_migration_needed() -> bool:
return False
Module exports
all = [ # Status "PATHS_AVAILABLE", # Constants "HOME", "FRAMEWORK_LOC", "PROJECTS_DIR", "USER_DATA_LOC", "CONTEXT_STORAGE", "SESSION_LOGS", "MACHINE_ID_FILE", "BACKUPS_DIR", "EXTENSIONS_DIR", "ORG_DB", "SESSIONS_DB", "CONTEXT_DB", # Functions "discover_projects_dir", "get_user_data_dir", "get_framework_dir", "get_context_storage_dir", "get_session_logs_dir", "get_machine_id_path", "get_backups_dir", "get_extensions_dir", "get_org_db_path", "get_sessions_db_path", "get_context_db_path", "get_legacy_paths", "check_migration_needed", ]
CLI for testing
if name == "main": print("CODITECT Path Helper (ADR-114, ADR-120)") print("=" * 50) print(f"PATHS_AVAILABLE: {PATHS_AVAILABLE}") print(f"PROJECTS_DIR: {PROJECTS_DIR}") print(f"USER_DATA_LOC: {USER_DATA_LOC}") print(f"CONTEXT_STORAGE: {CONTEXT_STORAGE}") print(f"SESSION_LOGS: {SESSION_LOGS}") print(f"EXTENSIONS_DIR: {EXTENSIONS_DIR}") print(f"FRAMEWORK_LOC: {FRAMEWORK_LOC}") print() print(f"Session logs exists: {SESSION_LOGS.exists()}") print(f"Extensions exists: {EXTENSIONS_DIR.exists()}")