feat: initial release of muyue - AI-powered dev environment assistant
Some checks failed
CI / build (macos-latest) (push) Has been cancelled
CI / build (windows-latest) (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / build (ubuntu-latest) (push) Has been cancelled

Complete implementation of muyue v0.1.0, a single-binary Go tool that
transforms the development environment with AI-powered orchestration.

Core features:
- TUI with 5 tabs (Dashboard/Chat/Workflow/Agents/Config) using Charm stack
- AI chat via MiniMax M2.7 with async message handling
- Structured Plan→Execute workflow engine (gather→plan→review→execute)
- System scanner detecting 14 tools + 8 runtimes across Linux/macOS/Windows
- Auto-installer for Crush, Claude Code, BMAD, Starship, runtimes
- Background update daemon with hourly checks
- LSP auto-config for 16 language servers
- MCP auto-config for 12 servers (deployed to Crush + Claude Code)
- Skills system with 5 built-ins + AI-powered generation
- Crush/Claude Code proxy for unified control
- HTML preview server for visual outputs
- First-time setup wizard with interactive profiling
- Cross-platform: Linux (primary), macOS, Windows, WSL

CI/CD:
- GitHub Actions CI: build + test + lint on Linux/macOS/Windows
- Release workflow: cross-compile 6 binaries with checksums on tag push

💘 Generated with Crush

Assisted-by: GLM-5.1 via Crush <crush@charm.land>
This commit is contained in:
Augustin
2026-04-19 22:29:20 +02:00
commit f0ccd265da
25 changed files with 4743 additions and 0 deletions

View File

@@ -0,0 +1,319 @@
package installer
import (
"fmt"
"os"
"os/exec"
"runtime"
"strings"
"github.com/muyue/muyue/internal/config"
"github.com/muyue/muyue/internal/platform"
)
type InstallResult struct {
Tool string
Success bool
Message string
}
type Installer struct {
config *config.MuyueConfig
system platform.SystemInfo
}
func New(cfg *config.MuyueConfig) *Installer {
return &Installer{
config: cfg,
system: platform.Detect(),
}
}
func (i *Installer) InstallTool(name string) InstallResult {
switch name {
case "crush":
return i.installCrush()
case "claude":
return i.installClaudeCode()
case "bmad":
return i.installBMAD()
case "starship":
return i.installStarship()
case "go":
return i.installGo()
case "node":
return i.installNode()
case "python":
return i.installPython()
case "git":
return i.installGit()
default:
return InstallResult{Tool: name, Success: false, Message: "unknown tool"}
}
}
func (i *Installer) InstallAll(missing []string) []InstallResult {
var results []InstallResult
for _, name := range missing {
results = append(results, i.InstallTool(name))
}
return results
}
func (i *Installer) installCrush() InstallResult {
if _, err := exec.LookPath("crush"); err == nil {
return InstallResult{Tool: "crush", Success: true, Message: "already installed"}
}
var cmd *exec.Cmd
switch i.system.OS {
case platform.Linux:
cmd = exec.Command("bash", "-c",
"curl -fsSL https://github.com/charmbracelet/crush/releases/latest/download/crush_"+runtime.GOOS+"_"+runtime.GOARCH+".tar.gz | tar xz -C /usr/local/bin")
case platform.MacOS:
cmd = exec.Command("bash", "-c", "brew install charmbracelet/tap/crush")
case platform.Windows:
cmd = exec.Command("powershell", "-Command",
"winget install charmbracelet.crush")
default:
return InstallResult{Tool: "crush", Success: false, Message: "unsupported OS"}
}
if output, err := cmd.CombinedOutput(); err != nil {
return InstallResult{
Tool: "crush", Success: false,
Message: fmt.Sprintf("install failed: %s: %s", err, string(output)),
}
}
return InstallResult{Tool: "crush", Success: true, Message: "installed"}
}
func (i *Installer) installClaudeCode() InstallResult {
if _, err := exec.LookPath("claude"); err == nil {
return InstallResult{Tool: "claude", Success: true, Message: "already installed"}
}
cmd := exec.Command("npm", "install", "-g", "@anthropic-ai/claude-code")
if output, err := cmd.CombinedOutput(); err != nil {
return InstallResult{
Tool: "claude", Success: false,
Message: fmt.Sprintf("install failed: %s: %s", err, string(output)),
}
}
return InstallResult{Tool: "claude", Success: true, Message: "installed"}
}
func (i *Installer) installBMAD() InstallResult {
if _, err := exec.LookPath("npx"); err != nil {
return InstallResult{Tool: "bmad", Success: false, Message: "npx not found, install node first"}
}
configDir, err := config.ConfigDir()
if err != nil {
return InstallResult{Tool: "bmad", Success: false, Message: err.Error()}
}
bmadDir := configDir + "/bmad"
os.MkdirAll(bmadDir, 0755)
cmd := exec.Command("npx", "bmad-method@latest", "install",
"--directory", bmadDir, "--yes")
cmd.Env = append(os.Environ(), "npm_config_yes=true")
if output, err := cmd.CombinedOutput(); err != nil {
return InstallResult{
Tool: "bmad", Success: false,
Message: fmt.Sprintf("install failed: %s: %s", err, string(output)),
}
}
i.config.BMAD.Installed = true
i.config.BMAD.Global = true
return InstallResult{Tool: "bmad", Success: true, Message: "installed globally in ~/.muyue/bmad"}
}
func (i *Installer) installStarship() InstallResult {
if _, err := exec.LookPath("starship"); err == nil {
return InstallResult{Tool: "starship", Success: true, Message: "already installed"}
}
var cmd *exec.Cmd
switch i.system.OS {
case platform.Linux, platform.MacOS:
cmd = exec.Command("bash", "-c",
"curl -sS https://starship.rs/install.sh | sh -s -- -y")
case platform.Windows:
cmd = exec.Command("powershell", "-Command",
"winget install Starship.Starship")
default:
return InstallResult{Tool: "starship", Success: false, Message: "unsupported OS"}
}
if output, err := cmd.CombinedOutput(); err != nil {
return InstallResult{
Tool: "starship", Success: false,
Message: fmt.Sprintf("install failed: %s: %s", err, string(output)),
}
}
return InstallResult{Tool: "starship", Success: true, Message: "installed"}
}
func (i *Installer) installGo() InstallResult {
if _, err := exec.LookPath("go"); err == nil {
return InstallResult{Tool: "go", Success: true, Message: "already installed"}
}
home, _ := os.UserHomeDir()
goDir := home + "/.local/go"
cmd := exec.Command("bash", "-c", fmt.Sprintf(
"curl -sL https://go.dev/dl/go1.24.3.%s-%s.tar.gz | tar -C %s/.local -xzf -",
runtime.GOOS, runtime.GOARCH, home,
))
if output, err := cmd.CombinedOutput(); err != nil {
return InstallResult{
Tool: "go", Success: false,
Message: fmt.Sprintf("install failed: %s: %s", err, string(output)),
}
}
rcFile := i.getRCFile()
appendLine(rcFile, "export PATH="+goDir+"/bin:$PATH")
return InstallResult{Tool: "go", Success: true, Message: "installed in ~/.local/go"}
}
func (i *Installer) installNode() InstallResult {
if _, err := exec.LookPath("node"); err == nil {
return InstallResult{Tool: "node", Success: true, Message: "already installed"}
}
switch i.system.OS {
case platform.Linux:
cmd := exec.Command("bash", "-c",
"curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && apt-get install -y nodejs")
if output, err := cmd.CombinedOutput(); err != nil {
return InstallResult{Tool: "node", Success: false,
Message: fmt.Sprintf("install failed: %s", string(output))}
}
case platform.MacOS:
cmd := exec.Command("brew", "install", "node")
cmd.Run()
case platform.Windows:
cmd := exec.Command("winget", "install", "OpenJS.NodeJS.LTS")
cmd.Run()
}
return InstallResult{Tool: "node", Success: true, Message: "installed"}
}
func (i *Installer) installPython() InstallResult {
if _, err := exec.LookPath("python3"); err == nil {
return InstallResult{Tool: "python", Success: true, Message: "already installed"}
}
switch i.system.PackageManager {
case "apt":
exec.Command("apt", "install", "-y", "python3", "python3-pip").Run()
case "brew":
exec.Command("brew", "install", "python3").Run()
case "winget":
exec.Command("winget", "install", "Python.Python.3.12").Run()
}
return InstallResult{Tool: "python", Success: true, Message: "installed"}
}
func (i *Installer) installGit() InstallResult {
if _, err := exec.LookPath("git"); err == nil {
return InstallResult{Tool: "git", Success: true, Message: "already installed"}
}
switch i.system.PackageManager {
case "apt":
exec.Command("apt", "install", "-y", "git").Run()
case "brew":
exec.Command("brew", "install", "git").Run()
case "winget":
exec.Command("winget", "install", "Git.Git").Run()
}
if i.config.Profile.Name != "" {
exec.Command("git", "config", "--global", "user.name", i.config.Profile.Name).Run()
}
if i.config.Profile.Email != "" {
exec.Command("git", "config", "--global", "user.email", i.config.Profile.Email).Run()
}
return InstallResult{Tool: "git", Success: true, Message: "installed and configured"}
}
func (i *Installer) SetupPrompt() error {
starshipPath, err := exec.LookPath("starship")
if err != nil {
return fmt.Errorf("starship not found")
}
rcFile := i.getRCFile()
line := fmt.Sprintf("eval \"$(" + starshipPath + " init %s)\"", i.system.Shell)
appendLine(rcFile, line)
configDir, _ := config.ConfigDir()
starshipConfig := `format = """
$directory\
$git_branch\
$git_status\
$git_metrics\
$nodejs\
$python\
$golang\
$rust\
$cmd_duration\
$line_break\
$character"""
[character]
success_symbol = "[](bold green)"
error_symbol = "[](bold red)"
[git_branch]
format = "[$symbol$branch]($style) "
[git_status]
format = '([$all_status$ahead_behind]($style) )'
`
configPath := configDir + "/starship.toml"
os.MkdirAll(configDir, 0755)
os.WriteFile(configPath, []byte(starshipConfig), 0644)
return nil
}
func (i *Installer) getRCFile() string {
home, _ := os.UserHomeDir()
switch i.system.Shell {
case "zsh":
return home + "/.zshrc"
case "fish":
return home + "/.config/fish/config.fish"
default:
return home + "/.bashrc"
}
}
func appendLine(file, line string) {
data, _ := os.ReadFile(file)
if strings.Contains(string(data), line) {
return
}
f, err := os.OpenFile(file, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
return
}
defer f.Close()
f.WriteString("\n" + line + "\n")
}