summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.tasks/tasks-test.jsonl16
-rw-r--r--.tasks/tasks.jsonl4
-rw-r--r--AGENTS.md16
-rw-r--r--Omni/Task.hs7
-rw-r--r--Omni/Task/Core.hs39
5 files changed, 72 insertions, 10 deletions
diff --git a/.tasks/tasks-test.jsonl b/.tasks/tasks-test.jsonl
index 4844efa..8ceed04 100644
--- a/.tasks/tasks-test.jsonl
+++ b/.tasks/tasks-test.jsonl
@@ -1,8 +1,8 @@
-{"taskCreatedAt":"2025-11-09T13:17:05.312663095Z","taskDependencies":[],"taskId":"t-QcC8yE","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"Test task","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:17:05.312663095Z"}
-{"taskCreatedAt":"2025-11-09T13:17:05.323188568Z","taskDependencies":[],"taskId":"t-QcCbiq","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"Test task for list","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:17:05.323188568Z"}
-{"taskCreatedAt":"2025-11-09T13:17:05.324209028Z","taskDependencies":[],"taskId":"t-QcCbyT","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"First task","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:17:05.324209028Z"}
-{"taskCreatedAt":"2025-11-09T13:17:05.32448785Z","taskDependencies":[{"depId":"t-QcCbyT","depType":"Blocks"}],"taskId":"t-QcCbDo","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"Blocked task","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:17:05.32448785Z"}
-{"taskCreatedAt":"2025-11-09T13:17:05.325243708Z","taskDependencies":[],"taskId":"t-QcCbPA","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"Original task","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:17:05.325243708Z"}
-{"taskCreatedAt":"2025-11-09T13:17:05.326628931Z","taskDependencies":[{"depId":"t-QcCbPA","depType":"DiscoveredFrom"}],"taskId":"t-QcCcbV","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"Discovered work","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:17:05.326628931Z"}
-{"taskCreatedAt":"2025-11-09T13:17:05.327446419Z","taskDependencies":[],"taskId":"t-QcCcp7","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"Task A","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:17:05.327446419Z"}
-{"taskCreatedAt":"2025-11-09T13:17:05.327656741Z","taskDependencies":[{"depId":"t-QcCcp7","depType":"Related"}],"taskId":"t-QcCcsv","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"Task B","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:17:05.327656741Z"}
+{"taskCreatedAt":"2025-11-09T13:46:29.314441029Z","taskDependencies":[],"taskId":"t-S7ZI0f","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"Test task","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:46:29.314441029Z"}
+{"taskCreatedAt":"2025-11-09T13:46:29.324362657Z","taskDependencies":[],"taskId":"t-S7ZKAh","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"Test task for list","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:46:29.324362657Z"}
+{"taskCreatedAt":"2025-11-09T13:46:29.325509209Z","taskDependencies":[],"taskId":"t-S7ZKSL","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"First task","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:46:29.325509209Z"}
+{"taskCreatedAt":"2025-11-09T13:46:29.325806692Z","taskDependencies":[{"depId":"t-S7ZKSL","depType":"Blocks"}],"taskId":"t-S7ZKXz","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"Blocked task","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:46:29.325806692Z"}
+{"taskCreatedAt":"2025-11-09T13:46:29.326793243Z","taskDependencies":[],"taskId":"t-S7ZLdt","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"Original task","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:46:29.326793243Z"}
+{"taskCreatedAt":"2025-11-09T13:46:29.327096316Z","taskDependencies":[{"depId":"t-S7ZLdt","depType":"DiscoveredFrom"}],"taskId":"t-S7ZLim","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"Discovered work","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:46:29.327096316Z"}
+{"taskCreatedAt":"2025-11-09T13:46:29.327966636Z","taskDependencies":[],"taskId":"t-S7ZLwp","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"Task A","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:46:29.327966636Z"}
+{"taskCreatedAt":"2025-11-09T13:46:29.328165148Z","taskDependencies":[{"depId":"t-S7ZLwp","depType":"Related"}],"taskId":"t-S7ZLzC","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"Task B","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:46:29.328165148Z"}
diff --git a/.tasks/tasks.jsonl b/.tasks/tasks.jsonl
index 4f50e47..72da314 100644
--- a/.tasks/tasks.jsonl
+++ b/.tasks/tasks.jsonl
@@ -2,7 +2,7 @@
{"taskCreatedAt":"2025-11-08T20:03:53.429072631Z","taskDependencies":[],"taskId":"t-d4e5f6","taskNamespace":null,"taskParent":null,"taskStatus":"Done","taskTitle":"Move dev instructions from README.md to AGENTS.md","taskType":"WorkTask","taskUpdatedAt":"2025-11-08T20:06:22.732392229Z"}
{"taskCreatedAt":"2025-11-08T20:06:27.395834401Z","taskDependencies":[],"taskId":"t-g7h8i9","taskNamespace":null,"taskParent":null,"taskStatus":"Done","taskTitle":"Task ids should be shorter. Use the sqids package in haskell to generate ids","taskType":"WorkTask","taskUpdatedAt":"2025-11-08T21:00:37.311865046Z"}
{"taskCreatedAt":"2025-11-08T20:09:35.590622249Z","taskDependencies":[],"taskId":"t-j0k1L2","taskNamespace":null,"taskParent":null,"taskStatus":"Done","taskTitle":"Tasks should have an optional namespace associated with them. Namespaces are first class citizens in this monorepo","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:14:05.775741617Z"}
-{"taskCreatedAt":"2025-11-08T20:10:09.944217463Z","taskDependencies":[],"taskId":"t-m3n4o5","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"There should be a command to list all projects.","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:30:16.560596069Z"}
+{"taskCreatedAt":"2025-11-08T20:10:09.944217463Z","taskDependencies":[],"taskId":"t-m3n4o5","taskNamespace":null,"taskParent":null,"taskStatus":"Done","taskTitle":"There should be a command to list all projects.","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:44:57.393279815Z"}
{"taskCreatedAt":"2025-11-08T20:20:38.785442739Z","taskDependencies":[],"taskId":"t-p6q7r8","taskNamespace":null,"taskParent":null,"taskStatus":"Done","taskTitle":"Instruct agents too use git-branchless and a patch based workflow rather than traditional git commands if and when they need to record things in git.","taskType":"WorkTask","taskUpdatedAt":"2025-11-08T21:09:06.854871964Z"}
{"taskCreatedAt":"2025-11-08T20:22:20.116289616Z","taskDependencies":[],"taskId":"t-s9T0u1","taskNamespace":null,"taskParent":null,"taskStatus":"Done","taskTitle":"instruct agents to include tests with all new features and bug fixes","taskType":"WorkTask","taskUpdatedAt":"2025-11-08T21:24:54.004658966Z"}
{"taskCreatedAt":"2025-11-08T20:45:12.764939794Z","taskDependencies":[],"taskId":"t-v2w3x4","taskNamespace":null,"taskParent":null,"taskStatus":"Done","taskTitle":"instruct agents to run 'bild --test' and 'lint' for whatever namespace(s) they are working on after completing a task and fix any reported errors","taskType":"WorkTask","taskUpdatedAt":"2025-11-08T21:25:10.756670871Z"}
@@ -14,7 +14,7 @@
{"taskCreatedAt":"2025-11-09T13:05:06.718797697Z","taskDependencies":[],"taskId":"t-PpYZt2","taskNamespace":"Omni/Task.hs","taskParent":"t-PpXWsU","taskStatus":"Open","taskTitle":"Implement child ID generation (t-abc123.1)","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:05:06.718797697Z"}
{"taskCreatedAt":"2025-11-09T13:05:06.746734115Z","taskDependencies":[],"taskId":"t-PpZ6JC","taskNamespace":"Omni/Task.hs","taskParent":"t-PpXWsU","taskStatus":"Open","taskTitle":"Add child_counters storage","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:05:06.746734115Z"}
{"taskCreatedAt":"2025-11-09T13:05:06.774903465Z","taskDependencies":[],"taskId":"t-PpZe3X","taskNamespace":"Omni/Task.hs","taskParent":"t-PpXWsU","taskStatus":"Open","taskTitle":"Update createTask to auto-generate child IDs","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:05:06.774903465Z"}
-{"taskCreatedAt":"2025-11-09T13:05:06.802295008Z","taskDependencies":[],"taskId":"t-PpZlbL","taskNamespace":"Omni/Task.hs","taskParent":"t-PpXWsU","taskStatus":"Open","taskTitle":"Implement task tree visualization command","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:05:06.802295008Z"}
+{"taskCreatedAt":"2025-11-09T13:05:06.802295008Z","taskDependencies":[],"taskId":"t-PpZlbL","taskNamespace":"Omni/Task.hs","taskParent":"t-PpXWsU","taskStatus":"Done","taskTitle":"Implement task tree visualization command","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:47:12.411364105Z"}
{"taskCreatedAt":"2025-11-09T13:05:06.829842253Z","taskDependencies":[],"taskId":"t-PpZsm4","taskNamespace":"Omni/Task.hs","taskParent":"t-PpXWsU","taskStatus":"Open","taskTitle":"Implement task stats command","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:05:06.829842253Z"}
{"taskCreatedAt":"2025-11-09T13:05:06.85771202Z","taskDependencies":[],"taskId":"t-PpZzBA","taskNamespace":"Omni/Task.hs","taskParent":"t-PpXWsU","taskStatus":"Open","taskTitle":"Implement epic progress tracking","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:05:06.85771202Z"}
{"taskCreatedAt":"2025-11-09T13:05:06.88583862Z","taskDependencies":[],"taskId":"t-PpZGVf","taskNamespace":"Omni/Task.hs","taskParent":"t-PpXWsU","taskStatus":"Done","taskTitle":"Add filtering by type and parent (list improvements)","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:17:51.373969453Z"}
diff --git a/AGENTS.md b/AGENTS.md
index 5d4497a..5f28f2f 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -97,6 +97,22 @@ task deps <id>
Shows the dependency tree for a task.
+### View Task Tree
+```bash
+task tree [<id>]
+```
+
+Shows task hierarchy with visual status indicators:
+- `[ ]` - Open
+- `[~]` - In Progress
+- `[✓]` - Done
+
+Examples:
+```bash
+task tree # Show all epics with their children
+task tree t-abc123 # Show specific epic/task with its children
+```
+
### Export Tasks
```bash
task export [--flush]
diff --git a/Omni/Task.hs b/Omni/Task.hs
index 0aca674..ef912f9 100644
--- a/Omni/Task.hs
+++ b/Omni/Task.hs
@@ -39,6 +39,7 @@ Usage:
task ready
task update <id> <status>
task deps <id>
+ task tree [<id>]
task export [--flush]
task import -i <file>
task test
@@ -51,6 +52,7 @@ Commands:
ready Show ready tasks (not blocked)
update Update task status
deps Show dependency tree
+ tree Show task tree (epics with children, or all epics if no ID given)
export Export and consolidate tasks to JSONL
import Import tasks from JSONL file
test Run tests
@@ -158,6 +160,11 @@ move args
| args `Cli.has` Cli.command "deps" = do
tid <- getArgText args "id"
showDependencyTree tid
+ | args `Cli.has` Cli.command "tree" = do
+ maybeId <- case Cli.getArg args (Cli.argument "id") of
+ Nothing -> pure Nothing
+ Just idStr -> pure <| Just (T.pack idStr)
+ showTaskTree maybeId
| args `Cli.has` Cli.command "export" = do
exportTasks
putText "Exported and consolidated tasks to .tasks/tasks.jsonl"
diff --git a/Omni/Task/Core.hs b/Omni/Task/Core.hs
index 140d7dc..1a9cb2e 100644
--- a/Omni/Task/Core.hs
+++ b/Omni/Task/Core.hs
@@ -256,6 +256,45 @@ showDependencyTree tid = do
deps = filter (\t -> taskId t `elem` depIds) allTasks
traverse_ (\dep -> printTree allTasks dep (indent + 1)) deps
+-- Show task tree (epic with children, or all epics if no ID given)
+showTaskTree :: Maybe Text -> IO ()
+showTaskTree maybeId = do
+ tasks <- loadTasks
+ case maybeId of
+ Nothing -> do
+ -- Show all epics with their children
+ let epics = filter (\t -> taskType t == Epic) tasks
+ if null epics
+ then putText "No epics found"
+ else traverse_ (printEpicTree tasks) epics
+ Just tid -> do
+ -- Show specific task/epic with its children
+ case filter (\t -> taskId t == tid) tasks of
+ [] -> putText "Task not found"
+ (task : _) -> printEpicTree tasks task
+ where
+ printEpicTree :: [Task] -> Task -> IO ()
+ printEpicTree allTasks task = printTreeNode allTasks task 0
+
+ printTreeNode :: [Task] -> Task -> Int -> IO ()
+ printTreeNode allTasks task indent = do
+ let prefix = T.pack (replicate (indent * 2) ' ')
+ typeStr = case taskType task of
+ Epic -> "[Epic]"
+ WorkTask -> "[Task]"
+ statusStr = case taskStatus task of
+ Open -> "[ ]"
+ InProgress -> "[~]"
+ Done -> "[✓]"
+ nsStr = case taskNamespace task of
+ Nothing -> ""
+ Just ns -> " [" <> ns <> "]"
+ putText <| prefix <> taskId task <> " " <> typeStr <> " " <> statusStr <> " " <> taskTitle task <> nsStr
+
+ -- Find and print children (tasks with this task as parent)
+ let children = filter (\t -> taskParent t == Just (taskId task)) allTasks
+ traverse_ (\child -> printTreeNode allTasks child (indent + 1)) children
+
-- Helper to print a task
printTask :: Task -> IO ()
printTask t =