diff --git a/agent/loop.go b/agent/loop.go index 57e50fe..ae851cd 100644 --- a/agent/loop.go +++ b/agent/loop.go @@ -18,18 +18,28 @@ const ( maxTurns = 10 ) -var systemPrompt = `Du bist ein autonomer Coding-Agent. Du bekommst einen Task und erledigst ihn vollständig. +var systemPrompt = `Du bist ein autonomer Coding-Agent. -Du hast folgende Tools zur Verfügung: -- TOOL:READ_FILE:pfad → Datei lesen -- TOOL:WRITE_FILE:pfad:inhalt → Datei schreiben -- TOOL:LIST_FILES:pfad → Verzeichnis auflisten +` + BuildToolPrompt() + ` -Regeln: -1. Analysiere den Task zuerst -2. Nutze die Tools um Dateien zu lesen/schreiben -3. Wenn der Task vollständig erledigt ist, schreibe am Ende: TASK_COMPLETE -4. Bei Fehlern beschreibe das Problem klar` +WICHTIGE REGELN: +1. Nutze IMMER relative Pfade (z.B. hello.go, nicht /absoluter/pfad/hello.go) +2. Schreibe Dateiinhalt NIEMALS in Markdown-Codeblöcken (keine Backticks) +3. Nutze IMMER das <<<...>>> Format für WRITE_FILE +4. Wenn der Task erledigt ist, schreibe EXAKT auf einer eigenen Zeile: TASK_COMPLETE + +Beispiel: +TOOL:WRITE_FILE:hello.go +<<< +package main + +import "fmt" + +func main() { + fmt.Println("Hello World") +} +>>> +TASK_COMPLETE` type AgentLoop struct { client *openai.Client diff --git a/agent/tools.go b/agent/tools.go index 3173fc2..dfb58f2 100644 --- a/agent/tools.go +++ b/agent/tools.go @@ -180,6 +180,7 @@ func readFile(absPath, displayPath string) string { } func writeFile(absPath, displayPath, content string) string { + content = cleanContent(content) if err := os.MkdirAll(filepath.Dir(absPath), 0755); err != nil { return fmt.Sprintf("WRITE_FILE ERROR: Verzeichnis anlegen fehlgeschlagen: %v", err) } @@ -213,12 +214,22 @@ func listFiles(absPath, displayPath string) string { // sanitizePath stellt sicher dass der Pfad innerhalb des workDir bleibt. // Verhindert Directory Traversal wie ../../etc/passwd func sanitizePath(workDir, relPath string) (string, error) { - // Absoluten Zielpfad berechnen - abs := filepath.Join(workDir, relPath) - abs = filepath.Clean(abs) + // Wenn LLM einen absoluten Pfad schickt → relativen Teil extrahieren + if filepath.IsAbs(relPath) { + workDirClean := filepath.Clean(workDir) + // Prüfen ob der absolute Pfad innerhalb des workDir liegt + if strings.HasPrefix(relPath, workDirClean) { + // Absoluten Pfad direkt nutzen, kein Join nötig + return filepath.Clean(relPath), nil + } + // Absoluter Pfad außerhalb workDir → nur Dateiname nehmen + relPath = filepath.Base(relPath) + } - // Muss mit workDir beginnen + // Normaler Fall: relativer Pfad + abs := filepath.Clean(filepath.Join(workDir, relPath)) workDirClean := filepath.Clean(workDir) + if !strings.HasPrefix(abs, workDirClean+string(filepath.Separator)) && abs != workDirClean { return "", fmt.Errorf("Pfad außerhalb des Arbeitsverzeichnisses") @@ -226,3 +237,21 @@ func sanitizePath(workDir, relPath string) (string, error) { return abs, nil } + +func cleanContent(content string) string { + // Escaped Quotes normalisieren + content = strings.ReplaceAll(content, `\"`, `"`) + content = strings.ReplaceAll(content, `\\n`, "\n") + content = strings.ReplaceAll(content, `\\t`, "\t") + + // Markdown Codeblöcke entfernen + lines := strings.Split(content, "\n") + var cleaned []string + for _, line := range lines { + if strings.HasPrefix(strings.TrimSpace(line), "```") { + continue + } + cleaned = append(cleaned, line) + } + return strings.TrimSpace(strings.Join(cleaned, "\n")) +} diff --git a/main.go b/main.go index 1c1fed9..62bd3ec 100644 --- a/main.go +++ b/main.go @@ -62,11 +62,10 @@ func selectModel(client *openai.Client) string { func main() { // Verzeichnis der ausführbaren Binary ermitteln - execPath, err := os.Executable() + execDir, err := os.Getwd() if err != nil { - log.Fatalf("Konnte Executable-Pfad nicht ermitteln: %v", err) + log.Fatalf("Konnte Arbeitsverzeichnis nicht ermitteln: %v", err) } - execDir := filepath.Dir(execPath) // Flags definieren verbose := flag.Bool("verbose", false, "Zeigt alle Chat-Nachrichten vollständig an")