forgexa-cli 1.29.2__tar.gz → 1.29.4__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.
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/PKG-INFO +1 -1
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/forgexa_cli/__init__.py +1 -1
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/forgexa_cli/daemon.py +67 -3
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/forgexa_cli.egg-info/PKG-INFO +1 -1
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/pyproject.toml +1 -1
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/README.md +0 -0
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/forgexa_cli/_build_config.py +0 -0
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/forgexa_cli/_local_bind.py +0 -0
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/forgexa_cli/agent_core.py +0 -0
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/forgexa_cli/autoupgrade.py +0 -0
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/forgexa_cli/main.py +0 -0
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/forgexa_cli/py.typed +0 -0
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/forgexa_cli.egg-info/SOURCES.txt +0 -0
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/forgexa_cli.egg-info/dependency_links.txt +0 -0
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/forgexa_cli.egg-info/entry_points.txt +0 -0
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/forgexa_cli.egg-info/requires.txt +0 -0
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/forgexa_cli.egg-info/top_level.txt +0 -0
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/setup.cfg +0 -0
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/tests/test_auth_and_runtime_commands.py +0 -0
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/tests/test_autoupgrade.py +0 -0
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/tests/test_check_command.py +0 -0
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/tests/test_expiry_warnings_and_revoke.py +0 -0
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/tests/test_local_bind_commands.py +0 -0
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/tests/test_runtime_credentials.py +0 -0
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/tests/test_session_credentials.py +0 -0
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/tests/test_silent_install.py +0 -0
- {forgexa_cli-1.29.2 → forgexa_cli-1.29.4}/tests/test_upgrade_observability.py +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""forgexa-cli — Forgexa command-line client."""
|
|
2
|
-
__version__ = "1.29.
|
|
2
|
+
__version__ = "1.29.4"
|
|
@@ -910,7 +910,7 @@ except (ImportError, ModuleNotFoundError):
|
|
|
910
910
|
# DAEMON_VERSION is the protocol/logic version of the daemon code.
|
|
911
911
|
# Kept in sync with pyproject.toml version via bump-version.sh.
|
|
912
912
|
# CLIENT_TYPE identifies which packaging/distribution this daemon runs in.
|
|
913
|
-
DAEMON_VERSION = "1.29.
|
|
913
|
+
DAEMON_VERSION = "1.29.4"
|
|
914
914
|
|
|
915
915
|
|
|
916
916
|
def _detect_client_type() -> str:
|
|
@@ -3467,11 +3467,13 @@ class WorkspaceManager:
|
|
|
3467
3467
|
|
|
3468
3468
|
if ws_path.exists():
|
|
3469
3469
|
# Validate that this worktree is healthy (its .git file points to a
|
|
3470
|
-
# valid main repo
|
|
3470
|
+
# valid main repo and has no stale Git index lock. Worktrees break
|
|
3471
|
+
# when directories are moved or an interrupted Git process leaves
|
|
3472
|
+
# its index locked.
|
|
3471
3473
|
if not await self._is_healthy_worktree(
|
|
3472
3474
|
ws_path,
|
|
3473
3475
|
main_repo=main_repo,
|
|
3474
|
-
require_unlocked=
|
|
3476
|
+
require_unlocked=True,
|
|
3475
3477
|
):
|
|
3476
3478
|
logger.warning("Broken worktree detected at %s — removing and recreating", ws_path)
|
|
3477
3479
|
await self._remove_broken_worktree(main_repo, ws_path, workspace_key)
|
|
@@ -10098,6 +10100,13 @@ class RuntimeDaemon:
|
|
|
10098
10100
|
source_branch = input_ctx.get("source_branch", "")
|
|
10099
10101
|
base_integration_sha = input_ctx.get("base_integration_sha")
|
|
10100
10102
|
default_branch = input_ctx.get("default_branch", "main")
|
|
10103
|
+
requirement_key = str(input_ctx.get("requirement_key") or "").strip()
|
|
10104
|
+
if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]*", requirement_key):
|
|
10105
|
+
requirement_key = ""
|
|
10106
|
+
prior_requirement_source_shas = [
|
|
10107
|
+
sha for sha in input_ctx.get("prior_requirement_source_shas", [])
|
|
10108
|
+
if isinstance(sha, str) and re.fullmatch(r"[0-9a-fA-F]{40}", sha)
|
|
10109
|
+
]
|
|
10101
10110
|
agent_type = input_ctx.get("agent_type", "claude")
|
|
10102
10111
|
fallback_chain = input_ctx.get("fallback_chain", [])
|
|
10103
10112
|
timeout_seconds = min(int(input_ctx.get("timeout_seconds", 300)), 300)
|
|
@@ -10281,6 +10290,53 @@ class RuntimeDaemon:
|
|
|
10281
10290
|
except RuntimeError:
|
|
10282
10291
|
diff_u = ""
|
|
10283
10292
|
conflicted_files = [f.strip() for f in diff_u.strip().splitlines() if f.strip()]
|
|
10293
|
+
replayed_artifact_paths: list[str] = []
|
|
10294
|
+
|
|
10295
|
+
artifact_prefix = f"docs/requirements/{requirement_key}/"
|
|
10296
|
+
if (
|
|
10297
|
+
conflicted_files
|
|
10298
|
+
and requirement_key
|
|
10299
|
+
and prior_requirement_source_shas
|
|
10300
|
+
and all(path.startswith(artifact_prefix) for path in conflicted_files)
|
|
10301
|
+
):
|
|
10302
|
+
try:
|
|
10303
|
+
conflict_status = await git("status", "--porcelain", cwd=workspace_path)
|
|
10304
|
+
except RuntimeError:
|
|
10305
|
+
conflict_status = ""
|
|
10306
|
+
add_add_paths = {
|
|
10307
|
+
line[3:].strip()
|
|
10308
|
+
for line in conflict_status.splitlines()
|
|
10309
|
+
if line.startswith("AA ")
|
|
10310
|
+
}
|
|
10311
|
+
if all(path in add_add_paths for path in conflicted_files):
|
|
10312
|
+
for prior_source_sha in prior_requirement_source_shas:
|
|
10313
|
+
try:
|
|
10314
|
+
await git(
|
|
10315
|
+
"merge-base", "--is-ancestor", prior_source_sha, base_ref,
|
|
10316
|
+
cwd=workspace_path,
|
|
10317
|
+
)
|
|
10318
|
+
except RuntimeError:
|
|
10319
|
+
continue
|
|
10320
|
+
for conflict_path in conflicted_files:
|
|
10321
|
+
await git(
|
|
10322
|
+
"checkout", "--theirs", "--", conflict_path,
|
|
10323
|
+
cwd=workspace_path,
|
|
10324
|
+
)
|
|
10325
|
+
await git("add", "--", conflict_path, cwd=workspace_path)
|
|
10326
|
+
await git(
|
|
10327
|
+
"-c", f"user.name={_git_name}",
|
|
10328
|
+
"-c", f"user.email={_git_email}",
|
|
10329
|
+
"commit", "--no-edit",
|
|
10330
|
+
cwd=workspace_path,
|
|
10331
|
+
)
|
|
10332
|
+
replayed_artifact_paths = list(conflicted_files)
|
|
10333
|
+
conflicted_files = []
|
|
10334
|
+
merge_conflicted = False
|
|
10335
|
+
logger.info(
|
|
10336
|
+
"deployment_merge: job %s replaced %d replayed requirement artifacts",
|
|
10337
|
+
job_id, len(replayed_artifact_paths),
|
|
10338
|
+
)
|
|
10339
|
+
break
|
|
10284
10340
|
|
|
10285
10341
|
resolved_agent_id: str | None = None
|
|
10286
10342
|
if merge_conflicted or conflicted_files:
|
|
@@ -10590,6 +10646,9 @@ class RuntimeDaemon:
|
|
|
10590
10646
|
if resolved_agent_id:
|
|
10591
10647
|
outcome = "ai_resolved"
|
|
10592
10648
|
step_msg = f"AI resolved merge via {resolved_agent_id}"
|
|
10649
|
+
elif replayed_artifact_paths:
|
|
10650
|
+
outcome = "clean_merged"
|
|
10651
|
+
step_msg = "Replaced replayed requirement artifacts"
|
|
10593
10652
|
else:
|
|
10594
10653
|
outcome = "clean_merged"
|
|
10595
10654
|
step_msg = "Merge completed cleanly"
|
|
@@ -10611,6 +10670,11 @@ class RuntimeDaemon:
|
|
|
10611
10670
|
"merge_commit_sha": candidate_sha,
|
|
10612
10671
|
"base_integration_sha": base_ref,
|
|
10613
10672
|
"source_commit_sha": source_commit_sha,
|
|
10673
|
+
"ai_resolution_notes": (
|
|
10674
|
+
f"Replaced {len(replayed_artifact_paths)} replayed requirement artifacts "
|
|
10675
|
+
f"under {artifact_prefix} from the new source commit."
|
|
10676
|
+
if replayed_artifact_paths else None
|
|
10677
|
+
),
|
|
10614
10678
|
})
|
|
10615
10679
|
|
|
10616
10680
|
except Exception as exc:
|
|
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
|
|
File without changes
|