diff --git a/src/cmdforge/registry/app.py b/src/cmdforge/registry/app.py index f17888f..04971a8 100644 --- a/src/cmdforge/registry/app.py +++ b/src/cmdforge/registry/app.py @@ -2001,7 +2001,7 @@ def create_app() -> Flask: row = query_one( g.db, """ - SELECT name, version, moderation_status, config_hash, published_at + SELECT name, version, moderation_status, moderation_note, config_hash, published_at FROM tools WHERE owner = ? AND name = ? ORDER BY published_at DESC @@ -2013,15 +2013,19 @@ def create_app() -> Flask: if not row: return error_response("TOOL_NOT_FOUND", f"Tool '{name}' not found", 404) - return jsonify({ - "data": { - "name": row["name"], - "version": row["version"], - "status": row["moderation_status"], - "config_hash": row["config_hash"], - "published_at": row["published_at"], - } - }) + result = { + "name": row["name"], + "version": row["version"], + "status": row["moderation_status"], + "config_hash": row["config_hash"], + "published_at": row["published_at"], + } + + # Include feedback if status is changes_requested or rejected + if row["moderation_status"] in ("changes_requested", "rejected") and row["moderation_note"]: + result["feedback"] = row["moderation_note"] + + return jsonify({"data": result}) @app.route("/api/v1/tools///deprecate", methods=["POST"]) @require_token @@ -2665,6 +2669,41 @@ def create_app() -> Flask: return jsonify({"data": {"status": "rejected", "tool_id": tool_id}}) + @app.route("/api/v1/admin/tools//request-changes", methods=["POST"]) + @require_moderator + def admin_request_changes(tool_id: int) -> Response: + """Request changes from tool publisher before approval.""" + data = request.get_json() or {} + feedback = (data.get("feedback") or data.get("reason") or "").strip() + + if not feedback: + return error_response("VALIDATION_ERROR", "Feedback is required", 400) + + tool = query_one(g.db, "SELECT * FROM tools WHERE id = ?", [tool_id]) + if not tool: + return error_response("TOOL_NOT_FOUND", "Tool not found", 404) + + g.db.execute( + """ + UPDATE tools + SET moderation_status = 'changes_requested', + moderation_note = ?, + moderated_by = ?, + moderated_at = ? + WHERE id = ? + """, + [feedback, g.current_publisher["slug"], datetime.utcnow().isoformat(), tool_id], + ) + g.db.commit() + + log_audit("request_changes", "tool", str(tool_id), { + "tool": f"{tool['owner']}/{tool['name']}", + "version": tool["version"], + "feedback": feedback[:200], # Truncate for audit log + }) + + return jsonify({"data": {"status": "changes_requested", "tool_id": tool_id}}) + @app.route("/api/v1/admin/scrutiny", methods=["GET"]) @require_moderator def admin_scrutiny_audit() -> Response: diff --git a/src/cmdforge/web/templates/admin/pending.html b/src/cmdforge/web/templates/admin/pending.html index e65c7dd..d485b68 100644 --- a/src/cmdforge/web/templates/admin/pending.html +++ b/src/cmdforge/web/templates/admin/pending.html @@ -78,7 +78,8 @@ {{ tool.published_at[:10] if tool.published_at else 'Unknown' }} - + + @@ -193,11 +194,33 @@
+
+ + +