Let task agents create and manage subtasks

This commit is contained in:
Ian Gulliver
2026-10-16 10:36:38 -07:00
parent fd63a0dfbc
commit 0f2415570c
30 changed files with 1067 additions and 287 deletions
+21 -3
View File
@@ -2,7 +2,7 @@ import { get, post, type ByreEvent, type Project, type Schemas, type Task } from
import { errorText, h, replace, showAlert } from "./dom.js";
import { markdown } from "./markdown.js";
import { models } from "./task.js";
import { currentSession, focusedProject, focusTask, on, onEach, state, taskState } from "./state.js";
import { currentSession, findTask, focusedProject, focusTask, on, onEach, state, taskState } from "./state.js";
import { taskStyle } from "./taskcolor.js";
type Todo = Schemas["Todo"];
@@ -106,6 +106,21 @@ function projectBlock(project: Project, todo: Todo | string | undefined, startin
);
}
function nested(active: Task[]): { task: Task; depth: number }[] {
const ids = new Set(active.map((t) => t.id));
const out: { task: Task; depth: number }[] = [];
const add = (task: Task, depth: number) => {
out.push({ task, depth });
for (const child of active.filter((t) => t.parent_task_id === task.id).reverse()) {
add(child, depth + 1);
}
};
for (const task of active.filter((t) => t.parent_task_id === undefined || !ids.has(t.parent_task_id))) {
add(task, 0);
}
return out;
}
function tasksBlock(active: Task[]): HTMLElement {
if (active.length === 0) {
return h("p", { class: "empty-row" }, "No active tasks.");
@@ -113,12 +128,13 @@ function tasksBlock(active: Task[]): HTMLElement {
return h(
"ul",
{ class: "task-list" },
...active.map((task) => {
...nested(active).map(({ task, depth }) => {
const ts = taskState(task);
const count = state.inbox.filter((item) => item.task_id === task.id).length;
const classes = [task.id === state.selectedTaskId && "selected", task.parent_task_id !== undefined && "subtask"].filter(Boolean).join(" ");
return h(
"li",
{ class: task.id === state.selectedTaskId ? "selected" : "", style: taskStyle(task.id) },
{ class: classes, style: `${taskStyle(task.id)}; --depth: ${depth}` },
h(
"a",
{
@@ -128,10 +144,12 @@ function tasksBlock(active: Task[]): HTMLElement {
focusTask(task.id);
},
},
task.parent_task_id !== undefined && h("span", { class: "subtask-mark", title: `Subtask of ${findTask(task.parent_task_id)?.title ?? task.parent_task_id}` }, "↳"),
h("span", { class: `dot ${ts ?? "none"}`, title: ts ?? "no session" }),
h("span", { class: "task-title task-name" }, task.title),
count > 0 && h("span", { class: "badge", title: "inbox items" }, count),
task.merge_queued_at !== undefined && h("span", { class: "task-state warn", title: "Waiting for the merge in progress in this project to finish" }, "merge held"),
task.merging_into_parent && h("span", { class: "task-state warn", title: "Waiting for the parent task's agent to merge it" }, "merging into parent"),
h("span", { class: "task-state" }, ts ?? "no agent"),
),
);