feat: /plan, /cancel, /continue, /discard + Context 262144 + KV-Cache q4_0
- Neue Befehle: /plan (Planungsmodus, nur PLAN.md), /cancel (Loop-Abbruch), /continue (Resume nach Unterbrechung), /discard (PLAN.md verwerfen) - contextWindow in models.json und llama.cpp-Servern: 131072 → 262144 - KV-Cache: q8_0 → q4_0 (weniger VRAM, passt zu 262k-Kontext auf 2× 3090) - parallel: 2 → 1 beim Coder (stabiler bei großem Kontext) - Optimize-Status mit ASCII-Fortschrittsbalken + Blocker-Preview - cancelRequested-Flag prüft nach jedem Loop-Schritt Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
b19c189e2e
commit
4a31535b76
4 changed files with 126 additions and 26 deletions
|
|
@ -54,7 +54,7 @@
|
||||||
"name": "Qwen3.6 27B Coder (llama.cpp :8001)",
|
"name": "Qwen3.6 27B Coder (llama.cpp :8001)",
|
||||||
"reasoning": true,
|
"reasoning": true,
|
||||||
"input": ["text"],
|
"input": ["text"],
|
||||||
"contextWindow": 131072,
|
"contextWindow": 262144,
|
||||||
"maxTokens": 16384,
|
"maxTokens": 16384,
|
||||||
"cost": {
|
"cost": {
|
||||||
"input": 0,
|
"input": 0,
|
||||||
|
|
@ -82,7 +82,7 @@
|
||||||
"name": "Qwen3.6 27B Judge (llama.cpp :8002)",
|
"name": "Qwen3.6 27B Judge (llama.cpp :8002)",
|
||||||
"reasoning": true,
|
"reasoning": true,
|
||||||
"input": ["text"],
|
"input": ["text"],
|
||||||
"contextWindow": 131072,
|
"contextWindow": 262144,
|
||||||
"maxTokens": 8192,
|
"maxTokens": 8192,
|
||||||
"cost": {
|
"cost": {
|
||||||
"input": 0,
|
"input": 0,
|
||||||
|
|
|
||||||
|
|
@ -239,6 +239,37 @@ function bedienungsanleitungPromptIncremental(files: string[]): string {
|
||||||
].join("\n");
|
].join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function planPrompt(task: string): string {
|
||||||
|
return [
|
||||||
|
"Du bist ein erfahrener Software-Architekt im PLANUNGSMODUS.",
|
||||||
|
"",
|
||||||
|
"ABSOLUTE VERBOTE — du darfst NICHT:",
|
||||||
|
"- Dateien editieren, schreiben oder löschen (kein edit, write, apply_patch)",
|
||||||
|
"- Git-Commits durchführen",
|
||||||
|
"- Tests oder Skripte ausführen die Seiteneffekte haben",
|
||||||
|
"",
|
||||||
|
"ERLAUBT:",
|
||||||
|
"- Dateien lesen (read, cat, grep, find)",
|
||||||
|
"- Git-History lesen (git log, git show, git diff)",
|
||||||
|
"- PLAN.md anlegen oder überschreiben (das ist dein Ausgabe-Dokument)",
|
||||||
|
"",
|
||||||
|
"Analysiere den Auftrag gründlich und erstelle einen konkreten Implementierungsplan.",
|
||||||
|
"",
|
||||||
|
"Auftrag:",
|
||||||
|
task,
|
||||||
|
"",
|
||||||
|
"Struktur deiner Ausgabe:",
|
||||||
|
"1. IST-Analyse (relevante Dateien, Architektur, Abhängigkeiten)",
|
||||||
|
"2. Implementierungsplan (nummerierte Schritte, konkret und umsetzbar)",
|
||||||
|
"3. Kritische Entscheidungen (Alternativen + Empfehlung)",
|
||||||
|
"4. Risiken und offene Fragen",
|
||||||
|
"5. Geschätzte Komplexität: einfach / mittel / komplex",
|
||||||
|
"",
|
||||||
|
"Schreibe den vollständigen Plan in PLAN.md.",
|
||||||
|
"Schließe ab mit: 'Plan bereit. Starte Umsetzung mit /coder oder /optimize --continue'",
|
||||||
|
].join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
// ── Hilfsfunktionen ─────────────────────────────────────────────────────────
|
// ── Hilfsfunktionen ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
// Legt TASK.md neu an oder hängt einen Zusatzauftrag an.
|
// Legt TASK.md neu an oder hängt einen Zusatzauftrag an.
|
||||||
|
|
@ -471,24 +502,30 @@ function finalNotify(
|
||||||
ctx.ui.setWidget("coder-judge", [
|
ctx.ui.setWidget("coder-judge", [
|
||||||
`Letzter Lauf: ${verdict} — ${detail} (${timestamp})`,
|
`Letzter Lauf: ${verdict} — ${detail} (${timestamp})`,
|
||||||
"─────────────────────────────────────────",
|
"─────────────────────────────────────────",
|
||||||
"Workflow: /coder <auftrag> | /judge | /fix | /shipit",
|
"Workflow: /coder <auftrag> | /judge | /fix | /shipit",
|
||||||
"Auto-Loop: /optimize <auftrag> [--rounds N] [--with-doku] [--continue]",
|
"Auto-Loop: /optimize <auftrag> [--rounds N] [--with-doku] [--continue]",
|
||||||
"Kleine Änderung: /patch <änderung> → /quick_check [was]",
|
"Planung: /plan <auftrag> → /coder | /optimize --continue | /discard",
|
||||||
"Finale Doku: /update_doku | Neues Projekt: /new_project <pfad>",
|
"Patch: /patch <änderung> → /quick_check [was]",
|
||||||
|
"Doku: /update_doku | Neues Projekt: /new_project <pfad>",
|
||||||
|
"Abbruch: Escape (Generation laufend) | /cancel (Loop nach aktuellem Schritt)",
|
||||||
|
"Resume: /continue | Modell: auto (Coder→:8001, Judge→:8002)",
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Extension ────────────────────────────────────────────────────────────────
|
// ── Extension ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
let cancelRequested = false;
|
||||||
|
|
||||||
export default function (pi: ExtensionAPI) {
|
export default function (pi: ExtensionAPI) {
|
||||||
pi.on("session_start", async function (_event, ctx) {
|
pi.on("session_start", async function (_event, ctx) {
|
||||||
ctx.ui.setWidget("coder-judge", [
|
ctx.ui.setWidget("coder-judge", [
|
||||||
"Workflow: /coder <auftrag> | /judge | /fix | /shipit",
|
"Workflow: /coder <auftrag> | /judge | /fix | /shipit",
|
||||||
"Auto-Loop: /optimize <auftrag> [--rounds N] [--with-doku]",
|
"Auto-Loop: /optimize <auftrag> [--rounds N] [--with-doku] [--continue]",
|
||||||
"Kleine Änderung: /patch <änderung> → /quick_check [was]",
|
"Planung: /plan <auftrag> → /coder | /optimize --continue | /discard",
|
||||||
"Finale Doku: /update_doku (nach SHIP – Kommentare + README + Bedienungsanleitung)",
|
"Patch: /patch <änderung> → /quick_check [was]",
|
||||||
"Neues Projekt: /new_project <pfad>",
|
"Doku: /update_doku | Neues Projekt: /new_project <pfad>",
|
||||||
"Modell wird automatisch gewechselt (Coder→:8001, Judge→:8002)"
|
"Abbruch: Escape (Generation laufend) | /cancel (Loop nach aktuellem Schritt)",
|
||||||
|
"Resume: /continue | Modell: auto (Coder→:8001, Judge→:8002)",
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -595,10 +632,12 @@ export default function (pi: ExtensionAPI) {
|
||||||
// TASK.md anlegen und Implementierung starten
|
// TASK.md anlegen und Implementierung starten
|
||||||
await writeTaskMd(pi, ctx, task);
|
await writeTaskMd(pi, ctx, task);
|
||||||
ctx.ui.setStatus("optimize", `Starte Optimierung (max ${maxRounds} Runden)…`);
|
ctx.ui.setStatus("optimize", `Starte Optimierung (max ${maxRounds} Runden)…`);
|
||||||
ctx.ui.setStatus("optimize", "Phase 1: Coder implementiert…");
|
const taskPreview = task.length > 55 ? task.slice(0, 52) + "…" : task;
|
||||||
|
ctx.ui.setStatus("optimize", `◉ Coder liest Anforderungen + implementiert: ${taskPreview}`);
|
||||||
await switchModel(pi, ctx, "llama-cpp-coder", "qwen3.5-coder");
|
await switchModel(pi, ctx, "llama-cpp-coder", "qwen3.5-coder");
|
||||||
await sendAndWait(pi, ctx, coderKickoff(task));
|
await sendAndWait(pi, ctx, coderKickoff(task));
|
||||||
await tickTaskMdStatus(pi, ctx, "Implementierung");
|
await tickTaskMdStatus(pi, ctx, "Implementierung");
|
||||||
|
if (cancelRequested) { cancelRequested = false; finalNotify(ctx, "⛔ Abgebrochen", "Nach Implementierung"); return; }
|
||||||
}
|
}
|
||||||
|
|
||||||
let lastBlockers = "";
|
let lastBlockers = "";
|
||||||
|
|
@ -606,43 +645,49 @@ export default function (pi: ExtensionAPI) {
|
||||||
|
|
||||||
// Schleife: Judge → (PASS? fertig : Fix → nächste Runde)
|
// Schleife: Judge → (PASS? fertig : Fix → nächste Runde)
|
||||||
for (let round = 1; round <= maxRounds; round++) {
|
for (let round = 1; round <= maxRounds; round++) {
|
||||||
ctx.ui.setStatus("optimize", `Runde ${round}/${maxRounds}: Judge prüft…`);
|
const prog = "●".repeat(round - 1) + "◉" + "○".repeat(maxRounds - round);
|
||||||
|
ctx.ui.setStatus("optimize", `${prog} Runde ${round}/${maxRounds}: Judge — TASK.md + letzter Commit + Tests…`);
|
||||||
await switchModel(pi, ctx, "llama-cpp-judge", "qwen3.5-judge");
|
await switchModel(pi, ctx, "llama-cpp-judge", "qwen3.5-judge");
|
||||||
await sendAndWait(pi, ctx, judgePrompt(""));
|
await sendAndWait(pi, ctx, judgePrompt(""));
|
||||||
|
if (cancelRequested) { cancelRequested = false; finalNotify(ctx, "⛔ Abgebrochen", `Nach Judge Runde ${round}`); return; }
|
||||||
|
|
||||||
const judgeText = getLastAssistantText(ctx);
|
const judgeText = getLastAssistantText(ctx);
|
||||||
verdict = parseVerdict(judgeText);
|
verdict = parseVerdict(judgeText);
|
||||||
|
|
||||||
if (verdict === "PASS" || verdict === "PASS WITH CONCERNS") {
|
if (verdict === "PASS" || verdict === "PASS WITH CONCERNS") {
|
||||||
await tickTaskMdStatus(pi, ctx, "Review bestanden (PASS)");
|
await tickTaskMdStatus(pi, ctx, "Review bestanden (PASS)");
|
||||||
ctx.ui.setStatus("optimize", `✓ ${verdict} nach Runde ${round}`);
|
ctx.ui.setStatus("optimize", `${"●".repeat(round)} ✓ ${verdict} nach Runde ${round}/${maxRounds} — ShipIt…`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop-Erkennung: gleicher Blocker zweimal → manuell eingreifen
|
// Loop-Erkennung: gleicher Blocker zweimal → manuell eingreifen
|
||||||
const currentBlockers = parseBlockers(judgeText);
|
const currentBlockers = parseBlockers(judgeText);
|
||||||
if (currentBlockers && currentBlockers === lastBlockers) {
|
if (currentBlockers && currentBlockers === lastBlockers) {
|
||||||
ctx.ui.setStatus("optimize", "⚠ Schleife: gleicher Blocker – manuelle Intervention nötig");
|
ctx.ui.setStatus("optimize", `${prog} ⚠ Gleicher Blocker in Runde ${round} – manuelle Intervention nötig`);
|
||||||
finalNotify(ctx, "⚠ Schleife", "Gleicher Blocker zweimal – manuelle Intervention nötig");
|
finalNotify(ctx, "⚠ Schleife", "Gleicher Blocker zweimal – manuelle Intervention nötig");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
lastBlockers = currentBlockers;
|
lastBlockers = currentBlockers;
|
||||||
|
|
||||||
if (round === maxRounds) {
|
if (round === maxRounds) {
|
||||||
ctx.ui.setStatus("optimize", `⚠ Max. ${maxRounds} Runden ohne PASS`);
|
ctx.ui.setStatus("optimize", `${"●".repeat(maxRounds)} ⚠ Max. ${maxRounds} Runden ohne PASS`);
|
||||||
finalNotify(ctx, "⚠ Kein PASS", `${maxRounds} Runden ohne PASS – bitte /judge und /fix manuell`);
|
finalNotify(ctx, "⚠ Kein PASS", `${maxRounds} Runden ohne PASS – bitte /judge und /fix manuell`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix-Phase
|
// Fix-Phase: Blocker-Preview aus Judge-Bericht anzeigen
|
||||||
ctx.ui.setStatus("optimize", `Runde ${round}/${maxRounds}: Coder fixt…`);
|
const blockerHint = currentBlockers
|
||||||
|
? (currentBlockers.length > 50 ? currentBlockers.slice(0, 47) + "…" : currentBlockers)
|
||||||
|
: "Kritikpunkte aus Judge-Bericht";
|
||||||
|
ctx.ui.setStatus("optimize", `${prog} Runde ${round}/${maxRounds}: Coder fixt — ${blockerHint}`);
|
||||||
await switchModel(pi, ctx, "llama-cpp-coder", "qwen3.5-coder");
|
await switchModel(pi, ctx, "llama-cpp-coder", "qwen3.5-coder");
|
||||||
await sendAndWait(pi, ctx, fixPrompt(""));
|
await sendAndWait(pi, ctx, fixPrompt(""));
|
||||||
|
if (cancelRequested) { cancelRequested = false; finalNotify(ctx, "⛔ Abgebrochen", `Nach Fix Runde ${round}`); return; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finale ShipIt-Prüfung nur bei PASS
|
// Finale ShipIt-Prüfung nur bei PASS
|
||||||
if (verdict === "PASS" || verdict === "PASS WITH CONCERNS") {
|
if (verdict === "PASS" || verdict === "PASS WITH CONCERNS") {
|
||||||
ctx.ui.setStatus("optimize", "Finale ShipIt-Prüfung…");
|
ctx.ui.setStatus("optimize", `${"●".repeat(maxRounds)}◉ ShipIt — SHIP oder NO-SHIP?…`);
|
||||||
await switchModel(pi, ctx, "llama-cpp-judge", "qwen3.5-judge");
|
await switchModel(pi, ctx, "llama-cpp-judge", "qwen3.5-judge");
|
||||||
await sendAndWait(pi, ctx, shipitPrompt(""));
|
await sendAndWait(pi, ctx, shipitPrompt(""));
|
||||||
|
|
||||||
|
|
@ -739,6 +784,61 @@ export default function (pi: ExtensionAPI) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── Planungsmodus ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
pi.registerCommand("plan", {
|
||||||
|
description: "Analysiert Auftrag, schmiedet Implementierungsplan in PLAN.md — macht keine Dateiänderungen. → qwen3.5-coder (:8001)",
|
||||||
|
handler: async function (args: string, ctx: ExtensionCommandContext) {
|
||||||
|
const task = (args || "").trim();
|
||||||
|
if (!task) {
|
||||||
|
ctx.ui.notify("Benutzung: /plan <auftrag>", "error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await writeTaskMd(pi, ctx, task);
|
||||||
|
await switchModel(pi, ctx, "llama-cpp-coder", "qwen3.5-coder");
|
||||||
|
ctx.ui.setStatus("plan", "Analysiere und plane (keine Dateiänderungen)…");
|
||||||
|
pi.sendUserMessage(planPrompt(task));
|
||||||
|
await ctx.waitForIdle();
|
||||||
|
ctx.ui.setStatus("plan", "");
|
||||||
|
finalNotify(ctx, "📋 Plan", "Analyse abgeschlossen — PLAN.md + Chat");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
pi.registerCommand("cancel", {
|
||||||
|
description: "Bricht laufenden Optimize-Loop nach dem aktuellen Schritt ab.",
|
||||||
|
handler: async function (_args: string, ctx: ExtensionCommandContext) {
|
||||||
|
cancelRequested = true;
|
||||||
|
ctx.ui.notify("Abbruch angefordert — wird nach aktuellem Schritt gestoppt", "warning");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
pi.registerCommand("discard", {
|
||||||
|
description: "Verwirft PLAN.md und setzt den Planungsstatus zurück.",
|
||||||
|
handler: async function (_args: string, ctx: ExtensionCommandContext) {
|
||||||
|
await pi.exec("bash", ["-c", "rm -f PLAN.md"], { cwd: ctx.cwd });
|
||||||
|
ctx.ui.notify("PLAN.md gelöscht — Plan verworfen", "info");
|
||||||
|
finalNotify(ctx, "🗑 Plan verworfen", "Neu starten mit /plan oder /coder");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
pi.registerCommand("continue", {
|
||||||
|
description: "Nimmt unterbrochenen Prozess wieder auf — liest TASK.md, PLAN.md, git log und entscheidet den nächsten Schritt.",
|
||||||
|
handler: async function (_args: string, ctx: ExtensionCommandContext) {
|
||||||
|
await switchModel(pi, ctx, "llama-cpp-coder", "qwen3.5-coder");
|
||||||
|
ctx.ui.setStatus("continue", "Analysiere unterbrochenen Prozess…");
|
||||||
|
pi.sendUserMessage([
|
||||||
|
"Ein Prozess wurde unterbrochen. Analysiere den aktuellen Stand und führe ihn sinnvoll fort:",
|
||||||
|
"1. Lies TASK.md für den Auftrag",
|
||||||
|
"2. Lies PLAN.md falls vorhanden (war ein Plan in Arbeit?)",
|
||||||
|
"3. Führe 'git log --oneline -5' aus um zu sehen was bereits committed wurde",
|
||||||
|
"4. Entscheide: Muss noch implementiert werden? Ist ein Review fällig? Müssen Fixes nachgezogen werden?",
|
||||||
|
"5. Fahre direkt mit dem nächsten sinnvollen Schritt fort — kein langer Bericht, einfach weitermachen.",
|
||||||
|
].join("\n"));
|
||||||
|
await ctx.waitForIdle();
|
||||||
|
ctx.ui.setStatus("continue", "");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// ── Projekt-Scaffolding ──────────────────────────────────────────────────
|
// ── Projekt-Scaffolding ──────────────────────────────────────────────────
|
||||||
|
|
||||||
pi.registerCommand("new_project", {
|
pi.registerCommand("new_project", {
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ docker run -d \
|
||||||
"$IMAGE" \
|
"$IMAGE" \
|
||||||
-m "/hf_home/${MODEL_REL_PATH}" \
|
-m "/hf_home/${MODEL_REL_PATH}" \
|
||||||
--alias "${MODEL_ALIAS}" \
|
--alias "${MODEL_ALIAS}" \
|
||||||
-c 131072 \
|
-c 262144 \
|
||||||
-n 16384 \
|
-n 16384 \
|
||||||
--jinja \
|
--jinja \
|
||||||
--no-context-shift \
|
--no-context-shift \
|
||||||
|
|
@ -45,11 +45,11 @@ docker run -d \
|
||||||
-ngl 999 \
|
-ngl 999 \
|
||||||
-fa on \
|
-fa on \
|
||||||
--kv-unified \
|
--kv-unified \
|
||||||
--cache-type-k q8_0 \
|
--cache-type-k q4_0 \
|
||||||
--cache-type-v q8_0 \
|
--cache-type-v q4_0 \
|
||||||
--batch-size 1024 \
|
--batch-size 1024 \
|
||||||
--ubatch-size 512 \
|
--ubatch-size 512 \
|
||||||
--parallel 2 \
|
--parallel 1 \
|
||||||
--cont-batching \
|
--cont-batching \
|
||||||
--host 0.0.0.0 \
|
--host 0.0.0.0 \
|
||||||
--port "$CONTAINER_PORT"
|
--port "$CONTAINER_PORT"
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ docker run -d \
|
||||||
"$IMAGE" \
|
"$IMAGE" \
|
||||||
-m "/hf_home/${MODEL_REL_PATH}" \
|
-m "/hf_home/${MODEL_REL_PATH}" \
|
||||||
--alias "${MODEL_ALIAS}" \
|
--alias "${MODEL_ALIAS}" \
|
||||||
-c 131072 \
|
-c 262144 \
|
||||||
-n 8192 \
|
-n 8192 \
|
||||||
--jinja \
|
--jinja \
|
||||||
--no-context-shift \
|
--no-context-shift \
|
||||||
|
|
@ -45,8 +45,8 @@ docker run -d \
|
||||||
-ngl 999 \
|
-ngl 999 \
|
||||||
-fa on \
|
-fa on \
|
||||||
--kv-unified \
|
--kv-unified \
|
||||||
--cache-type-k q8_0 \
|
--cache-type-k q4_0 \
|
||||||
--cache-type-v q8_0 \
|
--cache-type-v q4_0 \
|
||||||
--batch-size 512 \
|
--batch-size 512 \
|
||||||
--ubatch-size 256 \
|
--ubatch-size 256 \
|
||||||
--parallel 1 \
|
--parallel 1 \
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue