Added documentation for API, architecture, configuration, database, development guide, testing, and navigation. Includes helper scripts, diagrams, and guides for NFO files and migration.
155 lines
5.7 KiB
C#
155 lines
5.7 KiB
C#
#!/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<string> RunCopilot(IEnumerable<string> extraArgs, string prompt)
|
|
{
|
|
var output = new StringBuilder();
|
|
|
|
var argList = new List<string> { "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<string>(), $"/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<string>(), $"/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.");
|