summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.tasks/tasks-test.jsonl16
-rw-r--r--.tasks/tasks.jsonl4
-rw-r--r--AGENTS.md11
-rw-r--r--Omni/Task.hs19
-rw-r--r--Omni/Task/Core.hs13
5 files changed, 43 insertions, 20 deletions
diff --git a/.tasks/tasks-test.jsonl b/.tasks/tasks-test.jsonl
index 6d3f807..4844efa 100644
--- a/.tasks/tasks-test.jsonl
+++ b/.tasks/tasks-test.jsonl
@@ -1,8 +1,8 @@
-{"taskCreatedAt":"2025-11-09T12:53:20.744438371Z","taskDependencies":[],"taskId":"t-OEcNhO","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"Test task","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T12:53:20.744438371Z"}
-{"taskCreatedAt":"2025-11-09T12:53:20.754801027Z","taskDependencies":[],"taskId":"t-OEcPYX","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"Test task for list","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T12:53:20.754801027Z"}
-{"taskCreatedAt":"2025-11-09T12:53:20.755885939Z","taskDependencies":[],"taskId":"t-OEcQgs","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"First task","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T12:53:20.755885939Z"}
-{"taskCreatedAt":"2025-11-09T12:53:20.756171782Z","taskDependencies":[{"depId":"t-OEcQgs","depType":"Blocks"}],"taskId":"t-OEcQl4","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"Blocked task","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T12:53:20.756171782Z"}
-{"taskCreatedAt":"2025-11-09T12:53:20.756913699Z","taskDependencies":[],"taskId":"t-OEcQx2","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"Original task","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T12:53:20.756913699Z"}
-{"taskCreatedAt":"2025-11-09T12:53:20.757214632Z","taskDependencies":[{"depId":"t-OEcQx2","depType":"DiscoveredFrom"}],"taskId":"t-OEcQBT","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"Discovered work","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T12:53:20.757214632Z"}
-{"taskCreatedAt":"2025-11-09T12:53:20.75794819Z","taskDependencies":[],"taskId":"t-OEcQNI","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"Task A","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T12:53:20.75794819Z"}
-{"taskCreatedAt":"2025-11-09T12:53:20.758169712Z","taskDependencies":[{"depId":"t-OEcQNI","depType":"Related"}],"taskId":"t-OEcQRi","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"Task B","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T12:53:20.758169712Z"}
+{"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"}
diff --git a/.tasks/tasks.jsonl b/.tasks/tasks.jsonl
index c6b7b6c..0ff032c 100644
--- a/.tasks/tasks.jsonl
+++ b/.tasks/tasks.jsonl
@@ -1,7 +1,7 @@
{"taskCreatedAt":"2025-11-08T20:03:50.230851965Z","taskDependencies":[],"taskId":"t-a1b2c3","taskNamespace":null,"taskParent":null,"taskStatus":"Done","taskTitle":"Show help text when task invoked without args","taskType":"WorkTask","taskUpdatedAt":"2025-11-08T20:06:02.605878048Z"}
{"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":"Open","taskTitle":"Tasks should have an optional namespace associated with them. Namespaces are first class citizens in this monorepo","taskType":"WorkTask","taskUpdatedAt":"2025-11-08T20:09:35.590622249Z"}
+{"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-08T20:10:09.944217463Z"}
{"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"}
@@ -17,7 +17,7 @@
{"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.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":"Open","taskTitle":"Add filtering by type and parent (list improvements)","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:05:06.88583862Z"}
+{"taskCreatedAt":"2025-11-09T13:05:06.88583862Z","taskDependencies":[],"taskId":"t-PpZGVf","taskNamespace":"Omni/Task.hs","taskParent":"t-PpXWsU","taskStatus":"InProgress","taskTitle":"Add filtering by type and parent (list improvements)","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:14:05.997641101Z"}
{"taskCreatedAt":"2025-11-09T13:05:18.344932105Z","taskDependencies":[],"taskId":"t-PqLLXk","taskNamespace":"Omni/Task.hs","taskParent":"t-PpXWsU","taskStatus":"Done","taskTitle":"Implement epic and task types","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:05:18.406381682Z"}
{"taskCreatedAt":"2025-11-09T13:05:18.445111257Z","taskDependencies":[],"taskId":"t-PqMc17","taskNamespace":"Omni/Task.hs","taskParent":"t-PpXWsU","taskStatus":"Done","taskTitle":"Add enhanced dependency types (blocks, discovered-from, related)","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:05:18.50495798Z"}
{"taskCreatedAt":"2025-11-09T13:05:18.543055749Z","taskDependencies":[],"taskId":"t-PqMBuS","taskNamespace":"Omni/Task.hs","taskParent":"t-PpXWsU","taskStatus":"Done","taskTitle":"Protect production database from tests","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T13:05:18.602787251Z"}
diff --git a/AGENTS.md b/AGENTS.md
index ab91f8b..653e8f3 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -52,13 +52,18 @@ The `--namespace` option associates the task with a specific namespace in the mo
### List Tasks
```bash
-task list [--project=<project>]
+task list [--type=<type>] [--parent=<id>] [--status=<status>] [--namespace=<ns>]
```
Examples:
```bash
-task list # All tasks
-task list --project=auth # Filter by project
+task list # All tasks
+task list --type=epic # All epics
+task list --parent=t-abc123 # All tasks in an epic
+task list --status=open # All open tasks
+task list --status=done # All completed tasks
+task list --namespace="Omni/Task" # All tasks for a namespace
+task list --parent=t-abc123 --status=open # Combine filters: open tasks in epic
```
### Get Ready Work
diff --git a/Omni/Task.hs b/Omni/Task.hs
index f10ae25..ae854a7 100644
--- a/Omni/Task.hs
+++ b/Omni/Task.hs
@@ -35,7 +35,7 @@ task
Usage:
task init
task create <title> [--type=<type>] [--parent=<id>] [--deps=<ids>] [--dep-type=<type>] [--discovered-from=<id>] [--namespace=<ns>]
- task list [--type=<type>] [--parent=<id>]
+ task list [--type=<type>] [--parent=<id>] [--status=<status>] [--namespace=<ns>]
task ready
task update <id> <status>
task deps <id>
@@ -59,6 +59,7 @@ Options:
-h --help Show this help
--type=<type> Task type: epic or task (default: task)
--parent=<id> Parent epic ID
+ --status=<status> Filter by status: open, in-progress, done
--deps=<ids> Comma-separated list of dependency IDs
--dep-type=<type> Dependency type: blocks, discovered-from, parent-child, related (default: blocks)
--discovered-from=<id> Shortcut for --deps=<id> --dep-type=discovered-from
@@ -124,7 +125,19 @@ move args
maybeParent <- case Cli.getArg args (Cli.longOption "parent") of
Nothing -> pure Nothing
Just p -> pure <| Just (T.pack p)
- tasks <- listTasks maybeType maybeParent
+ maybeStatus <- case Cli.getArg args (Cli.longOption "status") of
+ Nothing -> pure Nothing
+ Just "open" -> pure <| Just Open
+ Just "in-progress" -> pure <| Just InProgress
+ Just "done" -> pure <| Just Done
+ Just other -> panic <| "Invalid status: " <> T.pack other <> ". Use: open, in-progress, or done"
+ maybeNamespace <- case Cli.getArg args (Cli.longOption "namespace") of
+ Nothing -> pure Nothing
+ Just ns -> do
+ let validNs = Namespace.fromHaskellModule ns
+ nsPath = T.pack <| Namespace.toPath validNs
+ pure <| Just nsPath
+ tasks <- listTasks maybeType maybeParent maybeStatus maybeNamespace
traverse_ printTask tasks
| args `Cli.has` Cli.command "ready" = do
tasks <- getReadyTasks
@@ -184,7 +197,7 @@ unitTests =
null (taskDependencies task) Test.@?= True,
Test.unit "can list tasks" <| do
_ <- createTask "Test task for list" WorkTask Nothing Nothing []
- tasks <- listTasks Nothing Nothing
+ tasks <- listTasks Nothing Nothing Nothing Nothing
not (null tasks) Test.@?= True,
Test.unit "ready tasks exclude blocked ones" <| do
task1 <- createTask "First task" WorkTask Nothing Nothing []
diff --git a/Omni/Task/Core.hs b/Omni/Task/Core.hs
index 6285ef7..140d7dc 100644
--- a/Omni/Task/Core.hs
+++ b/Omni/Task/Core.hs
@@ -5,7 +5,6 @@
module Omni.Task.Core where
import Alpha
-import Control.Monad ((>>=))
import Data.Aeson (FromJSON, ToJSON, decode, encode)
import qualified Data.Aeson as Aeson
import qualified Data.Aeson.KeyMap as KM
@@ -210,20 +209,26 @@ updateTaskStatus tid newStatus = do
TIO.writeFile tasksFile ""
traverse_ saveTask updatedTasks
--- List tasks, optionally filtered by type or parent
-listTasks :: Maybe TaskType -> Maybe Text -> IO [Task]
-listTasks maybeType maybeParent = do
+-- List tasks, optionally filtered by type, parent, status, or namespace
+listTasks :: Maybe TaskType -> Maybe Text -> Maybe Status -> Maybe Text -> IO [Task]
+listTasks maybeType maybeParent maybeStatus maybeNamespace = do
tasks <- loadTasks
let filtered =
tasks
|> filterByType maybeType
|> filterByParent maybeParent
+ |> filterByStatus maybeStatus
+ |> filterByNamespace maybeNamespace
pure filtered
where
filterByType Nothing ts = ts
filterByType (Just typ) ts = filter (\t -> taskType t == typ) ts
filterByParent Nothing ts = ts
filterByParent (Just pid) ts = filter (\t -> taskParent t == Just pid) ts
+ filterByStatus Nothing ts = ts
+ filterByStatus (Just status) ts = filter (\t -> taskStatus t == status) ts
+ filterByNamespace Nothing ts = ts
+ filterByNamespace (Just ns) ts = filter (\t -> taskNamespace t == Just ns) ts
-- Get ready tasks (not blocked by dependencies)
getReadyTasks :: IO [Task]