From b616e753da03d234c7e4e0a0ea50c9e192644cf9 Mon Sep 17 00:00:00 2001 From: Ben Sima Date: Fri, 28 Nov 2025 04:11:17 -0500 Subject: Add comments field to tasks for providing extra context All tests pass. Here's a summary of the changes I made: 1. **Added `Comment` data type** in `Omni/Task/Core.hs` with `commentTex 2. **Added `taskComments` field** to the `Task` type to store a list of 3. **Updated database schema** with a `comments TEXT` column (stored as 4. **Added SQL instances** for `[Comment]` to serialize/deserialize 5. **Added `addComment` function** to add timestamped comments to tasks 6. **Added CLI command** `task comment [--json]` 7. **Updated `showTaskDetailed`** to display comments in the detailed vi 8. **Added unit tests** for comments functionality 9. **Added CLI tests** for the comment command 10. **Fixed dependent files** (`Omni/Agent/Worker.hs` and `Omni/Jr/Web.h Task-Id: t-167 --- Omni/Task.hs | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 8 deletions(-) (limited to 'Omni/Task.hs') diff --git a/Omni/Task.hs b/Omni/Task.hs index 5e0595b..bf262ab 100644 --- a/Omni/Task.hs +++ b/Omni/Task.hs @@ -45,6 +45,7 @@ Usage: task create [options] task edit <id> [options] task delete <id> [--json] + task comment <id> <message> [--json] task list [options] task ready [--json] task show <id> [--json] @@ -63,6 +64,7 @@ Commands: create Create a new task or epic edit Edit an existing task delete Delete a task + comment Add a comment to a task list List all tasks ready Show ready tasks (not blocked) show Show detailed task information @@ -100,6 +102,7 @@ Arguments: <title> Task title <id> Task ID <status> Task status (draft, open, in-progress, review, approved, done) + <message> Comment message <file> JSONL file to import |] @@ -259,6 +262,13 @@ move' args if isJsonMode args then outputSuccess ("Deleted task " <> tid) else putStrLn <| "Deleted task: " <> T.unpack tid + | args `Cli.has` Cli.command "comment" = do + tid <- getArgText args "id" + message <- getArgText args "message" + updatedTask <- addComment tid message + if isJsonMode args + then outputJson updatedTask + else putStrLn <| "Added comment to task: " <> T.unpack tid | args `Cli.has` Cli.command "list" = do maybeType <- case Cli.getArg args (Cli.longOption "type") of Nothing -> pure Nothing @@ -539,9 +549,10 @@ unitTests = taskStatus = Open, taskPriority = P2, taskDependencies = [], + taskDescription = "Child 3", + taskComments = [], taskCreatedAt = taskCreatedAt child1, - taskUpdatedAt = taskUpdatedAt child1, - taskDescription = "Child 3" + taskUpdatedAt = taskUpdatedAt child1 } saveTask child3 @@ -601,7 +612,7 @@ unitTests = Test.unit "can create task with lowercase ID" <| do -- This verifies that lowercase IDs are accepted and not rejected let lowerId = "t-lowercase" - let task = Task lowerId "Lower" WorkTask Nothing Nothing Open P2 [] "Lower description" (read "2025-01-01 00:00:00 UTC") (read "2025-01-01 00:00:00 UTC") + let task = Task lowerId "Lower" WorkTask Nothing Nothing Open P2 [] "Lower description" [] (read "2025-01-01 00:00:00 UTC") (read "2025-01-01 00:00:00 UTC") saveTask task tasks <- loadTasks case findTask lowerId tasks of @@ -609,7 +620,7 @@ unitTests = Nothing -> Test.assertFailure "Should find task with lowercase ID", Test.unit "generateId produces valid ID" <| do tid <- generateId - let task = Task tid "Auto" WorkTask Nothing Nothing Open P2 [] "Auto description" (read "2025-01-01 00:00:00 UTC") (read "2025-01-01 00:00:00 UTC") + let task = Task tid "Auto" WorkTask Nothing Nothing Open P2 [] "Auto description" [] (read "2025-01-01 00:00:00 UTC") (read "2025-01-01 00:00:00 UTC") saveTask task tasks <- loadTasks case findTask tid tasks of @@ -633,7 +644,7 @@ unitTests = Test.unit "lowercase ID does not clash with existing uppercase ID" <| do -- Setup: Create task with Uppercase ID let upperId = "t-UPPER" - let task1 = Task upperId "Upper Task" WorkTask Nothing Nothing Open P2 [] "Upper description" (read "2025-01-01 00:00:00 UTC") (read "2025-01-01 00:00:00 UTC") + let task1 = Task upperId "Upper Task" WorkTask Nothing Nothing Open P2 [] "Upper description" [] (read "2025-01-01 00:00:00 UTC") (read "2025-01-01 00:00:00 UTC") saveTask task1 -- Action: Try to create task with Lowercase ID (same letters) @@ -644,7 +655,7 @@ unitTests = -- treating them as the same is dangerous. let lowerId = "t-upper" - let task2 = Task lowerId "Lower Task" WorkTask Nothing Nothing Open P2 [] "Lower description" (read "2025-01-01 00:00:01 UTC") (read "2025-01-01 00:00:01 UTC") + let task2 = Task lowerId "Lower Task" WorkTask Nothing Nothing Open P2 [] "Lower description" [] (read "2025-01-01 00:00:01 UTC") (read "2025-01-01 00:00:01 UTC") saveTask task2 tasks <- loadTasks @@ -684,7 +695,35 @@ unitTests = Test.unit "FromHttpApiData Status: valid values parse correctly" <| do (parseQueryParam "Open" :: Either Text Status) Test.@?= Right Open (parseQueryParam "InProgress" :: Either Text Status) Test.@?= Right InProgress - (parseQueryParam "Done" :: Either Text Status) Test.@?= Right Done + (parseQueryParam "Done" :: Either Text Status) Test.@?= Right Done, + Test.unit "can add comment to task" <| do + task <- createTask "Task with comment" WorkTask Nothing Nothing P2 [] "Description" + updatedTask <- addComment (taskId task) "This is a test comment" + length (taskComments updatedTask) Test.@?= 1 + case taskComments updatedTask of + (c : _) -> commentText c Test.@?= "This is a test comment" + [] -> Test.assertFailure "Expected at least one comment", + Test.unit "can add multiple comments to task" <| do + task <- createTask "Task with comments" WorkTask Nothing Nothing P2 [] "Description" + _ <- addComment (taskId task) "First comment" + updatedTask <- addComment (taskId task) "Second comment" + length (taskComments updatedTask) Test.@?= 2 + case taskComments updatedTask of + (c1 : c2 : _) -> do + commentText c1 Test.@?= "First comment" + commentText c2 Test.@?= "Second comment" + _ -> Test.assertFailure "Expected at least two comments", + Test.unit "comments are persisted" <| do + task <- createTask "Persistent comments" WorkTask Nothing Nothing P2 [] "Description" + _ <- addComment (taskId task) "Persisted comment" + tasks <- loadTasks + case findTask (taskId task) tasks of + Nothing -> Test.assertFailure "Could not reload task" + Just reloaded -> do + length (taskComments reloaded) Test.@?= 1 + case taskComments reloaded of + (c : _) -> commentText c Test.@?= "Persisted comment" + [] -> Test.assertFailure "Expected at least one comment" ] -- | Test CLI argument parsing to ensure docopt string matches actual usage @@ -918,5 +957,20 @@ cliTests = args `Cli.has` Cli.command "create" Test.@?= True args `Cli.has` Cli.longOption "json" Test.@?= True Cli.getArg args (Cli.longOption "priority") Test.@?= Just "1" - Cli.getArg args (Cli.longOption "namespace") Test.@?= Just "Omni/Task" + Cli.getArg args (Cli.longOption "namespace") Test.@?= Just "Omni/Task", + Test.unit "comment command" <| do + let result = Docopt.parseArgs help ["comment", "t-abc123", "This is a comment"] + case result of + Left err -> Test.assertFailure <| "Failed to parse 'comment': " <> show err + Right args -> do + args `Cli.has` Cli.command "comment" Test.@?= True + Cli.getArg args (Cli.argument "id") Test.@?= Just "t-abc123" + Cli.getArg args (Cli.argument "message") Test.@?= Just "This is a comment", + Test.unit "comment with --json flag" <| do + let result = Docopt.parseArgs help ["comment", "t-abc123", "Test comment", "--json"] + case result of + Left err -> Test.assertFailure <| "Failed to parse 'comment --json': " <> show err + Right args -> do + args `Cli.has` Cli.command "comment" Test.@?= True + args `Cli.has` Cli.longOption "json" Test.@?= True ] -- cgit v1.2.3