portacode 0.3.20.dev0__tar.gz → 0.3.20.dev2__tar.gz
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.
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/PKG-INFO +1 -1
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/_version.py +2 -2
- portacode-0.3.20.dev2/portacode/connection/handlers/project_state/README.md +312 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/handlers/project_state/git_manager.py +20 -3
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/handlers/project_state/handlers.py +21 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/handlers/project_state/manager.py +76 -10
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode.egg-info/PKG-INFO +1 -1
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode.egg-info/SOURCES.txt +1 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/.claude/agents/communication-manager.md +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/.claude/settings.local.json +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/.gitignore +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/.gitmodules +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/LICENSE +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/MANIFEST.in +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/Makefile +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/README.md +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/backup.sh +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/docker-compose.yaml +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/README.md +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/__init__.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/__main__.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/cli.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/README.md +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/__init__.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/client.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/handlers/README.md +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/handlers/WEBSOCKET_PROTOCOL.md +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/handlers/__init__.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/handlers/base.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/handlers/file_handlers.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/handlers/project_state/__init__.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/handlers/project_state/file_system_watcher.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/handlers/project_state/models.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/handlers/project_state/utils.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/handlers/project_state_handlers.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/handlers/registry.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/handlers/session.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/handlers/system_handlers.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/handlers/tab_factory.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/handlers/terminal_handlers.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/multiplex.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/terminal.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/data.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/keypair.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/service.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode.egg-info/dependency_links.txt +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode.egg-info/entry_points.txt +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode.egg-info/requires.txt +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode.egg-info/top_level.txt +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/pyproject.toml +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/restore.sh +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/run_tests.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/setup.cfg +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/setup.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/test.sh +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/test_modules/README.md +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/test_modules/__init__.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/test_modules/test_device_online.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/test_modules/test_file_operations.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/test_modules/test_login_flow.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/test_modules/test_navigate_testing_folder.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/test_modules/test_terminal_interaction.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/test_modules/test_terminal_start.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/testing_framework/.env.example +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/testing_framework/README.md +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/testing_framework/__init__.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/testing_framework/cli.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/testing_framework/core/__init__.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/testing_framework/core/base_test.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/testing_framework/core/cli_manager.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/testing_framework/core/hierarchical_runner.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/testing_framework/core/playwright_manager.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/testing_framework/core/runner.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/testing_framework/core/shared_cli_manager.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/testing_framework/core/test_discovery.py +0 -0
- {portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/testing_framework/requirements.txt +0 -0
|
@@ -17,5 +17,5 @@ __version__: str
|
|
|
17
17
|
__version_tuple__: VERSION_TUPLE
|
|
18
18
|
version_tuple: VERSION_TUPLE
|
|
19
19
|
|
|
20
|
-
__version__ = version = '0.3.20.
|
|
21
|
-
__version_tuple__ = version_tuple = (0, 3, 20, '
|
|
20
|
+
__version__ = version = '0.3.20.dev2'
|
|
21
|
+
__version_tuple__ = version_tuple = (0, 3, 20, 'dev2')
|
|
@@ -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.
|
|
@@ -1075,9 +1075,26 @@ class GitManager:
|
|
|
1075
1075
|
# Convert to relative path from repo root
|
|
1076
1076
|
rel_path = os.path.relpath(file_path, self.repo.working_dir)
|
|
1077
1077
|
|
|
1078
|
-
#
|
|
1079
|
-
|
|
1080
|
-
|
|
1078
|
+
# Check if repository has any commits (HEAD exists)
|
|
1079
|
+
has_commits = False
|
|
1080
|
+
try:
|
|
1081
|
+
# Try to get HEAD commit to check if repository has commits
|
|
1082
|
+
self.repo.head.commit
|
|
1083
|
+
has_commits = True
|
|
1084
|
+
except Exception:
|
|
1085
|
+
logger.debug("Repository has no commits yet (no HEAD)")
|
|
1086
|
+
has_commits = False
|
|
1087
|
+
|
|
1088
|
+
if has_commits:
|
|
1089
|
+
# Repository has commits - use git restore --staged
|
|
1090
|
+
self.repo.git.restore('--staged', rel_path)
|
|
1091
|
+
logger.info("Successfully unstaged file using restore: %s", rel_path)
|
|
1092
|
+
else:
|
|
1093
|
+
# Repository has no commits - use git rm --cached
|
|
1094
|
+
# This handles the case where files are staged but no initial commit exists
|
|
1095
|
+
self.repo.git.rm('--cached', rel_path)
|
|
1096
|
+
logger.info("Successfully unstaged file using rm --cached (no HEAD): %s", rel_path)
|
|
1097
|
+
|
|
1081
1098
|
return True
|
|
1082
1099
|
|
|
1083
1100
|
except Exception as e:
|
|
@@ -395,6 +395,13 @@ class ProjectStateGitStageHandler(AsyncHandler):
|
|
|
395
395
|
project_state = manager.projects[source_client_session]
|
|
396
396
|
project_state.git_status_summary = git_manager.get_status_summary()
|
|
397
397
|
project_state.git_detailed_status = git_manager.get_detailed_status()
|
|
398
|
+
|
|
399
|
+
# Force clear the last signature to ensure update is sent
|
|
400
|
+
# This is necessary because git operations should always trigger client updates
|
|
401
|
+
if hasattr(project_state, '_last_sent_signature'):
|
|
402
|
+
logger.info("Forcing git stage update by clearing signature cache")
|
|
403
|
+
project_state._last_sent_signature = None
|
|
404
|
+
|
|
398
405
|
await manager._send_project_state_update(project_state, server_project_id)
|
|
399
406
|
|
|
400
407
|
return {
|
|
@@ -444,6 +451,13 @@ class ProjectStateGitUnstageHandler(AsyncHandler):
|
|
|
444
451
|
project_state = manager.projects[source_client_session]
|
|
445
452
|
project_state.git_status_summary = git_manager.get_status_summary()
|
|
446
453
|
project_state.git_detailed_status = git_manager.get_detailed_status()
|
|
454
|
+
|
|
455
|
+
# Force clear the last signature to ensure update is sent
|
|
456
|
+
# This is necessary because git operations should always trigger client updates
|
|
457
|
+
if hasattr(project_state, '_last_sent_signature'):
|
|
458
|
+
logger.info("Forcing git unstage update by clearing signature cache")
|
|
459
|
+
project_state._last_sent_signature = None
|
|
460
|
+
|
|
447
461
|
await manager._send_project_state_update(project_state, server_project_id)
|
|
448
462
|
|
|
449
463
|
return {
|
|
@@ -493,6 +507,13 @@ class ProjectStateGitRevertHandler(AsyncHandler):
|
|
|
493
507
|
project_state = manager.projects[source_client_session]
|
|
494
508
|
project_state.git_status_summary = git_manager.get_status_summary()
|
|
495
509
|
project_state.git_detailed_status = git_manager.get_detailed_status()
|
|
510
|
+
|
|
511
|
+
# Force clear the last signature to ensure update is sent
|
|
512
|
+
# This is necessary because git operations should always trigger client updates
|
|
513
|
+
if hasattr(project_state, '_last_sent_signature'):
|
|
514
|
+
logger.info("Forcing git revert update by clearing signature cache")
|
|
515
|
+
project_state._last_sent_signature = None
|
|
516
|
+
|
|
496
517
|
await manager._send_project_state_update(project_state, server_project_id)
|
|
497
518
|
|
|
498
519
|
return {
|
|
@@ -413,8 +413,23 @@ class ProjectStateManager:
|
|
|
413
413
|
# Update the monitored folder to expanded state
|
|
414
414
|
monitored_folder = self._find_monitored_folder(project_state, folder_path)
|
|
415
415
|
if not monitored_folder:
|
|
416
|
-
logger.
|
|
417
|
-
|
|
416
|
+
logger.warning("Monitored folder not found for path: %s. Attempting to add it...", folder_path)
|
|
417
|
+
|
|
418
|
+
# Check if the folder exists and is within the project
|
|
419
|
+
if not os.path.exists(folder_path) or not os.path.isdir(folder_path):
|
|
420
|
+
logger.error("Cannot expand folder - path does not exist or is not a directory: %s", folder_path)
|
|
421
|
+
return False
|
|
422
|
+
|
|
423
|
+
# Check if it's within the project root
|
|
424
|
+
if not folder_path.startswith(project_state.project_folder_path):
|
|
425
|
+
logger.error("Cannot expand folder - path is outside project root: %s", folder_path)
|
|
426
|
+
return False
|
|
427
|
+
|
|
428
|
+
# Add the folder to monitored_folders
|
|
429
|
+
logger.info("Adding missing folder to monitored_folders: %s", folder_path)
|
|
430
|
+
new_monitored = MonitoredFolder(folder_path=folder_path, is_expanded=False)
|
|
431
|
+
project_state.monitored_folders.append(new_monitored)
|
|
432
|
+
monitored_folder = new_monitored
|
|
418
433
|
|
|
419
434
|
logger.info("Found monitored folder: %s, current is_expanded: %s", monitored_folder.folder_path, monitored_folder.is_expanded)
|
|
420
435
|
monitored_folder.is_expanded = True
|
|
@@ -809,7 +824,11 @@ class ProjectStateManager:
|
|
|
809
824
|
else:
|
|
810
825
|
logger.info("🔍 [TRACE] ❌ No git manager found for session: %s", client_session_id)
|
|
811
826
|
|
|
812
|
-
#
|
|
827
|
+
# Detect and add new directories created in expanded folders to monitored_folders
|
|
828
|
+
logger.info("🔍 [TRACE] Detecting new directories in expanded folders...")
|
|
829
|
+
await self._detect_and_add_new_directories(project_state)
|
|
830
|
+
|
|
831
|
+
# Sync all dependent state (items, watchdog)
|
|
813
832
|
logger.info("🔍 [TRACE] Syncing all state with monitored folders...")
|
|
814
833
|
await self._sync_all_state_with_monitored_folders(project_state)
|
|
815
834
|
|
|
@@ -818,13 +837,46 @@ class ProjectStateManager:
|
|
|
818
837
|
await self._send_project_state_update(project_state)
|
|
819
838
|
|
|
820
839
|
async def _detect_and_add_new_directories(self, project_state: ProjectState):
|
|
821
|
-
"""Detect new directories in monitored folders and add them to monitoring."""
|
|
822
|
-
|
|
823
|
-
|
|
840
|
+
"""Detect new directories in expanded monitored folders and add them to monitoring."""
|
|
841
|
+
logger.info("🔍 [TRACE] _detect_and_add_new_directories called")
|
|
842
|
+
|
|
843
|
+
# Get currently expanded monitored folders
|
|
844
|
+
expanded_monitored_folders = [mf for mf in project_state.monitored_folders if mf.is_expanded]
|
|
845
|
+
logger.info("🔍 [TRACE] Found %d expanded monitored folders", len(expanded_monitored_folders))
|
|
824
846
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
847
|
+
existing_monitored_paths = {mf.folder_path for mf in project_state.monitored_folders}
|
|
848
|
+
new_folders_added = False
|
|
849
|
+
|
|
850
|
+
for monitored_folder in expanded_monitored_folders:
|
|
851
|
+
folder_path = monitored_folder.folder_path
|
|
852
|
+
logger.info("🔍 [TRACE] Scanning expanded folder for new directories: %s", folder_path)
|
|
853
|
+
|
|
854
|
+
if not os.path.exists(folder_path) or not os.path.isdir(folder_path):
|
|
855
|
+
logger.warning("🔍 [TRACE] Expanded folder no longer exists: %s", folder_path)
|
|
856
|
+
continue
|
|
857
|
+
|
|
858
|
+
try:
|
|
859
|
+
with os.scandir(folder_path) as entries:
|
|
860
|
+
for entry in entries:
|
|
861
|
+
if entry.is_dir() and entry.name != '.git':
|
|
862
|
+
if entry.path not in existing_monitored_paths:
|
|
863
|
+
logger.info("🔍 [TRACE] ✅ Found new directory to monitor: %s", entry.path)
|
|
864
|
+
# Add new directory as collapsed and loaded
|
|
865
|
+
new_monitored = MonitoredFolder(folder_path=entry.path, is_expanded=False)
|
|
866
|
+
project_state.monitored_folders.append(new_monitored)
|
|
867
|
+
existing_monitored_paths.add(entry.path)
|
|
868
|
+
new_folders_added = True
|
|
869
|
+
logger.info("🔍 [TRACE] Added new directory to monitored_folders: %s", entry.path)
|
|
870
|
+
else:
|
|
871
|
+
logger.debug("🔍 [TRACE] Directory already monitored: %s", entry.path)
|
|
872
|
+
|
|
873
|
+
except (OSError, PermissionError) as e:
|
|
874
|
+
logger.error("🔍 [TRACE] Error scanning expanded folder %s: %s", folder_path, e)
|
|
875
|
+
|
|
876
|
+
if new_folders_added:
|
|
877
|
+
logger.info("🔍 [TRACE] ✅ New directories were added to monitoring")
|
|
878
|
+
else:
|
|
879
|
+
logger.debug("🔍 [TRACE] No new directories found to add")
|
|
828
880
|
|
|
829
881
|
async def _reload_visible_structures(self, project_state: ProjectState):
|
|
830
882
|
"""Reload all visible structures with flattened items."""
|
|
@@ -835,10 +887,24 @@ class ProjectStateManager:
|
|
|
835
887
|
logger.info("🔍 [TRACE] _send_project_state_update called for session: %s", project_state.client_session_id)
|
|
836
888
|
|
|
837
889
|
# Create state signature for change detection
|
|
890
|
+
# Include detailed git file information to ensure staging/unstaging triggers updates
|
|
891
|
+
git_detailed_signature = None
|
|
892
|
+
if project_state.git_detailed_status:
|
|
893
|
+
# Create a more detailed signature that captures individual file staging states
|
|
894
|
+
staged_files = tuple(sorted([(f.file_abs_path, f.change_type) for f in project_state.git_detailed_status.staged_changes]))
|
|
895
|
+
unstaged_files = tuple(sorted([(f.file_abs_path, f.change_type) for f in project_state.git_detailed_status.unstaged_changes]))
|
|
896
|
+
untracked_files = tuple(sorted([f.file_abs_path for f in project_state.git_detailed_status.untracked_files]))
|
|
897
|
+
git_detailed_signature = {
|
|
898
|
+
"head_commit_hash": project_state.git_detailed_status.head_commit_hash,
|
|
899
|
+
"staged_files": staged_files,
|
|
900
|
+
"unstaged_files": unstaged_files,
|
|
901
|
+
"untracked_files": untracked_files
|
|
902
|
+
}
|
|
903
|
+
|
|
838
904
|
current_state_signature = {
|
|
839
905
|
"git_branch": project_state.git_branch,
|
|
840
906
|
"git_status_summary": project_state.git_status_summary,
|
|
841
|
-
"git_detailed_status":
|
|
907
|
+
"git_detailed_status": git_detailed_signature,
|
|
842
908
|
"open_tabs": tuple((tab.tab_id, tab.tab_type, tab.title) for tab in project_state.open_tabs.values()),
|
|
843
909
|
"active_tab": project_state.active_tab.tab_id if project_state.active_tab else None,
|
|
844
910
|
"items_count": len(project_state.items),
|
|
@@ -43,6 +43,7 @@ portacode/connection/handlers/session.py
|
|
|
43
43
|
portacode/connection/handlers/system_handlers.py
|
|
44
44
|
portacode/connection/handlers/tab_factory.py
|
|
45
45
|
portacode/connection/handlers/terminal_handlers.py
|
|
46
|
+
portacode/connection/handlers/project_state/README.md
|
|
46
47
|
portacode/connection/handlers/project_state/__init__.py
|
|
47
48
|
portacode/connection/handlers/project_state/file_system_watcher.py
|
|
48
49
|
portacode/connection/handlers/project_state/git_manager.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/handlers/WEBSOCKET_PROTOCOL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/handlers/file_handlers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/handlers/project_state/utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/handlers/system_handlers.py
RENAMED
|
File without changes
|
{portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/handlers/tab_factory.py
RENAMED
|
File without changes
|
{portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/portacode/connection/handlers/terminal_handlers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/test_modules/test_navigate_testing_folder.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/testing_framework/core/hierarchical_runner.py
RENAMED
|
File without changes
|
{portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/testing_framework/core/playwright_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
{portacode-0.3.20.dev0 → portacode-0.3.20.dev2}/testing_framework/core/shared_cli_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|