// Package scaffold legt die Grundausstattung für ein Projekt an // (`nub init` / `/init`): .nub/config.toml mit einer kommentierten // Beispiel-Config und ein minimalistisches AGENTS.md. Von TUI und CLI // gemeinsam genutzt. package scaffold import ( "fmt" "os" "path/filepath" "nub/internal/session" ) // Result protokolliert, was Init tatsächlich angelegt bzw. übersprungen hat. type Result struct { ConfigPath string ConfigCreated bool AgentsPath string AgentsCreated bool } // Init legt .nub/config.toml und AGENTS.md an, falls sie noch nicht // existieren. Bestehende Dateien werden nie überschrieben — Init meldet in // Result nur, was es tatsächlich geschrieben hat. func Init(repoRoot string) (Result, error) { var res Result nubDir := filepath.Join(repoRoot, ".nub") if err := os.MkdirAll(nubDir, 0o700); err != nil { return res, fmt.Errorf("scaffold: .nub anlegen: %w", err) } if err := session.EnsureGitExclude(repoRoot); err != nil { return res, fmt.Errorf("scaffold: git exclude: %w", err) } res.ConfigPath = filepath.Join(nubDir, "config.toml") created, err := writeIfAbsent(res.ConfigPath, exampleConfigTOML, 0o600) if err != nil { return res, fmt.Errorf("scaffold: config.toml: %w", err) } res.ConfigCreated = created res.AgentsPath = filepath.Join(repoRoot, "AGENTS.md") created, err = writeIfAbsent(res.AgentsPath, exampleAgentsMD, 0o644) if err != nil { return res, fmt.Errorf("scaffold: AGENTS.md: %w", err) } res.AgentsCreated = created return res, nil } func writeIfAbsent(path, content string, perm os.FileMode) (bool, error) { if _, err := os.Stat(path); err == nil { return false, nil // existiert schon -> nicht anfassen } else if !os.IsNotExist(err) { return false, err } if err := os.WriteFile(path, []byte(content), perm); err != nil { return false, err } return true, nil } const exampleConfigTOML = `version = 1 [model] default = "gpt-4o-mini" endpoint = "openai" [[endpoint]] name = "openai" base_url = "https://api.openai.com/v1" # "env:VARNAME" liest den Key aus einer Umgebungsvariable; alternativ direkt # den Key eintragen, oder "none" für einen lokalen Endpoint ohne Key. api_key = "env:OPENAI_API_KEY" # [[endpoint]] # name = "local" # base_url = "http://localhost:11434/v1" # api_key = "none" # [endpoint.caps] # parallel_tool_calls = false [context] files = ["AGENTS.md", "REPOMAP.md"] walk_up = true max_tokens = 20000 [skills] paths = ["~/.nub/skills", ".nub/skills"] # Pro Tool: "auto" (ohne Rückfrage), "ask" (Rückfrage in der TUI, im # Print-Modus ein harter Fehler) oder "deny" (nie). deny_paths/deny_bash # überstimmen den Modus immer, auch bei "auto". [permissions] read = "auto" glob = "auto" grep = "auto" write = "ask" edit = "ask" bash = "ask" deny_paths = [".git/**", "**/.env", "**/id_rsa*"] deny_bash = ["rm -rf /", "git push --force*"] # [[mcp]] # name = "github" # command = "gh-mcp-server" # tools = ["create_issue", "get_pull_request"] # [profiles] # review = ["github"] # default = [] ` const exampleAgentsMD = `# Agent Instructions Kurze, projektspezifische Hinweise für nub. Diese Datei landet automatisch im System-Prompt jeder Session. ## Projekt ## Konventionen - ## Vorsicht bei - `