feat: Plan-Modus – Toggle im Chat zum Umschalten zwischen Plan/Ausführung

- Toggle-Button (🗺️) im Chat-Input-Bereich neben Explorer
- Plan-Modus wird per ChatRequest.planMode an Backend gesendet
- Backend: Write-Tools gefiltert (nur read_file, list_files, glob, grep,
  todo_write, memory_*, ask_user_choice bleiben), System-Prompt-Block
  ergänzt der Änderungen verbietet, tool_choice nie 'any' erzwungen
- ChainRecord.PlanMode: pro Chain gespeichert (informativ für History)
- Chain-Meta-Badge '📋 Plan-Modus' bei Plan-Chains
- Choice-Response erbt planMode vom vorherigen Chain
- CSS: .plan-mode-active Styling (Primary-Color Highlight)
- Placeholder wechselt zu 'Plannen...' wenn aktiv
- Plan-Modus wird bei Session-Wechsel zurückgesetzt
This commit is contained in:
Meleki committed 2026-03-07 12:07:39 +01:00
1 parent 1c11ffa3cd
commit 6d95f9aec2
13 files changed
+123 -13

No files matched your search

+25 -3
View File
@@ -50,6 +50,7 @@ public class AnthropicService
ReviewContext review = null,
FinalizerContext finalizer = null,
bool allowUserChoice = true,
bool planMode = false,
CancellationToken cancellationToken = default)
{
var sw = System.Diagnostics.Stopwatch.StartNew();
@@ -58,6 +59,20 @@ public class AnthropicService
int reviewRound = 0;
string lastStopReason = "";
// Plan-Modus: System-Prompt-Ergänzung
if (planMode && systemPrompt != null)
{
systemPrompt.Add(new { type = "text", text = @"## Plan-Modus aktiv
Der User hat den Plan-Modus aktiviert. Du DARFST KEINE Änderungen vornehmen:
- KEINE Dateien erstellen, bearbeiten oder löschen
- KEINE Shell-Befehle ausführen die Änderungen vornehmen (Commits, Installationen etc.)
- NUR lesen, analysieren, suchen und planen
- Nutze read_file, list_files, glob, grep und web_search um den Codebase zu verstehen
- Nutze ask_user_choice um Rückfragen zu stellen und Anforderungen zu klären
- Erstelle einen strukturierten Plan mit Aufgaben, Abhängigkeiten und Reihenfolge
- Der User wird den Plan-Modus deaktivieren wenn du mit der Umsetzung beginnen sollst" });
}
var chain = new ChainRecord
{
ChainId = chainId,
@@ -93,13 +108,19 @@ public class AnthropicService
{
var tools = new List<object>();
if (hasTools)
tools.AddRange(_toolService.GetToolDefinitions(includeFileTools: hasWorkspace, includeMemory: includeMemory, includeUserChoice: allowUserChoice));
{
var allTools = _toolService.GetToolDefinitions(includeFileTools: hasWorkspace, includeMemory: includeMemory, includeUserChoice: allowUserChoice);
if (planMode)
allTools = _toolService.FilterReadOnlyTools(allTools);
tools.AddRange(allTools);
}
if (hasWebSearch)
tools.Add(new { type = webSearchVersion, name = "web_search", max_uses = 5 });
payload["tools"] = tools;
// "any" erzwingt Tool-Nutzung, aber nur in der ersten Iteration,
// damit Claude nicht endlos Tools aufruft wenn die Aufgabe erledigt ist.
var effectiveChoice = (toolChoice == "any" && iterations == 1) ? "any" : "auto";
// Im Plan-Modus nie "any" erzwingen – Claude soll frei antworten können.
var effectiveChoice = (!planMode && toolChoice == "any" && iterations == 1) ? "any" : "auto";
payload["tool_choice"] = new { type = effectiveChoice };
}
@@ -252,7 +273,8 @@ public class AnthropicService
chainId,
durationMs = sw.ElapsedMilliseconds,
stopReason = finalStopReason,
streamErrorInfo
streamErrorInfo,
planMode
}, cancellationToken);
return;
}