All checks were successful
Beta Release / beta (push) Successful in 2m24s
Major changes: - Refactor CLI entry point to Cobra commands (root, setup, scan, doctor, install, update, lsp, mcp, skills, config, version) - Add LSP registry with health checks, auto-install, and editor config generation - Add MCP registry with editor detection, status tracking, and per-editor configuration - Add workflow engine with planner and step execution for automated task chains - Add conversation search, export (Markdown/JSON), and detailed token counting - Add streaming shell chat handler with tool call/result events - Add skill validation, dry-run testing, and export endpoints - Enrich dashboard with Tools/Activity/Status tabs and tool cards grid - Add PRD documentation - Complete i18n for both EN and FR 💘 Generated with Crush Assisted-by: GLM-5.1 via Crush <crush@charm.land>
334 lines
12 KiB
Go
334 lines
12 KiB
Go
package lsp
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
type RegistryEntry struct {
|
|
Name string `yaml:"name" json:"name"`
|
|
Language string `yaml:"language" json:"language"`
|
|
Description string `yaml:"description" json:"description"`
|
|
Command string `yaml:"command" json:"command"`
|
|
InstallCmd string `yaml:"install_cmd" json:"install_cmd"`
|
|
InstallType string `yaml:"install_type" json:"install_type"`
|
|
Category string `yaml:"category" json:"category"`
|
|
FilePatterns []string `yaml:"file_patterns,omitempty" json:"file_patterns,omitempty"`
|
|
ConfigFiles []string `yaml:"config_files,omitempty" json:"config_files,omitempty"`
|
|
Tags []string `yaml:"tags,omitempty" json:"tags,omitempty"`
|
|
HomePage string `yaml:"homepage,omitempty" json:"homepage,omitempty"`
|
|
|
|
NeovimSetup string `yaml:"neovim_setup,omitempty" json:"neovim_setup,omitempty"`
|
|
HelixLanguage string `yaml:"helix_language,omitempty" json:"helix_language,omitempty"`
|
|
}
|
|
|
|
type LSPRegistry struct {
|
|
SchemaVersion string `yaml:"schema_version"`
|
|
UpdatedAt time.Time `yaml:"updated_at"`
|
|
Servers []RegistryEntry `yaml:"servers"`
|
|
}
|
|
|
|
func DefaultLSPRegistry() *LSPRegistry {
|
|
return &LSPRegistry{
|
|
SchemaVersion: "v1",
|
|
UpdatedAt: time.Now(),
|
|
Servers: []RegistryEntry{
|
|
{
|
|
Name: "gopls", Language: "go", Description: "Go language server",
|
|
Command: "gopls", InstallCmd: "go install golang.org/x/tools/gopls@latest",
|
|
InstallType: "go", Category: "lsp", FilePatterns: []string{"*.go"},
|
|
ConfigFiles: []string{"go.mod"}, Tags: []string{"go", "linting", "completion"},
|
|
HomePage: "https://github.com/golang/tools",
|
|
NeovimSetup: `lspconfig.gopls.setup{}`,
|
|
HelixLanguage: "go",
|
|
},
|
|
{
|
|
Name: "pyright", Language: "python", Description: "Python type checker and language server",
|
|
Command: "pyright", InstallCmd: "npm install -g pyright",
|
|
InstallType: "npm", Category: "lsp", FilePatterns: []string{"*.py", "*.pyi"},
|
|
ConfigFiles: []string{"requirements.txt", "pyproject.toml", "setup.py", "Pipfile"},
|
|
Tags: []string{"python", "type-checking"}, HomePage: "https://github.com/microsoft/pyright",
|
|
NeovimSetup: `lspconfig.pyright.setup{}`,
|
|
HelixLanguage: "python",
|
|
},
|
|
{
|
|
Name: "typescript-language-server", Language: "typescript", Description: "TypeScript and JavaScript language server",
|
|
Command: "typescript-language-server", InstallCmd: "npm install -g typescript-language-server typescript",
|
|
InstallType: "npm", Category: "lsp", FilePatterns: []string{"*.ts", "*.tsx", "*.js", "*.jsx"},
|
|
ConfigFiles: []string{"tsconfig.json", "package.json"},
|
|
Tags: []string{"typescript", "javascript"}, HomePage: "https://github.com/typescript-language-server/typescript-language-server",
|
|
NeovimSetup: `lspconfig.tsserver.setup{}`,
|
|
HelixLanguage: "typescript",
|
|
},
|
|
{
|
|
Name: "vscode-json-language-server", Language: "json", Description: "JSON language server",
|
|
Command: "vscode-json-language-server", InstallCmd: "npm install -g vscode-langservers-extracted",
|
|
InstallType: "npm", Category: "lsp", FilePatterns: []string{"*.json", "*.jsonc"},
|
|
Tags: []string{"json"}, NeovimSetup: `lspconfig.jsonls.setup{}`,
|
|
HelixLanguage: "json",
|
|
},
|
|
{
|
|
Name: "vscode-html-language-server", Language: "html", Description: "HTML language server",
|
|
Command: "vscode-html-language-server", InstallCmd: "npm install -g vscode-langservers-extracted",
|
|
InstallType: "npm", Category: "lsp", FilePatterns: []string{"*.html", "*.htm"},
|
|
Tags: []string{"html"}, NeovimSetup: `lspconfig.html.setup{}`,
|
|
HelixLanguage: "html",
|
|
},
|
|
{
|
|
Name: "vscode-css-language-server", Language: "css", Description: "CSS/SCSS/LESS language server",
|
|
Command: "vscode-css-language-server", InstallCmd: "npm install -g vscode-langservers-extracted",
|
|
InstallType: "npm", Category: "lsp", FilePatterns: []string{"*.css", "*.scss", "*.less"},
|
|
Tags: []string{"css"}, NeovimSetup: `lspconfig.cssls.setup{}`,
|
|
HelixLanguage: "css",
|
|
},
|
|
{
|
|
Name: "yaml-language-server", Language: "yaml", Description: "YAML language server",
|
|
Command: "yaml-language-server", InstallCmd: "npm install -g yaml-language-server",
|
|
InstallType: "npm", Category: "lsp", FilePatterns: []string{"*.yml", "*.yaml"},
|
|
Tags: []string{"yaml"}, NeovimSetup: `lspconfig.yamlls.setup{}`,
|
|
HelixLanguage: "yaml",
|
|
},
|
|
{
|
|
Name: "bash-language-server", Language: "bash", Description: "Bash language server",
|
|
Command: "bash-language-server", InstallCmd: "npm install -g bash-language-server",
|
|
InstallType: "npm", Category: "lsp", FilePatterns: []string{"*.sh", "*.bash"},
|
|
Tags: []string{"bash", "shell"}, NeovimSetup: `lspconfig.bashls.setup{}`,
|
|
HelixLanguage: "bash",
|
|
},
|
|
{
|
|
Name: "rust-analyzer", Language: "rust", Description: "Rust language server",
|
|
Command: "rust-analyzer", InstallCmd: "rustup component add rust-analyzer",
|
|
InstallType: "rustup", Category: "lsp", FilePatterns: []string{"*.rs"},
|
|
ConfigFiles: []string{"Cargo.toml"}, Tags: []string{"rust"},
|
|
HomePage: "https://github.com/rust-lang/rust-analyzer",
|
|
NeovimSetup: `lspconfig.rust_analyzer.setup{}`,
|
|
HelixLanguage: "rust",
|
|
},
|
|
{
|
|
Name: "clangd", Language: "c/c++", Description: "C/C++ language server",
|
|
Command: "clangd", InstallCmd: "", InstallType: "system",
|
|
Category: "lsp", FilePatterns: []string{"*.c", "*.cpp", "*.h", "*.hpp"},
|
|
ConfigFiles: []string{"CMakeLists.txt", "Makefile"}, Tags: []string{"c", "cpp"},
|
|
NeovimSetup: `lspconfig.clangd.setup{}`,
|
|
HelixLanguage: "c",
|
|
},
|
|
{
|
|
Name: "lua-language-server", Language: "lua", Description: "Lua language server",
|
|
Command: "lua-language-server", InstallCmd: "npm install -g lua-language-server",
|
|
InstallType: "npm", Category: "lsp", FilePatterns: []string{"*.lua"},
|
|
Tags: []string{"lua"}, NeovimSetup: `lspconfig.lua_ls.setup{}`,
|
|
HelixLanguage: "lua",
|
|
},
|
|
{
|
|
Name: "dockerfile-language-server", Language: "dockerfile", Description: "Dockerfile language server",
|
|
Command: "docker-langserver", InstallCmd: "npm install -g dockerfile-language-server-nodejs",
|
|
InstallType: "npm", Category: "lsp", FilePatterns: []string{"Dockerfile", "Dockerfile.*"},
|
|
Tags: []string{"docker"}, NeovimSetup: `lspconfig.dockerls.setup{}`,
|
|
HelixLanguage: "dockerfile",
|
|
},
|
|
{
|
|
Name: "tailwindcss-language-server", Language: "tailwind", Description: "Tailwind CSS language server",
|
|
Command: "tailwindcss-language-server", InstallCmd: "npm install -g @tailwindcss/language-server",
|
|
InstallType: "npm", Category: "lsp", FilePatterns: []string{"*.html", "*.tsx", "*.jsx"},
|
|
ConfigFiles: []string{"tailwind.config.js", "tailwind.config.ts"},
|
|
Tags: []string{"tailwind", "css"}, NeovimSetup: `lspconfig.tailwindcss.setup{}`,
|
|
},
|
|
{
|
|
Name: "svelte-language-server", Language: "svelte", Description: "Svelte language server",
|
|
Command: "svelteserver", InstallCmd: "npm install -g svelte-language-server",
|
|
InstallType: "npm", Category: "lsp", FilePatterns: []string{"*.svelte"},
|
|
Tags: []string{"svelte"}, NeovimSetup: `lspconfig.svelte.setup{}`,
|
|
HelixLanguage: "svelte",
|
|
},
|
|
{
|
|
Name: "vue-language-server", Language: "vue", Description: "Vue language server",
|
|
Command: "vue-language-server", InstallCmd: "npm install -g @vue/language-server",
|
|
InstallType: "npm", Category: "lsp", FilePatterns: []string{"*.vue"},
|
|
Tags: []string{"vue"}, NeovimSetup: `lspconfig.vuels.setup{}`,
|
|
},
|
|
{
|
|
Name: "golangci-lint-langserver", Language: "go-lint", Description: "Go linter language server",
|
|
Command: "golangci-lint-langserver", InstallCmd: "go install github.com/nametake/golangci-lint-langserver@latest",
|
|
InstallType: "go", Category: "lsp", FilePatterns: []string{"*.go"},
|
|
Tags: []string{"go", "linting"},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
var lspRegistryPath string
|
|
|
|
func init() {
|
|
home, _ := os.UserHomeDir()
|
|
if home != "" {
|
|
lspRegistryPath = filepath.Join(home, ".muyue", "lsp-registry.yaml")
|
|
}
|
|
}
|
|
|
|
func SetLSPRegistryPath(p string) {
|
|
lspRegistryPath = p
|
|
}
|
|
|
|
func LoadLSPRegistry() (*LSPRegistry, error) {
|
|
if lspRegistryPath == "" {
|
|
return DefaultLSPRegistry(), nil
|
|
}
|
|
|
|
data, err := os.ReadFile(lspRegistryPath)
|
|
if err != nil {
|
|
return DefaultLSPRegistry(), nil
|
|
}
|
|
|
|
var reg LSPRegistry
|
|
if err := yaml.Unmarshal(data, ®); err != nil {
|
|
return nil, err
|
|
}
|
|
return ®, nil
|
|
}
|
|
|
|
func SaveLSPRegistry(reg *LSPRegistry) error {
|
|
if lspRegistryPath == "" {
|
|
return nil
|
|
}
|
|
reg.UpdatedAt = time.Now()
|
|
data, err := yaml.Marshal(reg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
os.MkdirAll(filepath.Dir(lspRegistryPath), 0755)
|
|
return os.WriteFile(lspRegistryPath, data, 0644)
|
|
}
|
|
|
|
func InitLSPRegistry() error {
|
|
if lspRegistryPath == "" {
|
|
return nil
|
|
}
|
|
if _, err := os.Stat(lspRegistryPath); err == nil {
|
|
return nil
|
|
}
|
|
return SaveLSPRegistry(DefaultLSPRegistry())
|
|
}
|
|
|
|
func DetectProjectLanguages(projectDir string) []string {
|
|
if projectDir == "" {
|
|
return nil
|
|
}
|
|
|
|
langDetectors := map[string][]string{
|
|
"go": {"go.mod", "go.sum"},
|
|
"python": {"requirements.txt", "pyproject.toml", "setup.py", "Pipfile", "uv.lock"},
|
|
"typescript": {"tsconfig.json", "package.json"},
|
|
"javascript": {"package.json"},
|
|
"rust": {"Cargo.toml"},
|
|
"ruby": {"Gemfile"},
|
|
"java": {"pom.xml", "build.gradle"},
|
|
"c": {"CMakeLists.txt", "Makefile"},
|
|
"cpp": {"CMakeLists.txt"},
|
|
"php": {"composer.json"},
|
|
"lua": {".luarc.json"},
|
|
"docker": {"Dockerfile"},
|
|
}
|
|
|
|
extDetectors := map[string]string{
|
|
".go": "go",
|
|
".py": "python",
|
|
".rs": "rust",
|
|
".ts": "typescript",
|
|
".tsx": "typescript",
|
|
".js": "javascript",
|
|
".jsx": "javascript",
|
|
".rb": "ruby",
|
|
".java": "java",
|
|
".c": "c",
|
|
".cpp": "cpp",
|
|
".h": "c",
|
|
".lua": "lua",
|
|
".vue": "vue",
|
|
".svelte": "svelte",
|
|
}
|
|
|
|
detected := map[string]bool{}
|
|
for lang, files := range langDetectors {
|
|
for _, f := range files {
|
|
if _, err := os.Stat(filepath.Join(projectDir, f)); err == nil {
|
|
detected[lang] = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
entries, err := os.ReadDir(projectDir)
|
|
if err == nil {
|
|
for _, e := range entries {
|
|
if e.IsDir() {
|
|
continue
|
|
}
|
|
ext := filepath.Ext(e.Name())
|
|
if lang, ok := extDetectors[ext]; ok {
|
|
detected[lang] = true
|
|
}
|
|
}
|
|
}
|
|
|
|
var languages []string
|
|
for lang := range detected {
|
|
languages = append(languages, lang)
|
|
}
|
|
return languages
|
|
}
|
|
|
|
func GenerateNeovimConfig(servers []RegistryEntry) string {
|
|
config := `-- Generated by Muyue LSP Manager
|
|
-- Add to your init.lua or require from lspconfig setup
|
|
local lspconfig = require('lspconfig')
|
|
|
|
`
|
|
for _, s := range servers {
|
|
if s.NeovimSetup != "" {
|
|
config += s.NeovimSetup + "\n"
|
|
}
|
|
}
|
|
return config
|
|
}
|
|
|
|
func GenerateHelixConfig(servers []RegistryEntry) string {
|
|
config := `# Generated by Muyue LSP Manager
|
|
# Add to ~/.config/helix/languages.toml
|
|
|
|
`
|
|
for _, s := range servers {
|
|
if s.HelixLanguage != "" {
|
|
config += "[[language]]\n"
|
|
config += "name = \"" + s.HelixLanguage + "\"\n"
|
|
config += "language-servers = [\"" + s.Name + "\"]\n\n"
|
|
}
|
|
}
|
|
return config
|
|
}
|
|
|
|
func GenerateVSCodeRecommendations(servers []RegistryEntry) []string {
|
|
extensionMap := map[string][]string{
|
|
"gopls": {"golang.go"},
|
|
"pyright": {"ms-python.python", "ms-python.vscode-pylance"},
|
|
"typescript-language-server": {"ms-vscode.vscode-typescript-next"},
|
|
"rust-analyzer": {"rust-lang.rust-analyzer"},
|
|
"lua-language-server": {"sumneko.lua"},
|
|
"tailwindcss-language-server": {"bradlc.vscode-tailwindcss"},
|
|
"svelte-language-server": {"svelte.svelte-vscode"},
|
|
"vue-language-server": {"vue.volar"},
|
|
"yaml-language-server": {"redhat.vscode-yaml"},
|
|
"bash-language-server": {"mads-hartmann.bash-ide-vscode"},
|
|
}
|
|
|
|
var extensions []string
|
|
for _, s := range servers {
|
|
if exts, ok := extensionMap[s.Name]; ok {
|
|
extensions = append(extensions, exts...)
|
|
}
|
|
}
|
|
return extensions
|
|
}
|