feat: add Cobra CLI, LSP/MCP registries, workflow engine, and enriched dashboard
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>
This commit is contained in:
333
internal/lsp/registry.go
Normal file
333
internal/lsp/registry.go
Normal file
@@ -0,0 +1,333 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user