From 896b4523d745c356485b1674156d6a07d7fba9d7 Mon Sep 17 00:00:00 2001 From: Girish Redekar Date: Sat, 28 Feb 2026 16:39:10 -0800 Subject: [PATCH] fix: add missing CLAUDE.md REST API routes in web server When Tauri invoke fails (e.g., paths with spaces/special chars on iCloud Drive), the frontend falls back to REST API mode. The CLAUDE.md endpoints were mapped in apiAdapter.ts but had no corresponding handlers in web_server.rs, causing the fallback to also fail with "Failed to load CLAUDE.md files". Adds three GET route handlers: - /api/claude-md?projectPath=... (find CLAUDE.md files) - /api/claude-md/read?filePath=... (read a CLAUDE.md file) - /api/claude-md/save?filePath=...&content=... (save a CLAUDE.md file) Fixes #444 Co-Authored-By: Claude Opus 4.6 --- src-tauri/src/web_server.rs | 56 ++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/src-tauri/src/web_server.rs b/src-tauri/src/web_server.rs index 01a28436f..fdd3fa0f1 100644 --- a/src-tauri/src/web_server.rs +++ b/src-tauri/src/web_server.rs @@ -1,7 +1,7 @@ use axum::extract::ws::{Message, WebSocket}; use axum::http::Method; use axum::{ - extract::{Path, State as AxumState, WebSocketUpgrade}, + extract::{Path, Query, State as AxumState, WebSocketUpgrade}, response::{Html, Json, Response}, routing::get, Router, @@ -10,6 +10,7 @@ use chrono; use futures_util::{SinkExt, StreamExt}; use serde::{Deserialize, Serialize}; use serde_json::json; +use std::collections::HashMap; use std::net::SocketAddr; use std::sync::Arc; use tokio::net::TcpListener; @@ -19,6 +20,9 @@ use tower_http::services::ServeDir; use which; use crate::commands; +use crate::commands::claude::{ + find_claude_md_files, read_claude_md_file, save_claude_md_file, ClaudeMdFile, +}; // Find Claude binary for web mode - use bundled binary first fn find_claude_binary_web() -> Result { @@ -766,6 +770,52 @@ async fn send_to_session(state: &AppState, session_id: &str, message: String) { } } +/// Find CLAUDE.md files in a project directory +async fn find_claude_md_files_handler( + Query(params): Query>, +) -> Json>> { + let project_path = match params.get("projectPath") { + Some(p) => p.clone(), + None => return Json(ApiResponse::error("Missing projectPath parameter".to_string())), + }; + match find_claude_md_files(project_path).await { + Ok(files) => Json(ApiResponse::success(files)), + Err(e) => Json(ApiResponse::error(e)), + } +} + +/// Read a specific CLAUDE.md file +async fn read_claude_md_file_handler( + Query(params): Query>, +) -> Json> { + let file_path = match params.get("filePath") { + Some(p) => p.clone(), + None => return Json(ApiResponse::error("Missing filePath parameter".to_string())), + }; + match read_claude_md_file(file_path).await { + Ok(content) => Json(ApiResponse::success(content)), + Err(e) => Json(ApiResponse::error(e)), + } +} + +/// Save a CLAUDE.md file +async fn save_claude_md_file_handler( + Query(params): Query>, +) -> Json> { + let file_path = match params.get("filePath") { + Some(p) => p.clone(), + None => return Json(ApiResponse::error("Missing filePath parameter".to_string())), + }; + let content = match params.get("content") { + Some(c) => c.clone(), + None => return Json(ApiResponse::error("Missing content parameter".to_string())), + }; + match save_claude_md_file(file_path, content).await { + Ok(msg) => Json(ApiResponse::success(msg)), + Err(e) => Json(ApiResponse::error(e)), + } +} + /// Create the web server pub async fn create_web_server(port: u16) -> Result<(), Box> { let state = AppState { @@ -802,6 +852,10 @@ pub async fn create_web_server(port: u16) -> Result<(), Box