portacode 0.3.19.dev4__py3-none-any.whl → 1.4.11.dev1__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.

Files changed (92) hide show
  1. portacode/_version.py +16 -3
  2. portacode/cli.py +143 -17
  3. portacode/connection/client.py +149 -10
  4. portacode/connection/handlers/WEBSOCKET_PROTOCOL.md +824 -21
  5. portacode/connection/handlers/__init__.py +28 -1
  6. portacode/connection/handlers/base.py +78 -16
  7. portacode/connection/handlers/chunked_content.py +244 -0
  8. portacode/connection/handlers/diff_handlers.py +603 -0
  9. portacode/connection/handlers/file_handlers.py +902 -17
  10. portacode/connection/handlers/project_aware_file_handlers.py +226 -0
  11. portacode/connection/handlers/project_state/README.md +312 -0
  12. portacode/connection/handlers/project_state/__init__.py +92 -0
  13. portacode/connection/handlers/project_state/file_system_watcher.py +179 -0
  14. portacode/connection/handlers/project_state/git_manager.py +1502 -0
  15. portacode/connection/handlers/project_state/handlers.py +875 -0
  16. portacode/connection/handlers/project_state/manager.py +1331 -0
  17. portacode/connection/handlers/project_state/models.py +108 -0
  18. portacode/connection/handlers/project_state/utils.py +50 -0
  19. portacode/connection/handlers/project_state_handlers.py +45 -2185
  20. portacode/connection/handlers/proxmox_infra.py +361 -0
  21. portacode/connection/handlers/registry.py +15 -4
  22. portacode/connection/handlers/session.py +483 -32
  23. portacode/connection/handlers/system_handlers.py +147 -8
  24. portacode/connection/handlers/tab_factory.py +53 -46
  25. portacode/connection/handlers/terminal_handlers.py +21 -8
  26. portacode/connection/handlers/update_handler.py +61 -0
  27. portacode/connection/multiplex.py +60 -2
  28. portacode/connection/terminal.py +214 -24
  29. portacode/keypair.py +63 -1
  30. portacode/link_capture/__init__.py +38 -0
  31. portacode/link_capture/__pycache__/__init__.cpython-311.pyc +0 -0
  32. portacode/link_capture/bin/__pycache__/link_capture_wrapper.cpython-311.pyc +0 -0
  33. portacode/link_capture/bin/elinks +3 -0
  34. portacode/link_capture/bin/gio-open +3 -0
  35. portacode/link_capture/bin/gnome-open +3 -0
  36. portacode/link_capture/bin/gvfs-open +3 -0
  37. portacode/link_capture/bin/kde-open +3 -0
  38. portacode/link_capture/bin/kfmclient +3 -0
  39. portacode/link_capture/bin/link_capture_exec.sh +11 -0
  40. portacode/link_capture/bin/link_capture_wrapper.py +75 -0
  41. portacode/link_capture/bin/links +3 -0
  42. portacode/link_capture/bin/links2 +3 -0
  43. portacode/link_capture/bin/lynx +3 -0
  44. portacode/link_capture/bin/mate-open +3 -0
  45. portacode/link_capture/bin/netsurf +3 -0
  46. portacode/link_capture/bin/sensible-browser +3 -0
  47. portacode/link_capture/bin/w3m +3 -0
  48. portacode/link_capture/bin/x-www-browser +3 -0
  49. portacode/link_capture/bin/xdg-open +3 -0
  50. portacode/logging_categories.py +140 -0
  51. portacode/pairing.py +103 -0
  52. portacode/static/js/test-ntp-clock.html +63 -0
  53. portacode/static/js/utils/ntp-clock.js +232 -0
  54. portacode/utils/NTP_ARCHITECTURE.md +136 -0
  55. portacode/utils/__init__.py +1 -0
  56. portacode/utils/diff_apply.py +456 -0
  57. portacode/utils/diff_renderer.py +371 -0
  58. portacode/utils/ntp_clock.py +65 -0
  59. portacode-1.4.11.dev1.dist-info/METADATA +298 -0
  60. portacode-1.4.11.dev1.dist-info/RECORD +97 -0
  61. {portacode-0.3.19.dev4.dist-info → portacode-1.4.11.dev1.dist-info}/WHEEL +1 -1
  62. portacode-1.4.11.dev1.dist-info/top_level.txt +3 -0
  63. test_modules/README.md +296 -0
  64. test_modules/__init__.py +1 -0
  65. test_modules/test_device_online.py +44 -0
  66. test_modules/test_file_operations.py +743 -0
  67. test_modules/test_git_status_ui.py +370 -0
  68. test_modules/test_login_flow.py +50 -0
  69. test_modules/test_navigate_testing_folder.py +361 -0
  70. test_modules/test_play_store_screenshots.py +294 -0
  71. test_modules/test_terminal_buffer_performance.py +261 -0
  72. test_modules/test_terminal_interaction.py +80 -0
  73. test_modules/test_terminal_loading_race_condition.py +95 -0
  74. test_modules/test_terminal_start.py +56 -0
  75. testing_framework/.env.example +21 -0
  76. testing_framework/README.md +334 -0
  77. testing_framework/__init__.py +17 -0
  78. testing_framework/cli.py +326 -0
  79. testing_framework/core/__init__.py +1 -0
  80. testing_framework/core/base_test.py +336 -0
  81. testing_framework/core/cli_manager.py +177 -0
  82. testing_framework/core/hierarchical_runner.py +577 -0
  83. testing_framework/core/playwright_manager.py +520 -0
  84. testing_framework/core/runner.py +447 -0
  85. testing_framework/core/shared_cli_manager.py +234 -0
  86. testing_framework/core/test_discovery.py +112 -0
  87. testing_framework/requirements.txt +12 -0
  88. portacode-0.3.19.dev4.dist-info/METADATA +0 -241
  89. portacode-0.3.19.dev4.dist-info/RECORD +0 -30
  90. portacode-0.3.19.dev4.dist-info/top_level.txt +0 -1
  91. {portacode-0.3.19.dev4.dist-info → portacode-1.4.11.dev1.dist-info}/entry_points.txt +0 -0
  92. {portacode-0.3.19.dev4.dist-info → portacode-1.4.11.dev1.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
+ ]