diff --git a/src/cmdforge/gui/pages/tools_page.py b/src/cmdforge/gui/pages/tools_page.py index 4633980..64ec6f0 100644 --- a/src/cmdforge/gui/pages/tools_page.py +++ b/src/cmdforge/gui/pages/tools_page.py @@ -46,8 +46,14 @@ def get_tool_publish_state(tool_name: str) -> Tuple[str, Optional[str]]: if not registry_hash: return ("local", None) - # Compute current hash (excluding hash fields) - current_hash = compute_config_hash(config) + # Compute hash the same way as publish: load Tool object and use to_dict() + # This ensures we compare the same normalized representation + tool = load_tool(tool_name) + if tool: + current_hash = compute_config_hash(tool.to_dict()) + else: + # Fallback to raw config if Tool can't be loaded + current_hash = compute_config_hash(config) if current_hash != registry_hash: return ("modified", registry_hash) @@ -473,15 +479,23 @@ class ToolsPage(QWidget): # Get status from registry status_data = client.get_my_tool_status(tool_name) new_status = status_data.get("status", "pending") + new_hash = status_data.get("config_hash") # Update local config config_data = yaml.safe_load(config_path.read_text()) or {} old_status = config_data.get("registry_status", "pending") + old_hash = config_data.get("registry_hash") + changed = False if old_status != new_status: config_data["registry_status"] = new_status - config_path.write_text(yaml.dump(config_data, default_flow_style=False, sort_keys=False)) + changed = True + if new_hash and old_hash != new_hash: + config_data["registry_hash"] = new_hash + changed = True + if changed: + config_path.write_text(yaml.dump(config_data, default_flow_style=False, sort_keys=False)) self.refresh() if new_status == "approved": self.main_window.show_status(f"Tool '{tool_name}' has been approved!") diff --git a/src/cmdforge/hash_utils.py b/src/cmdforge/hash_utils.py index 7bae348..11de7c7 100644 --- a/src/cmdforge/hash_utils.py +++ b/src/cmdforge/hash_utils.py @@ -26,7 +26,12 @@ def compute_config_hash(config: Dict[str, Any], exclude_fields: Optional[list] = Hash string in format "sha256:<64-char-hex>" """ if exclude_fields is None: - exclude_fields = ['published_hash', 'registry_hash'] + # Exclude registry metadata and publication metadata from hash + # These fields are added during publish but aren't part of tool content + exclude_fields = [ + 'published_hash', 'registry_hash', 'registry_status', # registry metadata + 'version', 'tags', # publication metadata (added by publish dialog) + ] # Create a copy without excluded fields config_copy = {k: v for k, v in config.items() if k not in exclude_fields}