portacode 0.3.20.dev1__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.
Files changed (76) hide show
  1. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/PKG-INFO +1 -1
  2. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/_version.py +2 -2
  3. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/connection/handlers/project_state/git_manager.py +20 -3
  4. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/connection/handlers/project_state/handlers.py +21 -0
  5. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/connection/handlers/project_state/manager.py +15 -1
  6. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode.egg-info/PKG-INFO +1 -1
  7. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/.claude/agents/communication-manager.md +0 -0
  8. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/.claude/settings.local.json +0 -0
  9. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/.gitignore +0 -0
  10. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/.gitmodules +0 -0
  11. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/LICENSE +0 -0
  12. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/MANIFEST.in +0 -0
  13. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/Makefile +0 -0
  14. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/README.md +0 -0
  15. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/backup.sh +0 -0
  16. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/docker-compose.yaml +0 -0
  17. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/README.md +0 -0
  18. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/__init__.py +0 -0
  19. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/__main__.py +0 -0
  20. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/cli.py +0 -0
  21. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/connection/README.md +0 -0
  22. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/connection/__init__.py +0 -0
  23. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/connection/client.py +0 -0
  24. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/connection/handlers/README.md +0 -0
  25. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/connection/handlers/WEBSOCKET_PROTOCOL.md +0 -0
  26. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/connection/handlers/__init__.py +0 -0
  27. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/connection/handlers/base.py +0 -0
  28. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/connection/handlers/file_handlers.py +0 -0
  29. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/connection/handlers/project_state/README.md +0 -0
  30. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/connection/handlers/project_state/__init__.py +0 -0
  31. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/connection/handlers/project_state/file_system_watcher.py +0 -0
  32. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/connection/handlers/project_state/models.py +0 -0
  33. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/connection/handlers/project_state/utils.py +0 -0
  34. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/connection/handlers/project_state_handlers.py +0 -0
  35. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/connection/handlers/registry.py +0 -0
  36. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/connection/handlers/session.py +0 -0
  37. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/connection/handlers/system_handlers.py +0 -0
  38. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/connection/handlers/tab_factory.py +0 -0
  39. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/connection/handlers/terminal_handlers.py +0 -0
  40. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/connection/multiplex.py +0 -0
  41. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/connection/terminal.py +0 -0
  42. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/data.py +0 -0
  43. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/keypair.py +0 -0
  44. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode/service.py +0 -0
  45. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode.egg-info/SOURCES.txt +0 -0
  46. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode.egg-info/dependency_links.txt +0 -0
  47. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode.egg-info/entry_points.txt +0 -0
  48. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode.egg-info/requires.txt +0 -0
  49. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/portacode.egg-info/top_level.txt +0 -0
  50. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/pyproject.toml +0 -0
  51. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/restore.sh +0 -0
  52. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/run_tests.py +0 -0
  53. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/setup.cfg +0 -0
  54. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/setup.py +0 -0
  55. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/test.sh +0 -0
  56. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/test_modules/README.md +0 -0
  57. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/test_modules/__init__.py +0 -0
  58. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/test_modules/test_device_online.py +0 -0
  59. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/test_modules/test_file_operations.py +0 -0
  60. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/test_modules/test_login_flow.py +0 -0
  61. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/test_modules/test_navigate_testing_folder.py +0 -0
  62. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/test_modules/test_terminal_interaction.py +0 -0
  63. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/test_modules/test_terminal_start.py +0 -0
  64. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/testing_framework/.env.example +0 -0
  65. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/testing_framework/README.md +0 -0
  66. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/testing_framework/__init__.py +0 -0
  67. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/testing_framework/cli.py +0 -0
  68. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/testing_framework/core/__init__.py +0 -0
  69. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/testing_framework/core/base_test.py +0 -0
  70. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/testing_framework/core/cli_manager.py +0 -0
  71. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/testing_framework/core/hierarchical_runner.py +0 -0
  72. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/testing_framework/core/playwright_manager.py +0 -0
  73. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/testing_framework/core/runner.py +0 -0
  74. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/testing_framework/core/shared_cli_manager.py +0 -0
  75. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/testing_framework/core/test_discovery.py +0 -0
  76. {portacode-0.3.20.dev1 → portacode-0.3.20.dev2}/testing_framework/requirements.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: portacode
3
- Version: 0.3.20.dev1
3
+ Version: 0.3.20.dev2
4
4
  Summary: Portacode CLI client and SDK
5
5
  Home-page: https://github.com/portacode/portacode
6
6
  Author: Meena Erian
@@ -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.dev1'
21
- __version_tuple__ = version_tuple = (0, 3, 20, 'dev1')
20
+ __version__ = version = '0.3.20.dev2'
21
+ __version_tuple__ = version_tuple = (0, 3, 20, 'dev2')
@@ -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
- # Reset the file from HEAD (unstage)
1079
- self.repo.git.restore('--staged', rel_path)
1080
- logger.info("Successfully unstaged file: %s", rel_path)
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 {
@@ -887,10 +887,24 @@ class ProjectStateManager:
887
887
  logger.info("🔍 [TRACE] _send_project_state_update called for session: %s", project_state.client_session_id)
888
888
 
889
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
+
890
904
  current_state_signature = {
891
905
  "git_branch": project_state.git_branch,
892
906
  "git_status_summary": project_state.git_status_summary,
893
- "git_detailed_status": str(project_state.git_detailed_status) if project_state.git_detailed_status else None,
907
+ "git_detailed_status": git_detailed_signature,
894
908
  "open_tabs": tuple((tab.tab_id, tab.tab_type, tab.title) for tab in project_state.open_tabs.values()),
895
909
  "active_tab": project_state.active_tab.tab_id if project_state.active_tab else None,
896
910
  "items_count": len(project_state.items),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: portacode
3
- Version: 0.3.20.dev1
3
+ Version: 0.3.20.dev2
4
4
  Summary: Portacode CLI client and SDK
5
5
  Home-page: https://github.com/portacode/portacode
6
6
  Author: Meena Erian
File without changes
File without changes