feat(tasks): TUI task board
Add a cross-project task board to the TUI (T / palette): a roster fetched from GET /tasks with vim-style nav, refresh, and an enter-to-open detail pane (goal, criteria, paths, links, notes) built from the list payload — no extra fetch. Server: TaskService.listAll() and a project-optional GET /tasks (scoped with ?project=, whole board without; recent-first). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -55,6 +55,16 @@ class TaskService(
|
||||
fun list(projectId: ProjectId): List<Task> =
|
||||
board(projectId).filterValues { !it.deleted }.map { (id, state) -> Task(id, state) }
|
||||
|
||||
/**
|
||||
* Every task across every project, for a cross-project board. Enumerates the per-project task
|
||||
* streams ([TaskStreams.PREFIX]) and rebuilds each — there is no global task stream, so this is
|
||||
* the union of the per-project boards.
|
||||
*/
|
||||
fun listAll(): List<Task> =
|
||||
eventStore.allSessionIds()
|
||||
.filter { it.value.startsWith(TaskStreams.PREFIX) }
|
||||
.flatMap { stream -> list(ProjectId(stream.value.removePrefix(TaskStreams.PREFIX))) }
|
||||
|
||||
fun getTask(taskId: TaskId): Task? {
|
||||
val state = board(projectOf(taskId))[taskId] ?: return null
|
||||
return if (state.deleted) null else Task(taskId, state)
|
||||
|
||||
@@ -72,6 +72,14 @@ class TaskServiceTest {
|
||||
assertNull(service.claim(TaskId("auth-999"), "claude-opus"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `listAll spans every project's stream`() = runBlocking {
|
||||
service.createTask(project, "a", "g")
|
||||
service.createTask(ProjectId("billing"), "b", "g")
|
||||
|
||||
assertEquals(setOf("auth-1", "billing-1"), service.listAll().map { it.taskId.value }.toSet())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `keys keep incrementing even after deletion`() = runBlocking {
|
||||
val first = service.createTask(project, "one", "g").taskId
|
||||
|
||||
Reference in New Issue
Block a user