From 24ea12bbaf7359d79e197043b0151c00b3928f08 Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 23 May 2026 21:19:15 +0200 Subject: [PATCH] Add Docs/runner.csx --- docs/runner.csx | 154 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 docs/runner.csx diff --git a/docs/runner.csx b/docs/runner.csx new file mode 100644 index 0000000..45ae2a1 --- /dev/null +++ b/docs/runner.csx @@ -0,0 +1,154 @@ +#!/usr/bin/env dotnet-script +#nullable enable + +using System; +using System.IO; +using System.Diagnostics; +using System.Threading; +using System.Text; +using System.Text.RegularExpressions; +using System.Linq; +using System.Collections.Generic; + +// ── Ctrl+C: kill active process and exit cleanly ────────────────────────────── +var cts = new CancellationTokenSource(); +Process? activeProcess = null; + +Console.CancelKeyPress += (_, e) => +{ + e.Cancel = true; + Console.WriteLine("\n[runner] Interrupted — shutting down..."); + cts.Cancel(); + try { activeProcess?.Kill(entireProcessTree: true); } catch { } +}; + +// ── Paths ───────────────────────────────────────────────────────────────────── +var repoRoot = Directory.GetCurrentDirectory(); +var tasksFile = Path.Combine(repoRoot, "Docs", "Tasks.md"); + +if (!File.Exists(tasksFile)) +{ + Console.Error.WriteLine($"[runner] ERROR: Tasks.md not found at {tasksFile}"); + Console.Error.WriteLine("[runner] Run this script from the repository root."); + Environment.Exit(1); +} + +// ── Read & split by "---" separator lines ──────────────────────────────────── +var content = File.ReadAllText(tasksFile); +var items = Regex + .Split(content, @"\r?\n---\r?\n") + .Select(s => s.Trim()) + .Where(s => s.Length > 0) + .ToList(); + +Console.WriteLine($"[runner] Found {items.Count} section(s) in Tasks.md"); + +// ── Helper: run copilot and stream output, return full output ───────────────── +async Task RunCopilot(IEnumerable extraArgs, string prompt) +{ + var output = new StringBuilder(); + + var argList = new List { "launch", "copilot", "--model", "minimax-m2.7:cloud", "--yes", "--", "--allow-all-tools" }; + argList.AddRange(extraArgs); + argList.Add("-p"); + argList.Add(prompt); + + var psi = new ProcessStartInfo("ollama") + { + WorkingDirectory = repoRoot, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + }; + foreach (var a in argList) + psi.ArgumentList.Add(a); + + activeProcess = new Process { StartInfo = psi }; + + activeProcess.OutputDataReceived += (_, e) => + { + if (e.Data is null) return; + Console.WriteLine(e.Data); + output.AppendLine(e.Data); + }; + activeProcess.ErrorDataReceived += (_, e) => + { + if (e.Data is null) return; + Console.Error.WriteLine(e.Data); + output.AppendLine(e.Data); + }; + + activeProcess.Start(); + activeProcess.BeginOutputReadLine(); + activeProcess.BeginErrorReadLine(); + + await activeProcess.WaitForExitAsync(cts.Token); + activeProcess = null; + + return output.ToString(); +} + +// ── Main loop ───────────────────────────────────────────────────────────────── +for (int i = 0; i < items.Count; i++) +{ + var item = items[i]; + if (cts.IsCancellationRequested) break; + + Console.WriteLine(); + Console.WriteLine("[runner] ══════════════════════════════════════════════"); + Console.WriteLine($"[runner] Task:\n{item}"); + Console.WriteLine("[runner] ══════════════════════════════════════════════"); + Console.WriteLine(); + + // Step 1 — run the task prompt + await RunCopilot(Enumerable.Empty(), $"/caveman full"); + await RunCopilot(new[] { "--continue" }, $"read ./Docs/instructions.md. {item}"); + if (cts.IsCancellationRequested) break; + + // Step 2 — confirm completion in the same chat session + Console.WriteLine("\n[runner] Asking for task confirmation...\n"); + var confirmation = await RunCopilot( + new[] { "--continue" }, + "are you sure tasks is done. reply with yes" + ); + if (cts.IsCancellationRequested) break; + + // Step 3 — check for "yes" in the reply, with retry logic for issue resolution + int maxRetries = 3; + int retryCount = 0; + bool taskConfirmed = confirmation.Contains("yes", StringComparison.OrdinalIgnoreCase); + + while (!taskConfirmed && retryCount < maxRetries) + { + retryCount++; + Console.WriteLine($"\n[runner] Attempt {retryCount}/{maxRetries}: Resolving remaining issues and running tests...\n"); + + confirmation = await RunCopilot( + new[] { "--continue" }, + "resolve any remaining issues, make sure all tests are running and pass. then confirm with yes if done" + ); + if (cts.IsCancellationRequested) break; + + taskConfirmed = confirmation.Contains("yes", StringComparison.OrdinalIgnoreCase); + } + + if (!taskConfirmed) + { + Console.WriteLine($"\n[runner] Task not confirmed as done after {maxRetries} attempts. Stopping."); + break; + } + + // Step 4 — commit the work + Console.WriteLine("\n[runner] Task confirmed. Making git commit...\n"); + + await RunCopilot(Enumerable.Empty(), $"/caveman full"); + await RunCopilot(new[] { "--continue" }, "make git commit"); + if (cts.IsCancellationRequested) break; + + // Step 5 — remove completed task from Tasks.md + var remaining = items.Skip(i + 1).ToList(); + File.WriteAllText(tasksFile, string.Join("\n\n---\n\n", remaining)); + Console.WriteLine("[runner] Removed completed task from Tasks.md"); +} + +Console.WriteLine("\n[runner] Finished.");