portacode 0.3.4.dev0__py3-none-any.whl → 1.4.11.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of portacode might be problematic. Click here for more details.
- portacode/_version.py +16 -3
- portacode/cli.py +155 -19
- portacode/connection/client.py +152 -12
- portacode/connection/handlers/WEBSOCKET_PROTOCOL.md +1577 -0
- portacode/connection/handlers/__init__.py +43 -1
- portacode/connection/handlers/base.py +122 -18
- portacode/connection/handlers/chunked_content.py +244 -0
- portacode/connection/handlers/diff_handlers.py +603 -0
- portacode/connection/handlers/file_handlers.py +902 -17
- portacode/connection/handlers/project_aware_file_handlers.py +226 -0
- portacode/connection/handlers/project_state/README.md +312 -0
- portacode/connection/handlers/project_state/__init__.py +92 -0
- portacode/connection/handlers/project_state/file_system_watcher.py +179 -0
- portacode/connection/handlers/project_state/git_manager.py +1502 -0
- portacode/connection/handlers/project_state/handlers.py +875 -0
- portacode/connection/handlers/project_state/manager.py +1331 -0
- portacode/connection/handlers/project_state/models.py +108 -0
- portacode/connection/handlers/project_state/utils.py +50 -0
- portacode/connection/handlers/project_state_handlers.py +45 -0
- portacode/connection/handlers/proxmox_infra.py +307 -0
- portacode/connection/handlers/registry.py +53 -10
- portacode/connection/handlers/session.py +705 -53
- portacode/connection/handlers/system_handlers.py +142 -8
- portacode/connection/handlers/tab_factory.py +389 -0
- portacode/connection/handlers/terminal_handlers.py +150 -11
- portacode/connection/handlers/update_handler.py +61 -0
- portacode/connection/multiplex.py +60 -2
- portacode/connection/terminal.py +695 -28
- portacode/keypair.py +63 -1
- portacode/link_capture/__init__.py +38 -0
- portacode/link_capture/__pycache__/__init__.cpython-311.pyc +0 -0
- portacode/link_capture/bin/__pycache__/link_capture_wrapper.cpython-311.pyc +0 -0
- portacode/link_capture/bin/elinks +3 -0
- portacode/link_capture/bin/gio-open +3 -0
- portacode/link_capture/bin/gnome-open +3 -0
- portacode/link_capture/bin/gvfs-open +3 -0
- portacode/link_capture/bin/kde-open +3 -0
- portacode/link_capture/bin/kfmclient +3 -0
- portacode/link_capture/bin/link_capture_exec.sh +11 -0
- portacode/link_capture/bin/link_capture_wrapper.py +75 -0
- portacode/link_capture/bin/links +3 -0
- portacode/link_capture/bin/links2 +3 -0
- portacode/link_capture/bin/lynx +3 -0
- portacode/link_capture/bin/mate-open +3 -0
- portacode/link_capture/bin/netsurf +3 -0
- portacode/link_capture/bin/sensible-browser +3 -0
- portacode/link_capture/bin/w3m +3 -0
- portacode/link_capture/bin/x-www-browser +3 -0
- portacode/link_capture/bin/xdg-open +3 -0
- portacode/logging_categories.py +140 -0
- portacode/pairing.py +103 -0
- portacode/service.py +6 -0
- portacode/static/js/test-ntp-clock.html +63 -0
- portacode/static/js/utils/ntp-clock.js +232 -0
- portacode/utils/NTP_ARCHITECTURE.md +136 -0
- portacode/utils/__init__.py +1 -0
- portacode/utils/diff_apply.py +456 -0
- portacode/utils/diff_renderer.py +371 -0
- portacode/utils/ntp_clock.py +65 -0
- portacode-1.4.11.dev0.dist-info/METADATA +298 -0
- portacode-1.4.11.dev0.dist-info/RECORD +97 -0
- {portacode-0.3.4.dev0.dist-info → portacode-1.4.11.dev0.dist-info}/WHEEL +1 -1
- portacode-1.4.11.dev0.dist-info/top_level.txt +3 -0
- test_modules/README.md +296 -0
- test_modules/__init__.py +1 -0
- test_modules/test_device_online.py +44 -0
- test_modules/test_file_operations.py +743 -0
- test_modules/test_git_status_ui.py +370 -0
- test_modules/test_login_flow.py +50 -0
- test_modules/test_navigate_testing_folder.py +361 -0
- test_modules/test_play_store_screenshots.py +294 -0
- test_modules/test_terminal_buffer_performance.py +261 -0
- test_modules/test_terminal_interaction.py +80 -0
- test_modules/test_terminal_loading_race_condition.py +95 -0
- test_modules/test_terminal_start.py +56 -0
- testing_framework/.env.example +21 -0
- testing_framework/README.md +334 -0
- testing_framework/__init__.py +17 -0
- testing_framework/cli.py +326 -0
- testing_framework/core/__init__.py +1 -0
- testing_framework/core/base_test.py +336 -0
- testing_framework/core/cli_manager.py +177 -0
- testing_framework/core/hierarchical_runner.py +577 -0
- testing_framework/core/playwright_manager.py +520 -0
- testing_framework/core/runner.py +447 -0
- testing_framework/core/shared_cli_manager.py +234 -0
- testing_framework/core/test_discovery.py +112 -0
- testing_framework/requirements.txt +12 -0
- portacode-0.3.4.dev0.dist-info/METADATA +0 -236
- portacode-0.3.4.dev0.dist-info/RECORD +0 -27
- portacode-0.3.4.dev0.dist-info/top_level.txt +0 -1
- {portacode-0.3.4.dev0.dist-info → portacode-1.4.11.dev0.dist-info}/entry_points.txt +0 -0
- {portacode-0.3.4.dev0.dist-info → portacode-1.4.11.dev0.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"""Project-aware file operation handlers that integrate with project state management."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import logging
|
|
5
|
+
from typing import Any, Dict
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from .base import SyncHandler
|
|
9
|
+
from .project_state.manager import get_or_create_project_state_manager
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ProjectAwareFileWriteHandler(SyncHandler):
|
|
15
|
+
"""Handler for writing file contents that updates project state tabs."""
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def command_name(self) -> str:
|
|
19
|
+
return "file_write"
|
|
20
|
+
|
|
21
|
+
def execute(self, message: Dict[str, Any]) -> Dict[str, Any]:
|
|
22
|
+
"""Write file contents and update project state tabs."""
|
|
23
|
+
file_path = message.get("path")
|
|
24
|
+
content = message.get("content", "")
|
|
25
|
+
# Optimistic lock: ensure the client saw the correct file state
|
|
26
|
+
expected_mtime = message.get("expected_mtime")
|
|
27
|
+
if expected_mtime is not None:
|
|
28
|
+
try:
|
|
29
|
+
current_mtime = os.path.getmtime(file_path)
|
|
30
|
+
except FileNotFoundError:
|
|
31
|
+
raise ValueError(f"File not found: {file_path}")
|
|
32
|
+
if current_mtime != expected_mtime:
|
|
33
|
+
raise ValueError(
|
|
34
|
+
f"File was modified on disk (current {current_mtime} != expected {expected_mtime})"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
if not file_path:
|
|
38
|
+
raise ValueError("path parameter is required")
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
# Create parent directories if they don't exist
|
|
42
|
+
Path(file_path).parent.mkdir(parents=True, exist_ok=True)
|
|
43
|
+
|
|
44
|
+
# Write the file
|
|
45
|
+
with open(file_path, 'w', encoding='utf-8') as f:
|
|
46
|
+
f.write(content)
|
|
47
|
+
|
|
48
|
+
# Update project state tabs that have this file open
|
|
49
|
+
try:
|
|
50
|
+
manager = get_or_create_project_state_manager(self.context, self.control_channel)
|
|
51
|
+
|
|
52
|
+
# Update all project states that have tabs open for this file
|
|
53
|
+
for client_session_id, project_state in manager.projects.items():
|
|
54
|
+
tabs_updated = False
|
|
55
|
+
|
|
56
|
+
# Check if any tabs have this file path
|
|
57
|
+
for tab_id, tab in project_state.openTabs.items():
|
|
58
|
+
if tab.get('file_path') == file_path:
|
|
59
|
+
# Update tab content to match what was just saved
|
|
60
|
+
tab['content'] = content
|
|
61
|
+
tab['is_dirty'] = False
|
|
62
|
+
tab['originalContent'] = content
|
|
63
|
+
tabs_updated = True
|
|
64
|
+
logger.info(f"Updated tab {tab_id} content for file {file_path} in project state {client_session_id}")
|
|
65
|
+
|
|
66
|
+
# Broadcast updated project state if we made changes
|
|
67
|
+
if tabs_updated:
|
|
68
|
+
logger.info(f"Broadcasting project state update for client session {client_session_id}")
|
|
69
|
+
manager.broadcast_project_state(client_session_id)
|
|
70
|
+
|
|
71
|
+
except Exception as e:
|
|
72
|
+
logger.warning(f"Failed to update project state after file write: {e}")
|
|
73
|
+
# Don't fail the file write just because project state update failed
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
"event": "file_write_response",
|
|
77
|
+
"path": file_path,
|
|
78
|
+
"bytes_written": len(content.encode('utf-8')),
|
|
79
|
+
"success": True,
|
|
80
|
+
}
|
|
81
|
+
except PermissionError:
|
|
82
|
+
raise RuntimeError(f"Permission denied: {file_path}")
|
|
83
|
+
except OSError as e:
|
|
84
|
+
raise RuntimeError(f"Failed to write file: {e}")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class ProjectAwareFileCreateHandler(SyncHandler):
|
|
88
|
+
"""Handler for creating new files that updates project state."""
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def command_name(self) -> str:
|
|
92
|
+
return "file_create"
|
|
93
|
+
|
|
94
|
+
def execute(self, message: Dict[str, Any]) -> Dict[str, Any]:
|
|
95
|
+
"""Create a new file and refresh project state."""
|
|
96
|
+
parent_path = message.get("parent_path")
|
|
97
|
+
file_name = message.get("file_name")
|
|
98
|
+
content = message.get("content", "")
|
|
99
|
+
|
|
100
|
+
if not parent_path:
|
|
101
|
+
raise ValueError("parent_path parameter is required")
|
|
102
|
+
if not file_name:
|
|
103
|
+
raise ValueError("file_name parameter is required")
|
|
104
|
+
|
|
105
|
+
# Validate file name (no path separators or special chars)
|
|
106
|
+
if "/" in file_name or "\\" in file_name or file_name in [".", ".."]:
|
|
107
|
+
raise ValueError("Invalid file name")
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
# Ensure parent directory exists
|
|
111
|
+
parent_dir = Path(parent_path)
|
|
112
|
+
if not parent_dir.exists():
|
|
113
|
+
raise ValueError(f"Parent directory does not exist: {parent_path}")
|
|
114
|
+
if not parent_dir.is_dir():
|
|
115
|
+
raise ValueError(f"Parent path is not a directory: {parent_path}")
|
|
116
|
+
|
|
117
|
+
# Create the full file path
|
|
118
|
+
file_path = parent_dir / file_name
|
|
119
|
+
|
|
120
|
+
# Check if file already exists
|
|
121
|
+
if file_path.exists():
|
|
122
|
+
raise ValueError(f"File already exists: {file_name}")
|
|
123
|
+
|
|
124
|
+
# Create the file
|
|
125
|
+
with open(file_path, 'w', encoding='utf-8') as f:
|
|
126
|
+
f.write(content)
|
|
127
|
+
|
|
128
|
+
# Trigger project state refresh
|
|
129
|
+
try:
|
|
130
|
+
manager = get_or_create_project_state_manager(self.context, self.control_channel)
|
|
131
|
+
|
|
132
|
+
# Schedule the refresh (don't await since this is sync handler)
|
|
133
|
+
import asyncio
|
|
134
|
+
try:
|
|
135
|
+
loop = asyncio.get_event_loop()
|
|
136
|
+
if loop.is_running():
|
|
137
|
+
loop.create_task(manager.refresh_project_state_for_file_change(str(file_path)))
|
|
138
|
+
logger.info(f"Scheduled project state refresh after file creation: {file_path}")
|
|
139
|
+
except Exception as e:
|
|
140
|
+
logger.warning(f"Could not schedule project state refresh: {e}")
|
|
141
|
+
|
|
142
|
+
except Exception as e:
|
|
143
|
+
logger.warning(f"Failed to refresh project state after file creation: {e}")
|
|
144
|
+
# Don't fail the file creation just because project state refresh failed
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
"event": "file_create_response",
|
|
148
|
+
"parent_path": parent_path,
|
|
149
|
+
"file_name": file_name,
|
|
150
|
+
"file_path": str(file_path),
|
|
151
|
+
"success": True,
|
|
152
|
+
}
|
|
153
|
+
except PermissionError:
|
|
154
|
+
raise RuntimeError(f"Permission denied: {parent_path}")
|
|
155
|
+
except OSError as e:
|
|
156
|
+
raise RuntimeError(f"Failed to create file: {e}")
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class ProjectAwareFolderCreateHandler(SyncHandler):
|
|
160
|
+
"""Handler for creating new folders that updates project state."""
|
|
161
|
+
|
|
162
|
+
@property
|
|
163
|
+
def command_name(self) -> str:
|
|
164
|
+
return "folder_create"
|
|
165
|
+
|
|
166
|
+
def execute(self, message: Dict[str, Any]) -> Dict[str, Any]:
|
|
167
|
+
"""Create a new folder and refresh project state."""
|
|
168
|
+
parent_path = message.get("parent_path")
|
|
169
|
+
folder_name = message.get("folder_name")
|
|
170
|
+
|
|
171
|
+
if not parent_path:
|
|
172
|
+
raise ValueError("parent_path parameter is required")
|
|
173
|
+
if not folder_name:
|
|
174
|
+
raise ValueError("folder_name parameter is required")
|
|
175
|
+
|
|
176
|
+
# Validate folder name (no path separators or special chars)
|
|
177
|
+
if "/" in folder_name or "\\" in folder_name or folder_name in [".", ".."]:
|
|
178
|
+
raise ValueError("Invalid folder name")
|
|
179
|
+
|
|
180
|
+
try:
|
|
181
|
+
# Ensure parent directory exists
|
|
182
|
+
parent_dir = Path(parent_path)
|
|
183
|
+
if not parent_dir.exists():
|
|
184
|
+
raise ValueError(f"Parent directory does not exist: {parent_path}")
|
|
185
|
+
if not parent_dir.is_dir():
|
|
186
|
+
raise ValueError(f"Parent path is not a directory: {parent_path}")
|
|
187
|
+
|
|
188
|
+
# Create the full folder path
|
|
189
|
+
folder_path = parent_dir / folder_name
|
|
190
|
+
|
|
191
|
+
# Check if folder already exists
|
|
192
|
+
if folder_path.exists():
|
|
193
|
+
raise ValueError(f"Folder already exists: {folder_name}")
|
|
194
|
+
|
|
195
|
+
# Create the folder
|
|
196
|
+
folder_path.mkdir(parents=False, exist_ok=False)
|
|
197
|
+
|
|
198
|
+
# Trigger project state refresh
|
|
199
|
+
try:
|
|
200
|
+
manager = get_or_create_project_state_manager(self.context, self.control_channel)
|
|
201
|
+
|
|
202
|
+
# Schedule the refresh (don't await since this is sync handler)
|
|
203
|
+
import asyncio
|
|
204
|
+
try:
|
|
205
|
+
loop = asyncio.get_event_loop()
|
|
206
|
+
if loop.is_running():
|
|
207
|
+
loop.create_task(manager.refresh_project_state_for_file_change(str(folder_path)))
|
|
208
|
+
logger.info(f"Scheduled project state refresh after folder creation: {folder_path}")
|
|
209
|
+
except Exception as e:
|
|
210
|
+
logger.warning(f"Could not schedule project state refresh: {e}")
|
|
211
|
+
|
|
212
|
+
except Exception as e:
|
|
213
|
+
logger.warning(f"Failed to refresh project state after folder creation: {e}")
|
|
214
|
+
# Don't fail the folder creation just because project state refresh failed
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
"event": "folder_create_response",
|
|
218
|
+
"parent_path": parent_path,
|
|
219
|
+
"folder_name": folder_name,
|
|
220
|
+
"folder_path": str(folder_path),
|
|
221
|
+
"success": True,
|
|
222
|
+
}
|
|
223
|
+
except PermissionError:
|
|
224
|
+
raise RuntimeError(f"Permission denied: {parent_path}")
|
|
225
|
+
except OSError as e:
|
|
226
|
+
raise RuntimeError(f"Failed to create folder: {e}")
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
# Project State Management - Modular Architecture
|
|
2
|
+
|
|
3
|
+
This document explains the modular architecture of the project state management system, which was refactored from a single 3000+ line file into a well-organized, maintainable structure.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The project state management system handles:
|
|
8
|
+
- File system monitoring and change detection
|
|
9
|
+
- Git repository integration and operations
|
|
10
|
+
- Tab management (file tabs, diff tabs, etc.)
|
|
11
|
+
- Real-time project state synchronization
|
|
12
|
+
- Client session management
|
|
13
|
+
|
|
14
|
+
## Architecture
|
|
15
|
+
|
|
16
|
+
The original monolithic `project_state_handlers.py` file has been broken down into the following modules:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
project_state/
|
|
20
|
+
├── __init__.py # Public API exports
|
|
21
|
+
├── models.py # Data structures and models
|
|
22
|
+
├── git_manager.py # Git operations and repository management
|
|
23
|
+
├── file_system_watcher.py # File system change monitoring
|
|
24
|
+
├── manager.py # Central project state coordinator
|
|
25
|
+
├── handlers.py # Request handlers for various operations
|
|
26
|
+
├── utils.py # Utility functions and helpers
|
|
27
|
+
└── README.md # This documentation
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Module Details
|
|
31
|
+
|
|
32
|
+
### 1. `models.py` - Data Structures
|
|
33
|
+
|
|
34
|
+
Contains all the dataclasses and models used throughout the system:
|
|
35
|
+
|
|
36
|
+
- **`ProjectState`**: Complete state of a project (files, git status, tabs, etc.)
|
|
37
|
+
- **`FileItem`**: Represents a file or directory with metadata
|
|
38
|
+
- **`TabInfo`**: Represents an editor tab with content and metadata
|
|
39
|
+
- **`MonitoredFolder`**: Represents a folder being monitored for changes
|
|
40
|
+
- **`GitFileChange`**: Represents a single file change in git
|
|
41
|
+
- **`GitDetailedStatus`**: Detailed git status with file hashes
|
|
42
|
+
|
|
43
|
+
**Key Features:**
|
|
44
|
+
- All models are dataclasses for easy serialization
|
|
45
|
+
- Comprehensive typing throughout
|
|
46
|
+
- Self-documenting field definitions
|
|
47
|
+
|
|
48
|
+
### 2. `git_manager.py` - Git Operations
|
|
49
|
+
|
|
50
|
+
Handles all Git-related functionality:
|
|
51
|
+
|
|
52
|
+
**Core Responsibilities:**
|
|
53
|
+
- Repository detection and initialization
|
|
54
|
+
- File status tracking (staged, modified, untracked, etc.)
|
|
55
|
+
- Diff generation with syntax highlighting
|
|
56
|
+
- Content retrieval at different git references
|
|
57
|
+
- Git commands (stage, unstage, revert)
|
|
58
|
+
|
|
59
|
+
**Key Features:**
|
|
60
|
+
- Graceful fallback when GitPython is not available
|
|
61
|
+
- Performance optimizations for large files
|
|
62
|
+
- HTML diff generation with syntax highlighting
|
|
63
|
+
- Support for diff-match-patch algorithm
|
|
64
|
+
- Cross-platform path handling
|
|
65
|
+
|
|
66
|
+
**Performance Safeguards:**
|
|
67
|
+
- Timeouts for diff generation
|
|
68
|
+
- Size limits for large files
|
|
69
|
+
- Batch syntax highlighting for better performance
|
|
70
|
+
- Simplified diff view for very large files
|
|
71
|
+
|
|
72
|
+
### 3. `file_system_watcher.py` - File System Monitoring
|
|
73
|
+
|
|
74
|
+
Monitors file system changes using the watchdog library:
|
|
75
|
+
|
|
76
|
+
**Core Responsibilities:**
|
|
77
|
+
- Cross-platform file system monitoring
|
|
78
|
+
- Event filtering and debouncing
|
|
79
|
+
- Git repository change detection
|
|
80
|
+
- Thread-safe async event handling
|
|
81
|
+
|
|
82
|
+
**Key Features:**
|
|
83
|
+
- Selective monitoring of relevant file changes
|
|
84
|
+
- Special handling for .git directory changes
|
|
85
|
+
- Debug tracing for troubleshooting
|
|
86
|
+
- Graceful fallback when watchdog is not available
|
|
87
|
+
|
|
88
|
+
**Event Filtering:**
|
|
89
|
+
- Skips temporary and debug files
|
|
90
|
+
- Focuses on meaningful file changes
|
|
91
|
+
- Monitors git-specific files for status updates
|
|
92
|
+
|
|
93
|
+
### 4. `manager.py` - Central Coordinator
|
|
94
|
+
|
|
95
|
+
The main `ProjectStateManager` class that orchestrates all operations:
|
|
96
|
+
|
|
97
|
+
**Core Responsibilities:**
|
|
98
|
+
- Project state initialization and lifecycle
|
|
99
|
+
- Folder expansion/collapse logic
|
|
100
|
+
- Tab management (open, close, activate)
|
|
101
|
+
- File system change processing
|
|
102
|
+
- Client session management
|
|
103
|
+
- State synchronization and updates
|
|
104
|
+
|
|
105
|
+
**Key Features:**
|
|
106
|
+
- Singleton pattern for global state management
|
|
107
|
+
- Debounced file change processing
|
|
108
|
+
- Flattened item structure for performance
|
|
109
|
+
- Detailed debug logging and state tracking
|
|
110
|
+
- Client session isolation
|
|
111
|
+
|
|
112
|
+
**State Management:**
|
|
113
|
+
- Each client session has independent project state
|
|
114
|
+
- Monitored folders with expansion states
|
|
115
|
+
- Real-time synchronization with clients
|
|
116
|
+
- Orphaned state cleanup
|
|
117
|
+
|
|
118
|
+
### 5. `handlers.py` - Request Handlers
|
|
119
|
+
|
|
120
|
+
AsyncHandler classes for different operations:
|
|
121
|
+
|
|
122
|
+
**Handler Classes:**
|
|
123
|
+
- `ProjectStateFolderExpandHandler` - Expand folders
|
|
124
|
+
- `ProjectStateFolderCollapseHandler` - Collapse folders
|
|
125
|
+
- `ProjectStateFileOpenHandler` - Open files in tabs
|
|
126
|
+
- `ProjectStateTabCloseHandler` - Close tabs
|
|
127
|
+
- `ProjectStateSetActiveTabHandler` - Set active tab
|
|
128
|
+
- `ProjectStateDiffOpenHandler` - Open diff tabs
|
|
129
|
+
- `ProjectStateGitStageHandler` - Stage files
|
|
130
|
+
- `ProjectStateGitUnstageHandler` - Unstage files
|
|
131
|
+
- `ProjectStateGitRevertHandler` - Revert files
|
|
132
|
+
|
|
133
|
+
**Key Features:**
|
|
134
|
+
- Consistent error handling and validation
|
|
135
|
+
- Server/client session mapping
|
|
136
|
+
- Automatic state updates after operations
|
|
137
|
+
- Comprehensive logging
|
|
138
|
+
|
|
139
|
+
### 6. `utils.py` - Utility Functions
|
|
140
|
+
|
|
141
|
+
Shared utility functions:
|
|
142
|
+
|
|
143
|
+
- **`generate_tab_key()`**: Creates unique keys for different tab types
|
|
144
|
+
- Support for file tabs, diff tabs, untitled tabs
|
|
145
|
+
- Handles git reference parameters for diff tabs
|
|
146
|
+
|
|
147
|
+
## Preserved Functionality
|
|
148
|
+
|
|
149
|
+
All functionality from the original file has been preserved:
|
|
150
|
+
|
|
151
|
+
✅ **Complete Feature Parity**
|
|
152
|
+
- All classes, methods, and functions maintained
|
|
153
|
+
- Original behavior preserved exactly
|
|
154
|
+
- All logging statements preserved
|
|
155
|
+
- All documentation comments maintained
|
|
156
|
+
|
|
157
|
+
✅ **Performance Optimizations**
|
|
158
|
+
- Large file handling safeguards
|
|
159
|
+
- Diff generation timeouts
|
|
160
|
+
- Syntax highlighting optimizations
|
|
161
|
+
- Memory-efficient processing
|
|
162
|
+
|
|
163
|
+
✅ **Error Handling**
|
|
164
|
+
- Graceful fallbacks for missing dependencies
|
|
165
|
+
- Cross-platform compatibility
|
|
166
|
+
- Comprehensive exception handling
|
|
167
|
+
- Debug mode support
|
|
168
|
+
|
|
169
|
+
✅ **Git Integration**
|
|
170
|
+
- Full GitPython integration
|
|
171
|
+
- Advanced diff capabilities
|
|
172
|
+
- Multi-reference comparisons
|
|
173
|
+
- HTML diff generation
|
|
174
|
+
|
|
175
|
+
## Usage Examples
|
|
176
|
+
|
|
177
|
+
### Basic Usage
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
# Import the main components
|
|
181
|
+
from project_state import (
|
|
182
|
+
get_or_create_project_state_manager,
|
|
183
|
+
ProjectState,
|
|
184
|
+
GitManager
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
# Get the global project state manager
|
|
188
|
+
manager = get_or_create_project_state_manager(context, control_channel)
|
|
189
|
+
|
|
190
|
+
# Initialize a project
|
|
191
|
+
project_state = await manager.initialize_project_state(
|
|
192
|
+
client_session_id="session123",
|
|
193
|
+
project_folder_path="/path/to/project"
|
|
194
|
+
)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Using Individual Components
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
# Use GitManager independently
|
|
201
|
+
from project_state import GitManager
|
|
202
|
+
|
|
203
|
+
git_manager = GitManager("/path/to/project")
|
|
204
|
+
if git_manager.is_git_repo:
|
|
205
|
+
status = git_manager.get_detailed_status()
|
|
206
|
+
print(f"Branch: {git_manager.get_branch_name()}")
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Handler Integration
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
# Import handlers for request processing
|
|
213
|
+
from project_state import ProjectStateFolderExpandHandler
|
|
214
|
+
|
|
215
|
+
# Use in your handler registry
|
|
216
|
+
handler = ProjectStateFolderExpandHandler()
|
|
217
|
+
result = await handler.execute(message)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Migration Guide
|
|
221
|
+
|
|
222
|
+
The refactoring maintains complete backward compatibility:
|
|
223
|
+
|
|
224
|
+
1. **Existing imports continue to work** - The original module structure is preserved
|
|
225
|
+
2. **No API changes** - All function signatures remain the same
|
|
226
|
+
3. **Same behavior** - All functionality works exactly as before
|
|
227
|
+
4. **Performance improvements** - Better organization enables easier optimization
|
|
228
|
+
|
|
229
|
+
## Benefits of Modular Architecture
|
|
230
|
+
|
|
231
|
+
### 1. **Maintainability**
|
|
232
|
+
- Single responsibility principle
|
|
233
|
+
- Easier to locate and modify specific functionality
|
|
234
|
+
- Reduced cognitive load when working on specific features
|
|
235
|
+
|
|
236
|
+
### 2. **Testability**
|
|
237
|
+
- Individual modules can be tested in isolation
|
|
238
|
+
- Clear dependencies between components
|
|
239
|
+
- Easier mocking for unit tests
|
|
240
|
+
|
|
241
|
+
### 3. **Reusability**
|
|
242
|
+
- Components can be used independently
|
|
243
|
+
- GitManager can be used outside project state context
|
|
244
|
+
- Models can be shared across different systems
|
|
245
|
+
|
|
246
|
+
### 4. **Performance**
|
|
247
|
+
- Better import optimization
|
|
248
|
+
- Reduced memory footprint for unused components
|
|
249
|
+
- Clearer performance bottleneck identification
|
|
250
|
+
|
|
251
|
+
### 5. **Documentation**
|
|
252
|
+
- Self-documenting module structure
|
|
253
|
+
- Clear separation of concerns
|
|
254
|
+
- Easier onboarding for new developers
|
|
255
|
+
|
|
256
|
+
## Development Guidelines
|
|
257
|
+
|
|
258
|
+
### Adding New Features
|
|
259
|
+
|
|
260
|
+
1. **Identify the appropriate module** based on the feature's responsibility
|
|
261
|
+
2. **Follow existing patterns** for consistency
|
|
262
|
+
3. **Update the `__init__.py`** to export new public APIs
|
|
263
|
+
4. **Add comprehensive logging** for debugging
|
|
264
|
+
5. **Include performance safeguards** for resource-intensive operations
|
|
265
|
+
|
|
266
|
+
### Modifying Existing Features
|
|
267
|
+
|
|
268
|
+
1. **Maintain backward compatibility** unless breaking changes are explicitly approved
|
|
269
|
+
2. **Preserve all logging statements** for debugging continuity
|
|
270
|
+
3. **Update documentation** to reflect changes
|
|
271
|
+
4. **Test across all affected modules** to ensure integration works
|
|
272
|
+
|
|
273
|
+
### Best Practices
|
|
274
|
+
|
|
275
|
+
- Use type hints consistently
|
|
276
|
+
- Follow the established logging patterns
|
|
277
|
+
- Include docstrings for all public methods
|
|
278
|
+
- Handle errors gracefully with appropriate fallbacks
|
|
279
|
+
- Consider performance implications of changes
|
|
280
|
+
|
|
281
|
+
## Troubleshooting
|
|
282
|
+
|
|
283
|
+
### Common Issues
|
|
284
|
+
|
|
285
|
+
1. **Import Errors**: Ensure you're importing from the correct module path
|
|
286
|
+
2. **Missing Dependencies**: Check for optional dependencies (GitPython, watchdog, etc.)
|
|
287
|
+
3. **Performance Issues**: Review file size limits and timeout settings
|
|
288
|
+
4. **State Synchronization**: Check client session mapping and event handling
|
|
289
|
+
|
|
290
|
+
### Debug Mode
|
|
291
|
+
|
|
292
|
+
Enable debug mode for detailed state tracking:
|
|
293
|
+
|
|
294
|
+
```python
|
|
295
|
+
manager.set_debug_mode(True, "/path/to/debug.json")
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
This creates a JSON file with complete project state information for analysis.
|
|
299
|
+
|
|
300
|
+
## Future Enhancements
|
|
301
|
+
|
|
302
|
+
The modular architecture enables several future improvements:
|
|
303
|
+
|
|
304
|
+
1. **Plugin System**: Easy addition of new file system watchers or git providers
|
|
305
|
+
2. **Caching Layer**: Independent caching modules for performance
|
|
306
|
+
3. **API Versioning**: Separate handler versions for backward compatibility
|
|
307
|
+
4. **Microservice Architecture**: Components can be extracted to separate services
|
|
308
|
+
5. **Enhanced Testing**: Module-specific test suites with better coverage
|
|
309
|
+
|
|
310
|
+
## Conclusion
|
|
311
|
+
|
|
312
|
+
The modular architecture maintains all functionality while providing a solid foundation for future development. The separation of concerns makes the codebase more maintainable, testable, and extensible, while preserving the robust feature set that was built in the original implementation.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""Project State Management Package
|
|
2
|
+
|
|
3
|
+
This package provides a modular architecture for managing project state in the
|
|
4
|
+
Portacode application, including file system monitoring, git integration,
|
|
5
|
+
tab management, and real-time state synchronization.
|
|
6
|
+
|
|
7
|
+
The package is organized into the following modules:
|
|
8
|
+
|
|
9
|
+
- models: Data structures and models (ProjectState, FileItem, TabInfo, etc.)
|
|
10
|
+
- git_manager: Git operations and repository management
|
|
11
|
+
- file_system_watcher: File system change monitoring
|
|
12
|
+
- manager: Central project state coordinator
|
|
13
|
+
- handlers: Request handlers for various operations
|
|
14
|
+
- utils: Utility functions and helpers
|
|
15
|
+
|
|
16
|
+
Usage:
|
|
17
|
+
from project_state.manager import get_or_create_project_state_manager
|
|
18
|
+
from project_state.handlers import ProjectStateFolderExpandHandler
|
|
19
|
+
from project_state.models import ProjectState, FileItem
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
# Public API exports
|
|
23
|
+
from .models import (
|
|
24
|
+
ProjectState,
|
|
25
|
+
FileItem,
|
|
26
|
+
TabInfo,
|
|
27
|
+
MonitoredFolder,
|
|
28
|
+
GitFileChange,
|
|
29
|
+
GitDetailedStatus
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
from .manager import (
|
|
33
|
+
ProjectStateManager,
|
|
34
|
+
get_or_create_project_state_manager,
|
|
35
|
+
reset_global_project_state_manager,
|
|
36
|
+
debug_global_manager_state
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
from .git_manager import GitManager
|
|
40
|
+
from .file_system_watcher import FileSystemWatcher
|
|
41
|
+
|
|
42
|
+
from .handlers import (
|
|
43
|
+
ProjectStateFolderExpandHandler,
|
|
44
|
+
ProjectStateFolderCollapseHandler,
|
|
45
|
+
ProjectStateFileOpenHandler,
|
|
46
|
+
ProjectStateTabCloseHandler,
|
|
47
|
+
ProjectStateSetActiveTabHandler,
|
|
48
|
+
ProjectStateDiffOpenHandler,
|
|
49
|
+
ProjectStateGitStageHandler,
|
|
50
|
+
ProjectStateGitUnstageHandler,
|
|
51
|
+
ProjectStateGitRevertHandler,
|
|
52
|
+
ProjectStateGitCommitHandler,
|
|
53
|
+
handle_client_session_cleanup
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
from .utils import generate_tab_key
|
|
57
|
+
|
|
58
|
+
__all__ = [
|
|
59
|
+
# Models
|
|
60
|
+
'ProjectState',
|
|
61
|
+
'FileItem',
|
|
62
|
+
'TabInfo',
|
|
63
|
+
'MonitoredFolder',
|
|
64
|
+
'GitFileChange',
|
|
65
|
+
'GitDetailedStatus',
|
|
66
|
+
|
|
67
|
+
# Core classes
|
|
68
|
+
'ProjectStateManager',
|
|
69
|
+
'GitManager',
|
|
70
|
+
'FileSystemWatcher',
|
|
71
|
+
|
|
72
|
+
# Manager functions
|
|
73
|
+
'get_or_create_project_state_manager',
|
|
74
|
+
'reset_global_project_state_manager',
|
|
75
|
+
'debug_global_manager_state',
|
|
76
|
+
|
|
77
|
+
# Handlers
|
|
78
|
+
'ProjectStateFolderExpandHandler',
|
|
79
|
+
'ProjectStateFolderCollapseHandler',
|
|
80
|
+
'ProjectStateFileOpenHandler',
|
|
81
|
+
'ProjectStateTabCloseHandler',
|
|
82
|
+
'ProjectStateSetActiveTabHandler',
|
|
83
|
+
'ProjectStateDiffOpenHandler',
|
|
84
|
+
'ProjectStateGitStageHandler',
|
|
85
|
+
'ProjectStateGitUnstageHandler',
|
|
86
|
+
'ProjectStateGitRevertHandler',
|
|
87
|
+
'ProjectStateGitCommitHandler',
|
|
88
|
+
'handle_client_session_cleanup',
|
|
89
|
+
|
|
90
|
+
# Utils
|
|
91
|
+
'generate_tab_key'
|
|
92
|
+
]
|