V17 / KANBAN

kanban_prepare_operation

Application-bound scenario operation. Named XML inputs control execution; this is not a main-agent tool.

See source registrationIntroduced in V16V17

What it does

Read the exact source snapshot below for its parameters, behavior, and registration.

Read the implementation

This page describes the selected release. The source link points to the same exact commit; it cannot silently open a newer implementation.

Lilith/Core/Kanban/KanbanOperations.cs ↗

Implementation code

Read the implementation from this version's exact source snapshot. Helper links below stay on the same release.

Lilith/Core/Kanban/KanbanOperations.cs

C#
using Lilith.Core.Commands;
using Lilith.Core.Projects;

namespace Lilith.Core.Kanban;

internal static class KanbanOperations
{
    internal static readonly string[] Names = ["kanban_prepare_operation", "kanban_status_operation"];
    internal static Task<string> ExecuteAsync(ProviderClient provider, string name, IReadOnlyDictionary<string, string> inputs, CancellationToken ct)
    {
        ct.ThrowIfCancellationRequested();
        var session = provider.Kanban ?? throw new InvalidOperationException("Kanban is not configured.");
        string Get(string key) => inputs.GetValueOrDefault(key, "");
        var result = name switch
        {
            "kanban_prepare_operation" => session.Prepare(Get("name"), Get("kind"), Get("prd"), Get("architecture"),
                int.TryParse(Get("repair_limit"), out int limit) ? limit : throw new ArgumentException("repair_limit must be an integer.")),
            "kanban_status_operation" => session.Store.Get(Get("id")),
            _ => throw new ArgumentException("Unknown Kanban operation.")
        };
        return Task.FromResult(ProjectJson.Serialize(new { result.Id, result.State, result.Message, board = session.Dashboard.PublicUrl,
            viewer = "Use the host /kanban command to open the authenticated viewer. Its control token is not supplied to model calls.",
            command = result.State == "waiting_plan" ? $"/kanban-approve {result.Id} {result.Revision}" : $"/kanban-status {result.Id}" }));
    }
}

public static class KanbanCommands
{
    public static void Register(ChatCommandRegistry commands, Func<ProviderClient> provider)
    {
        KanbanSession Session() => provider().Kanban ?? throw new InvalidOperationException("Kanban unavailable.");
        commands.Register("kanban", "Show the local board dashboard and saved runs.", c => { c.RequireNoArguments(); return Session().Dashboard.Url + "\n" + ProjectJson.Serialize(Session().Store.List().Select(b => new { b.Id, b.Name, b.State, b.Message })); });
        commands.Register("kanban-status", "Inspect one board and all task details.", c => System.Text.Json.JsonSerializer.Serialize(Session().Store.Get(c.Arguments.Trim()), KanbanStore.Json));
        commands.Register("kanban-pause", "Pause before the next subagent; return to chat when paused.", c => Session().Pause(c.Arguments.Trim()));
        commands.Register("kanban-resume", "Explicitly resume one approved board; never grants code execution.", c => Session().Resume(c.Arguments.Trim()));
        commands.RegisterAsync("kanban-cancel", "Cancel a run while retaining private drafts and evidence.", async c => new(true, await Session().CancelAsync(c.Arguments.Trim(),c.CancellationToken)));
        commands.Register("kanban-approve", "Approve the exact reviewed board: ID REVISION.", c =>
        {
            var args = c.Arguments.Split(' ', StringSplitOptions.RemoveEmptyEntries);
            if (args.Length != 2 || !long.TryParse(args[1], out long revision)) throw new ArgumentException("Use /kanban-approve ID REVISION.");
            return Session().Approve(args[0], revision);
        });
        commands.RegisterAsync("kanban-test", "Explicit execution decision for this board's project; up to ten runtime rounds.", async c => new(true, await provider().TestKanbanAsync(c.Arguments.Trim(), c.CancellationToken)));
        commands.RegisterAsync("kanban-publish", "Prepare separate exact publication for this qualified board.", async c => new(true, await provider().PublishKanbanAsync(c.Arguments.Trim(), c.CancellationToken)));
        commands.Register("kanban-publication-review", "Read the complete exact publication proposal for a board.", c => Session().ReadPublication(c.Arguments.Trim()));
        commands.RegisterAsync("kanban-publication-approve", "User approval of a board's already prepared exact workspace proposal.", async c => new(true, await provider().ApproveKanbanPublicationAsync(c.Arguments.Trim(), c.CancellationToken)));
        commands.RegisterAsync("kanban-wait", "Wait for the current worker to pause or reach its next host decision; no permissions granted.", async c =>
        { c.RequireNoArguments(); while (Session().IsWorking) await Task.Delay(200, c.CancellationToken); return new(true, "Kanban reached a host decision or stopped."); });
        KanbanBoard Pending(string state)
        {
            var boards = Session().Store.List().Where(b => b.State == state).ToArray();
            return boards.Length == 1 ? boards[0] : throw new InvalidOperationException("Expected exactly one " + state + " board. Use an explicit ID.");
        }
        commands.Register("kanban-approve-pending", "Scripted user decision: approve the single pending source plan, not code execution.", c => { c.RequireNoArguments(); var b = Pending("waiting_plan"); return Session().Approve(b.Id,b.Revision); });
        commands.RegisterAsync("kanban-test-pending", "Explicit scripted user execution decision for the single pending Kanban project.", async c => { c.RequireNoArguments(); return new(true, await provider().TestKanbanAsync(Pending("waiting_test").Id,c.CancellationToken)); });
        commands.RegisterAsync("kanban-publish-pending", "Prepare exact publication for the single qualified board; does not approve workspace writes.", async c => { c.RequireNoArguments(); return new(true, await provider().PublishKanbanAsync(Pending("waiting_publication").Id,c.CancellationToken)); });
        commands.RegisterAsync("kanban-publication-approve-pending", "Scripted user decision approving the single prepared exact board publication.", async c => { c.RequireNoArguments(); return new(true, await provider().ApproveKanbanPublicationAsync(Pending("waiting_publication").Id,c.CancellationToken)); });
    }
}

Scenarios using this tool