forgexa-cli 1.22.3__tar.gz → 1.22.5__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 (21) hide show
  1. {forgexa_cli-1.22.3 → forgexa_cli-1.22.5}/PKG-INFO +1 -1
  2. {forgexa_cli-1.22.3 → forgexa_cli-1.22.5}/forgexa_cli/__init__.py +1 -1
  3. {forgexa_cli-1.22.3 → forgexa_cli-1.22.5}/forgexa_cli/daemon.py +54 -6
  4. {forgexa_cli-1.22.3 → forgexa_cli-1.22.5}/forgexa_cli.egg-info/PKG-INFO +1 -1
  5. {forgexa_cli-1.22.3 → forgexa_cli-1.22.5}/pyproject.toml +1 -1
  6. {forgexa_cli-1.22.3 → forgexa_cli-1.22.5}/README.md +0 -0
  7. {forgexa_cli-1.22.3 → forgexa_cli-1.22.5}/forgexa_cli/_build_config.py +0 -0
  8. {forgexa_cli-1.22.3 → forgexa_cli-1.22.5}/forgexa_cli/autoupgrade.py +0 -0
  9. {forgexa_cli-1.22.3 → forgexa_cli-1.22.5}/forgexa_cli/main.py +0 -0
  10. {forgexa_cli-1.22.3 → forgexa_cli-1.22.5}/forgexa_cli/py.typed +0 -0
  11. {forgexa_cli-1.22.3 → forgexa_cli-1.22.5}/forgexa_cli.egg-info/SOURCES.txt +0 -0
  12. {forgexa_cli-1.22.3 → forgexa_cli-1.22.5}/forgexa_cli.egg-info/dependency_links.txt +0 -0
  13. {forgexa_cli-1.22.3 → forgexa_cli-1.22.5}/forgexa_cli.egg-info/entry_points.txt +0 -0
  14. {forgexa_cli-1.22.3 → forgexa_cli-1.22.5}/forgexa_cli.egg-info/requires.txt +0 -0
  15. {forgexa_cli-1.22.3 → forgexa_cli-1.22.5}/forgexa_cli.egg-info/top_level.txt +0 -0
  16. {forgexa_cli-1.22.3 → forgexa_cli-1.22.5}/setup.cfg +0 -0
  17. {forgexa_cli-1.22.3 → forgexa_cli-1.22.5}/tests/test_auth_and_runtime_commands.py +0 -0
  18. {forgexa_cli-1.22.3 → forgexa_cli-1.22.5}/tests/test_autoupgrade.py +0 -0
  19. {forgexa_cli-1.22.3 → forgexa_cli-1.22.5}/tests/test_check_command.py +0 -0
  20. {forgexa_cli-1.22.3 → forgexa_cli-1.22.5}/tests/test_silent_install.py +0 -0
  21. {forgexa_cli-1.22.3 → forgexa_cli-1.22.5}/tests/test_upgrade_observability.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: forgexa-cli
3
- Version: 1.22.3
3
+ Version: 1.22.5
4
4
  Summary: Forgexa CLI — command-line client and AI agent runtime for the Forgexa platform
5
5
  Author-email: Jason Sun <dev.winds@gmail.com>
6
6
  License-Expression: MIT
@@ -1,2 +1,2 @@
1
1
  """forgexa-cli — Forgexa command-line client."""
2
- __version__ = "1.22.3"
2
+ __version__ = "1.22.5"
@@ -555,7 +555,7 @@ except (ImportError, ModuleNotFoundError):
555
555
  # DAEMON_VERSION is the protocol/logic version of the daemon code.
556
556
  # Kept in sync with pyproject.toml version via bump-version.sh.
557
557
  # CLIENT_TYPE identifies which packaging/distribution this daemon runs in.
558
- DAEMON_VERSION = "1.22.3"
558
+ DAEMON_VERSION = "1.22.5"
559
559
 
560
560
 
561
561
  def _detect_client_type() -> str:
@@ -2617,8 +2617,18 @@ class WorkspaceManager:
2617
2617
 
2618
2618
  async def _remove_broken_worktree(self, main_repo: Path, ws_path: Path, workspace_key: str):
2619
2619
  """Remove a broken worktree directory and clean up stale worktree refs."""
2620
- # Try to prune from main repo first
2620
+ # `git worktree prune` keeps an entry while its directory exists, even
2621
+ # when that directory's .git pointer is corrupt.
2622
+ self._safe_rmtree(ws_path)
2623
+
2621
2624
  if main_repo.exists():
