package api import ( "encoding/json" "net/http" "os" "github.com/muyue/muyue/internal/lsp" "github.com/muyue/muyue/internal/mcp" "github.com/muyue/muyue/internal/scanner" "github.com/muyue/muyue/internal/skills" "github.com/muyue/muyue/internal/version" ) func (s *Server) handleInfo(w http.ResponseWriter, r *http.Request) { writeJSON(w, map[string]interface{}{ "name": version.Name, "version": version.Version, "author": version.Author, }) } func (s *Server) handleSystem(w http.ResponseWriter, r *http.Request) { if s.scanResult == nil { s.scanResult = scanner.ScanSystem() } writeJSON(w, map[string]interface{}{ "system": s.scanResult.System, }) } func (s *Server) handleTools(w http.ResponseWriter, r *http.Request) { if s.scanResult == nil { s.scanResult = scanner.ScanSystem() } type toolInfo struct { Name string `json:"name"` Installed bool `json:"installed"` Version string `json:"version"` Path string `json:"path"` } tools := make([]toolInfo, len(s.scanResult.Tools)) for i, t := range s.scanResult.Tools { tools[i] = toolInfo{ Name: t.Name, Installed: t.Installed, Version: t.Version, Path: t.Path, } } writeJSON(w, map[string]interface{}{ "tools": tools, "total": len(tools), }) } func (s *Server) handleConfig(w http.ResponseWriter, r *http.Request) { if s.config == nil { writeError(w, "no config", http.StatusNotFound) return } writeJSON(w, map[string]interface{}{ "profile": s.config.Profile, "terminal": s.config.Terminal, "bmad": s.config.BMAD, }) } func (s *Server) handleProviders(w http.ResponseWriter, r *http.Request) { if s.config == nil { writeError(w, "no config", http.StatusNotFound) return } writeJSON(w, map[string]interface{}{ "providers": s.config.AI.Providers, }) } func (s *Server) handleSkills(w http.ResponseWriter, r *http.Request) { list, err := skills.List() if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } writeJSON(w, map[string]interface{}{ "skills": list, "count": len(list), }) } func (s *Server) handleLSP(w http.ResponseWriter, r *http.Request) { servers := lsp.ScanServers() writeJSON(w, map[string]interface{}{ "servers": servers, }) } func (s *Server) handleMCP(w http.ResponseWriter, r *http.Request) { servers := mcp.ScanServers() home, _ := os.UserHomeDir() editors := mcp.DetectInstalledEditors(home) statuses := mcp.GetAllStatuses() writeJSON(w, map[string]interface{}{ "servers": servers, "configured": true, "detected_editors": editors, "statuses": statuses, }) } func (s *Server) handleMCPConfigure(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { writeError(w, "POST only", http.StatusMethodNotAllowed) return } var body struct { Editor string `json:"editor,omitempty"` } if r.Body != nil { json.NewDecoder(r.Body).Decode(&body) } if body.Editor != "" { if err := mcp.ConfigureForEditor(s.config, body.Editor); err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } } else { if err := mcp.ConfigureAll(s.config); err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } } writeJSON(w, map[string]string{"status": "ok"}) } func (s *Server) handleMCPStatus(w http.ResponseWriter, r *http.Request) { statuses := mcp.GetAllStatuses() writeJSON(w, map[string]interface{}{ "statuses": statuses, }) } func (s *Server) handleMCPRegistry(w http.ResponseWriter, r *http.Request) { reg, err := mcp.LoadRegistry() if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } writeJSON(w, map[string]interface{}{ "registry": reg, }) } func (s *Server) handleLSPHealth(w http.ResponseWriter, r *http.Request) { servers := lsp.ScanServers() type healthInfo struct { Name string `json:"name"` Language string `json:"language"` Installed bool `json:"installed"` Healthy bool `json:"healthy"` Detail string `json:"detail,omitempty"` } var results []healthInfo for _, srv := range servers { healthy, detail := lsp.HealthCheck(srv.Name) results = append(results, healthInfo{ Name: srv.Name, Language: srv.Language, Installed: srv.Installed, Healthy: healthy, Detail: detail, }) } writeJSON(w, map[string]interface{}{ "servers": results, }) } func (s *Server) handleLSPAutoInstall(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { writeError(w, "POST only", http.StatusMethodNotAllowed) return } var body struct { ProjectDir string `json:"project_dir,omitempty"` } if r.Body != nil { json.NewDecoder(r.Body).Decode(&body) } if body.ProjectDir == "" { home, _ := os.UserHomeDir() body.ProjectDir = home } results, err := lsp.AutoInstallForProject(body.ProjectDir) if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } writeJSON(w, map[string]interface{}{ "results": results, }) } func (s *Server) handleLSPEditorConfig(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { writeError(w, "POST only", http.StatusMethodNotAllowed) return } var body struct { Editor string `json:"editor"` Names []string `json:"names,omitempty"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeError(w, err.Error(), http.StatusBadRequest) return } allServers := lsp.ScanServers() var selected []lsp.LSPServer if len(body.Names) > 0 { nameSet := map[string]bool{} for _, n := range body.Names { nameSet[n] = true } for _, srv := range allServers { if nameSet[srv.Name] { selected = append(selected, srv) } } } else { for _, srv := range allServers { if srv.Installed { selected = append(selected, srv) } } } config, err := lsp.GenerateEditorConfigs(selected, body.Editor, "") if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } writeJSON(w, map[string]interface{}{ "editor": body.Editor, "config": config, }) } func (s *Server) handleSkillValidate(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { writeError(w, "POST only", http.StatusMethodNotAllowed) return } var body struct { Name string `json:"name"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeError(w, err.Error(), http.StatusBadRequest) return } skill, err := skills.Get(body.Name) if err != nil { writeError(w, err.Error(), http.StatusNotFound) return } errs := skills.Validate(skill) writeJSON(w, map[string]interface{}{ "name": body.Name, "valid": len(errs) == 0, "errors": errs, "dependencies": skills.CheckDependencies(skill), }) } func (s *Server) handleSkillTest(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { writeError(w, "POST only", http.StatusMethodNotAllowed) return } var body struct { Name string `json:"name"` SampleTask string `json:"sample_task,omitempty"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeError(w, err.Error(), http.StatusBadRequest) return } result := skills.DryRun(body.Name, body.SampleTask) writeJSON(w, result) } func (s *Server) handleSkillExport(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { writeError(w, "POST only", http.StatusMethodNotAllowed) return } var body struct { Name string `json:"name"` ExportPath string `json:"export_path"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeError(w, err.Error(), http.StatusBadRequest) return } home, _ := os.UserHomeDir() if body.ExportPath == "" { body.ExportPath = home + "/.muyue/exports/" + body.Name + ".md" } if err := skills.Export(body.Name, body.ExportPath); err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } writeJSON(w, map[string]string{"status": "ok", "path": body.ExportPath}) } func (s *Server) handleSkillImport(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { writeError(w, "POST only", http.StatusMethodNotAllowed) return } var body struct { ImportPath string `json:"import_path"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeError(w, err.Error(), http.StatusBadRequest) return } skill, err := skills.Import(body.ImportPath) if err != nil { writeError(w, err.Error(), http.StatusBadRequest) return } if err := skills.Create(skill); err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } writeJSON(w, map[string]interface{}{"status": "ok", "skill": skill.Name}) } func (s *Server) handleDashboardStatus(w http.ResponseWriter, r *http.Request) { mcpStatuses := mcp.GetAllStatuses() lspServers := lsp.ScanServers() skillList, _ := skills.List() mcpHealthy := 0 mcpTotal := len(mcpStatuses) for _, st := range mcpStatuses { if st.Healthy { mcpHealthy++ } } lspInstalled := 0 lspTotal := len(lspServers) for _, srv := range lspServers { if srv.Installed { lspInstalled++ } } skillsDeployed := len(skillList) var skillIssues []string for _, sk := range skillList { missing := skills.CheckDependencies(&sk) if len(missing) > 0 { for _, dep := range missing { skillIssues = append(skillIssues, sk.Name+": missing "+dep.Type+" "+dep.Name) } } } writeJSON(w, map[string]interface{}{ "mcp": map[string]interface{}{ "total": mcpTotal, "healthy": mcpHealthy, "servers": mcpStatuses, }, "lsp": map[string]interface{}{ "total": lspTotal, "installed": lspInstalled, "servers": lspServers, }, "skills": map[string]interface{}{ "total": skillsDeployed, "issues": skillIssues, "deployed": skillList, }, }) } func (s *Server) handleScan(w http.ResponseWriter, r *http.Request) { s.scanResult = scanner.ScanSystem() writeJSON(w, map[string]string{"status": "ok"}) } func (s *Server) handleEditors(w http.ResponseWriter, r *http.Request) { editors := scanner.ScanEditors() writeJSON(w, map[string]interface{}{"editors": editors}) }