forgexa-cli 1.13.6__tar.gz → 1.14.0__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.13.6 → forgexa_cli-1.14.0}/PKG-INFO +1 -1
- {forgexa_cli-1.13.6 → forgexa_cli-1.14.0}/forgexa_cli/__init__.py +1 -1
- {forgexa_cli-1.13.6 → forgexa_cli-1.14.0}/forgexa_cli/daemon.py +81 -3
- {forgexa_cli-1.13.6 → forgexa_cli-1.14.0}/forgexa_cli.egg-info/PKG-INFO +1 -1
- {forgexa_cli-1.13.6 → forgexa_cli-1.14.0}/pyproject.toml +1 -1
- {forgexa_cli-1.13.6 → forgexa_cli-1.14.0}/README.md +0 -0
- {forgexa_cli-1.13.6 → forgexa_cli-1.14.0}/forgexa_cli/_build_config.py +0 -0
- {forgexa_cli-1.13.6 → forgexa_cli-1.14.0}/forgexa_cli/main.py +0 -0
- {forgexa_cli-1.13.6 → forgexa_cli-1.14.0}/forgexa_cli/py.typed +0 -0
- {forgexa_cli-1.13.6 → forgexa_cli-1.14.0}/forgexa_cli.egg-info/SOURCES.txt +0 -0
- {forgexa_cli-1.13.6 → forgexa_cli-1.14.0}/forgexa_cli.egg-info/dependency_links.txt +0 -0
- {forgexa_cli-1.13.6 → forgexa_cli-1.14.0}/forgexa_cli.egg-info/entry_points.txt +0 -0
- {forgexa_cli-1.13.6 → forgexa_cli-1.14.0}/forgexa_cli.egg-info/requires.txt +0 -0
- {forgexa_cli-1.13.6 → forgexa_cli-1.14.0}/forgexa_cli.egg-info/top_level.txt +0 -0
- {forgexa_cli-1.13.6 → forgexa_cli-1.14.0}/setup.cfg +0 -0
- {forgexa_cli-1.13.6 → forgexa_cli-1.14.0}/tests/test_auth_and_runtime_commands.py +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""forgexa-cli — Forgexa command-line client."""
|
|
2
|
-
__version__ = "1.
|
|
2
|
+
__version__ = "1.14.0"
|
|
@@ -523,7 +523,7 @@ except (ImportError, ModuleNotFoundError):
|
|
|
523
523
|
# DAEMON_VERSION is the protocol/logic version of the daemon code.
|
|
524
524
|
# Kept in sync with pyproject.toml version via bump-version.sh.
|
|
525
525
|
# CLIENT_TYPE identifies which packaging/distribution this daemon runs in.
|
|
526
|
-
DAEMON_VERSION = "1.
|
|
526
|
+
DAEMON_VERSION = "1.14.0"
|
|
527
527
|
|
|
528
528
|
|
|
529
529
|
def _detect_client_type() -> str:
|
|
@@ -6611,6 +6611,39 @@ class RuntimeDaemon:
|
|
|
6611
6611
|
if result.status == "success" and task.node_type == "fix":
|
|
6612
6612
|
await self._collect_bugfix_artifacts(workspace_path, task, result)
|
|
6613
6613
|
|
|
6614
|
+
# 4.9 For review/coding/testing nodes: attach updated analysis.json inline
|
|
6615
|
+
# so the backend always receives the latest snapshot even if push fails.
|
|
6616
|
+
# This closes the mirror-sync gap: review agents update analysis.json
|
|
6617
|
+
# (open_risks, phase_handoffs) and this step ensures the content reaches
|
|
6618
|
+
# runtimes.py as an inline artifact regardless of push success.
|
|
6619
|
+
if result.status == "success" and task.node_type in ("review", "coding", "testing"):
|
|
6620
|
+
analysis_dir = (
|
|
6621
|
+
(task.input_data or {}).get("analysis_output_dir", "")
|
|
6622
|
+
or ""
|
|
6623
|
+
).replace("\\", "/").lstrip("./").rstrip("/")
|
|
6624
|
+
if analysis_dir:
|
|
6625
|
+
_aj_path = workspace_path / analysis_dir / "analysis.json"
|
|
6626
|
+
if _aj_path.exists() and _aj_path.stat().st_size > 0:
|
|
6627
|
+
try:
|
|
6628
|
+
_aj_rel = str(_aj_path.relative_to(workspace_path)).replace("\\", "/")
|
|
6629
|
+
_existing_paths = {a.get("path", "").replace("\\", "/") for a in result.artifacts}
|
|
6630
|
+
if _aj_rel not in _existing_paths:
|
|
6631
|
+
_aj_content = _aj_path.read_text(encoding="utf-8", errors="replace")
|
|
6632
|
+
result.artifacts.append({
|
|
6633
|
+
"path": _aj_rel,
|
|
6634
|
+
"content": _aj_content,
|
|
6635
|
+
"type": "application/json",
|
|
6636
|
+
})
|
|
6637
|
+
logger.debug(
|
|
6638
|
+
"Task %s (%s): attached updated analysis.json inline (%d bytes)",
|
|
6639
|
+
task.task_id, task.node_type, len(_aj_content),
|
|
6640
|
+
)
|
|
6641
|
+
except Exception as _aj_err:
|
|
6642
|
+
logger.debug(
|
|
6643
|
+
"Task %s: could not attach analysis.json artifact: %s",
|
|
6644
|
+
task.task_id, _aj_err,
|
|
6645
|
+
)
|
|
6646
|
+
|
|
6614
6647
|
# 5. Auto-commit and push if changes exist
|
|
6615
6648
|
if result.status == "success":
|
|
6616
6649
|
commit_result = await self._auto_commit(workspace_path, task)
|
|
@@ -6791,9 +6824,31 @@ class RuntimeDaemon:
|
|
|
6791
6824
|
json_path = base / "analysis.json"
|
|
6792
6825
|
if json_path.exists() and json_path.stat().st_size > 0:
|
|
6793
6826
|
try:
|
|
6794
|
-
_json.loads(json_path.read_text(encoding="utf-8"))
|
|
6827
|
+
_analysis_data = _json.loads(json_path.read_text(encoding="utf-8"))
|
|
6795
6828
|
except _json.JSONDecodeError as e:
|
|
6796
6829
|
issues.append(f"analysis.json is not valid JSON: {e}")
|
|
6830
|
+
_analysis_data = None
|
|
6831
|
+
# Task 6: validate phase_handoffs and open_risks fields
|
|
6832
|
+
if _analysis_data is not None:
|
|
6833
|
+
if "open_risks" not in _analysis_data:
|
|
6834
|
+
issues.append(
|
|
6835
|
+
"analysis.json missing 'open_risks' field "
|
|
6836
|
+
"(expected empty array [] at minimum)"
|
|
6837
|
+
)
|
|
6838
|
+
handoffs = _analysis_data.get("phase_handoffs")
|
|
6839
|
+
if not handoffs or not isinstance(handoffs, list):
|
|
6840
|
+
issues.append(
|
|
6841
|
+
"analysis.json missing 'phase_handoffs' array "
|
|
6842
|
+
"(must contain at least one entry with phase='analysis')"
|
|
6843
|
+
)
|
|
6844
|
+
elif not any(
|
|
6845
|
+
isinstance(h, dict) and h.get("phase") == "analysis"
|
|
6846
|
+
for h in handoffs
|
|
6847
|
+
):
|
|
6848
|
+
issues.append(
|
|
6849
|
+
"analysis.json phase_handoffs has no entry "
|
|
6850
|
+
"with phase='analysis'"
|
|
6851
|
+
)
|
|
6797
6852
|
|
|
6798
6853
|
# Validate test-intent.json if required by this type
|
|
6799
6854
|
if "test-intent.json" in required_files:
|
|
@@ -6848,7 +6903,7 @@ class RuntimeDaemon:
|
|
|
6848
6903
|
elif design_path.stat().st_size == 0:
|
|
6849
6904
|
issues.append(f"Design document is empty: {doc_dir}/design.md")
|
|
6850
6905
|
|
|
6851
|
-
elif node_type in ("coding", "testing", "fix"):
|
|
6906
|
+
elif node_type in ("coding", "testing", "fix", "review"):
|
|
6852
6907
|
# Syntax-check modified Python and JS/TS files
|
|
6853
6908
|
for f in result.files_changed:
|
|
6854
6909
|
fpath = workspace_path / f
|
|
@@ -6871,6 +6926,29 @@ class RuntimeDaemon:
|
|
|
6871
6926
|
f"bracket imbalance (open={opens}, close={closes})"
|
|
6872
6927
|
)
|
|
6873
6928
|
|
|
6929
|
+
# Task 6 (P6 fix): warning-level phase_handoffs check for non-analysis
|
|
6930
|
+
# nodes. These nodes SHOULD append a phase_handoffs entry to analysis.json
|
|
6931
|
+
# after completion. Missing entry is NOT blocking (agent may have skipped
|
|
6932
|
+
# WRITEBACK) but is logged for observability and instructions iteration.
|
|
6933
|
+
_ao_dir = (task.input_data or {}).get("analysis_output_dir", "")
|
|
6934
|
+
if _ao_dir:
|
|
6935
|
+
_aj_p = workspace_path / _ao_dir.lstrip("./").rstrip("/").replace("\\", "/") / "analysis.json"
|
|
6936
|
+
if _aj_p.exists() and _aj_p.stat().st_size > 0:
|
|
6937
|
+
try:
|
|
6938
|
+
_aj_d = _json.loads(_aj_p.read_text(encoding="utf-8"))
|
|
6939
|
+
_handoffs = _aj_d.get("phase_handoffs") or []
|
|
6940
|
+
if not any(
|
|
6941
|
+
isinstance(h, dict) and h.get("phase") == node_type
|
|
6942
|
+
for h in _handoffs
|
|
6943
|
+
):
|
|
6944
|
+
logger.info(
|
|
6945
|
+
"Node %s (type=%s) did not append phase_handoffs "
|
|
6946
|
+
"entry to analysis.json — agent may have skipped WRITEBACK",
|
|
6947
|
+
task.task_id, node_type,
|
|
6948
|
+
)
|
|
6949
|
+
except (_json.JSONDecodeError, UnicodeDecodeError):
|
|
6950
|
+
pass # analysis.json corrupt — already flagged elsewhere
|
|
6951
|
+
|
|
6874
6952
|
# Testing-specific: validate structured test assets
|
|
6875
6953
|
if node_type == "testing":
|
|
6876
6954
|
# Determine which checks to run for this requirement type.
|
|
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
|