2625
+ # Prune also preserves explicitly locked worktrees, even after
2626
+ # their directories disappear. Unlock before pruning so a stale
2627
+ # registration cannot keep its branch ref occupied forever.
2628
+ try:
2629
+ await self._git("worktree", "unlock", str(ws_path), cwd=main_repo)
2630
+ except RuntimeError:
2631
+ pass
2622
2632
  try:
2623
2633
  await self._git("worktree", "prune", cwd=main_repo)
2624
2634
  except RuntimeError:
@@ -2627,9 +2637,6 @@ class WorkspaceManager:
2627
2637
  wt_ref = main_repo / ".git" / "worktrees" / workspace_key
2628
2638
  if wt_ref.exists():
2629
2639
  self._safe_rmtree(wt_ref)
2630
- # Remove the broken worktree directory using _safe_rmtree so that
2631
- # read-only git objects on Windows are properly handled.
2632
- self._safe_rmtree(ws_path)
2633
2640
 
2634
2641
  async def _safe_rmtree_main(self, main_repo: Path) -> None:
2635
2642
  """Remove _main repo, first evicting all linked worktrees to prevent orphans.
@@ -2677,8 +2684,10 @@ class WorkspaceManager:
2677
2684
  self,
2678
2685
  main_repo: Path,
2679
2686
  branch_name: str,
2687
+ *,
2688
+ healthy_only: bool = True,
2680
2689
  ) -> Path | None:
2681
- """Return an existing healthy linked worktree already on branch_name."""
2690
+ """Return a linked worktree already on branch_name."""
2682
2691
  try:
2683
2692
  raw = await self._git("worktree", "list", "--porcelain", cwd=main_repo)
2684
2693
  except RuntimeError:
@@ -2704,6 +2713,8 @@ class WorkspaceManager:
2704
2713
  for candidate_path, candidate_branch in entries:
2705
2714
  if candidate_path == main_repo or candidate_branch != target_ref:
2706
2715
  continue
2716
+ if not healthy_only:
2717
+ return candidate_path
2707
2718
  if candidate_path.exists() and await self._is_healthy_worktree(candidate_path, main_repo=main_repo):
2708
2719
  return candidate_path
2709
2720
 
@@ -3827,6 +3838,43 @@ class WorkspaceManager:
3827
3838
  cwd=main_repo,
3828
3839
  )
3829
3840
  except Exception as exc:
3841
+ existing_branch_worktree = await self._find_existing_worktree_for_branch(
3842
+ main_repo, branch_name,
3843
+ )
3844
+ if existing_branch_worktree is not None:
3845
+ logger.info(
3846
+ "Fresh start reusing worktree %s after resetting %s to origin/%s",
3847
+ existing_branch_worktree, branch_name, default_branch,
3848
+ )
3849
+ await self._git(
3850
+ "checkout", "-B", branch_name, f"origin/{default_branch}",
3851
+ cwd=existing_branch_worktree,
3852
+ )
3853
+ return existing_branch_worktree
3854
+
3855
+ stale_branch_worktree = await self._find_existing_worktree_for_branch(
3856
+ main_repo, branch_name, healthy_only=False,
3857
+ )
3858
+ if stale_branch_worktree is not None:
3859
+ logger.warning(
3860
+ "Fresh start found broken worktree %s holding branch %s; removing it before retrying",
3861
+ stale_branch_worktree, branch_name,
3862
+ )
3863
+ try:
3864
+ await self._remove_broken_worktree(
3865
+ main_repo, stale_branch_worktree, stale_branch_worktree.name,
3866
+ )
3867
+ await self._git(
3868
+ "branch", "-f", branch_name, f"origin/{default_branch}",
3869
+ cwd=main_repo,
3870
+ )
3871
+ await self._git(
3872
+ "worktree", "add", str(ws_path), branch_name,
3873
+ cwd=main_repo,
3874
+ )
3875
+ return ws_path
3876
+ except Exception as recovery_exc:
3877
+ exc = recovery_exc
3830
3878
  self._safe_rmtree(ws_path)
3831
3879
  raise RuntimeError(
3832
3880
  f"Failed to create clean fresh worktree for branch '{branch_name}'. "
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: forgexa-cli
3
- Version: 1.22.3
3
+ Version: 1.22.5
4
4
  Summary: Forgexa CLI — command-line client and AI agent runtime for the Forgexa platform
5
5
  Author-email: Jason Sun <dev.winds@gmail.com>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "forgexa-cli"
3
- version = "1.22.3"
3
+ version = "1.22.5"
4
4
  description = "Forgexa CLI — command-line client and AI agent runtime for the Forgexa platform"
5
5
  requires-python = ">=3.9"
6
6
  license = "MIT"
File without changes
File without changes