144 lines
4.3 KiB
Python
144 lines
4.3 KiB
Python
"""Workspace directory management for project isolation."""
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
import shutil
|
|
|
|
|
|
@dataclass
|
|
class Workspace:
|
|
"""Represents a project workspace with defined directory structure.
|
|
|
|
Each project has its own isolated workspace directory following the layout:
|
|
~/gsd-workspaces/{project}/
|
|
├── .gsd/
|
|
│ ├── state.json # Session state
|
|
│ ├── state.json.bak # State backup
|
|
│ └── session-dump/ # Session dump
|
|
├── code/ # Project code (optional)
|
|
└── io-logs/
|
|
└── {session-id}.log # I/O logs
|
|
"""
|
|
|
|
project_name: str
|
|
root: Optional[Path] = None
|
|
|
|
def __post_init__(self):
|
|
"""Compute root path if not provided."""
|
|
if self.root is None:
|
|
self.root = Path.home() / "gsd-workspaces" / self.project_name
|
|
elif isinstance(self.root, str):
|
|
self.root = Path(self.root)
|
|
|
|
@property
|
|
def gsd_dir(self) -> Path:
|
|
"""Path to .gsd directory containing state files."""
|
|
return self.root / ".gsd"
|
|
|
|
@property
|
|
def state_path(self) -> Path:
|
|
"""Path to state.json file."""
|
|
return self.gsd_dir / "state.json"
|
|
|
|
@property
|
|
def state_backup_path(self) -> Path:
|
|
"""Path to state.json.bak file."""
|
|
return self.gsd_dir / "state.json.bak"
|
|
|
|
@property
|
|
def session_dump_dir(self) -> Path:
|
|
"""Path to session-dump directory."""
|
|
return self.gsd_dir / "session-dump"
|
|
|
|
@property
|
|
def code_dir(self) -> Path:
|
|
"""Path to code directory."""
|
|
return self.root / "code"
|
|
|
|
@property
|
|
def io_logs_dir(self) -> Path:
|
|
"""Path to io-logs directory."""
|
|
return self.root / "io-logs"
|
|
|
|
def io_log_path(self, session_id: str) -> Path:
|
|
"""Get path to I/O log file for a specific session.
|
|
|
|
Args:
|
|
session_id: The session identifier.
|
|
|
|
Returns:
|
|
Path to the session's I/O log file.
|
|
"""
|
|
return self.io_logs_dir / f"{session_id}.log"
|
|
|
|
|
|
class WorkspaceManager:
|
|
"""Manages workspace creation, destruction, and lifecycle.
|
|
|
|
The WorkspaceManager handles:
|
|
- Creating workspace directory structure
|
|
- Destroying workspaces (cleanup)
|
|
- Checking workspace existence
|
|
- Providing workspace objects
|
|
"""
|
|
|
|
WORKSPACES_ROOT = Path.home() / "gsd-workspaces"
|
|
|
|
def __init__(self, project_name: str, root: Optional[Path] = None):
|
|
"""Initialize workspace manager.
|
|
|
|
Args:
|
|
project_name: Name of the project (used for workspace directory).
|
|
root: Optional custom root path for workspaces (useful for testing).
|
|
"""
|
|
self.project_name = project_name
|
|
self.root = root or self.WORKSPACES_ROOT
|
|
if isinstance(self.root, str):
|
|
self.root = Path(self.root)
|
|
self.workspace = Workspace(project_name, self.root / project_name)
|
|
|
|
def create(self) -> Workspace:
|
|
"""Create workspace directory structure.
|
|
|
|
Creates all required directories:
|
|
- .gsd/ (with session-dump/ subdirectory)
|
|
- code/
|
|
- io-logs/
|
|
|
|
This operation is idempotent - calling multiple times will not fail.
|
|
|
|
Returns:
|
|
The Workspace object with paths configured.
|
|
"""
|
|
# Create all directories
|
|
self.workspace.gsd_dir.mkdir(parents=True, exist_ok=True)
|
|
self.workspace.session_dump_dir.mkdir(exist_ok=True)
|
|
self.workspace.code_dir.mkdir(exist_ok=True)
|
|
self.workspace.io_logs_dir.mkdir(exist_ok=True)
|
|
return self.workspace
|
|
|
|
def destroy(self) -> None:
|
|
"""Remove entire workspace directory.
|
|
|
|
Completely removes the workspace and all its contents.
|
|
Safe to call even if workspace doesn't exist.
|
|
"""
|
|
if self.workspace.root.exists():
|
|
shutil.rmtree(self.workspace.root)
|
|
|
|
def exists(self) -> bool:
|
|
"""Check if workspace exists.
|
|
|
|
Returns:
|
|
True if workspace root directory exists, False otherwise.
|
|
"""
|
|
return self.workspace.root.exists()
|
|
|
|
def get_workspace(self) -> Workspace:
|
|
"""Get workspace object (does not create).
|
|
|
|
Returns:
|
|
The Workspace object with paths configured.
|
|
"""
|
|
return self.workspace
|