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/Core.hs | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 3 deletions(-) (limited to 'Omni/Task') diff --git a/Omni/Task/Core.hs b/Omni/Task/Core.hs index 18f80e2..1be97b9 100644 --- a/Omni/Task/Core.hs +++ b/Omni/Task/Core.hs @@ -36,6 +36,7 @@ data Task = Task taskPriority :: Priority, -- Priority level (0-4) taskDependencies :: [Dependency], -- List of dependencies with types taskDescription :: Text, -- Required description + taskComments :: [Comment], -- Timestamped comments for extra context taskCreatedAt :: UTCTime, taskUpdatedAt :: UTCTime } @@ -123,6 +124,13 @@ data Fact = Fact } deriving (Show, Eq, Generic) +-- Comment for task notes/context +data Comment = Comment + { commentText :: Text, + commentCreatedAt :: UTCTime + } + deriving (Show, Eq, Generic) + instance ToJSON TaskType instance FromJSON TaskType @@ -171,6 +179,10 @@ instance ToJSON Fact instance FromJSON Fact +instance ToJSON Comment + +instance FromJSON Comment + -- HTTP API Instances (for Servant query params) instance FromHttpApiData Status where @@ -240,6 +252,17 @@ instance SQL.FromField [Dependency] where instance SQL.ToField [Dependency] where toField deps = SQL.toField (BLC.unpack (encode deps)) +-- Store comments as JSON text +instance SQL.FromField [Comment] where + fromField f = do + t <- SQL.fromField f :: SQLOk.Ok String + case Aeson.decode (BLC.pack t) of + Just x -> pure x + Nothing -> pure [] -- Default to empty if parse fail or null + +instance SQL.ToField [Comment] where + toField comments = SQL.toField (BLC.unpack (encode comments)) + instance SQL.FromRow Task where fromRow = Task @@ -252,6 +275,7 @@ instance SQL.FromRow Task where <*> SQL.field <*> SQL.field <*> (fromMaybe "" SQL.field -- comments <*> SQL.field <*> SQL.field @@ -266,6 +290,7 @@ instance SQL.ToRow Task where SQL.toField (taskPriority t), SQL.toField (taskDependencies t), SQL.toField (taskDescription t), + SQL.toField (taskComments t), SQL.toField (taskCreatedAt t), SQL.toField (taskUpdatedAt t) ] @@ -402,6 +427,7 @@ initTaskDb = do \ priority TEXT NOT NULL, \ \ dependencies TEXT NOT NULL, \ \ description TEXT, \ + \ comments TEXT NOT NULL DEFAULT '[]', \ \ created_at TIMESTAMP NOT NULL, \ \ updated_at TIMESTAMP NOT NULL \ \)" @@ -488,6 +514,7 @@ tasksColumns = ("priority", "TEXT"), ("dependencies", "TEXT"), ("description", "TEXT"), + ("comments", "TEXT"), ("created_at", "TIMESTAMP"), ("updated_at", "TIMESTAMP") ] @@ -584,7 +611,7 @@ getSuffix parent childId = loadTasks :: IO [Task] loadTasks = withDb <| \conn -> do - SQL.query_ conn "SELECT id, title, type, parent, namespace, status, priority, dependencies, description, created_at, updated_at FROM tasks" + SQL.query_ conn "SELECT id, title, type, parent, namespace, status, priority, dependencies, description, comments, created_at, updated_at FROM tasks" -- Save a single task (UPSERT) saveTask :: Task -> IO () @@ -593,8 +620,8 @@ saveTask task = SQL.execute conn "INSERT OR REPLACE INTO tasks \ - \ (id, title, type, parent, namespace, status, priority, dependencies, description, created_at, updated_at) \ - \ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + \ (id, title, type, parent, namespace, status, priority, dependencies, description, comments, created_at, updated_at) \ + \ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" task -- Create a new task @@ -621,6 +648,7 @@ createTask title taskType parent namespace priority deps description = taskPriority = priority, taskDependencies = deps', taskDescription = description, + taskComments = [], taskCreatedAt = now, taskUpdatedAt = now } @@ -681,6 +709,20 @@ deleteTask tid = withDb <| \conn -> SQL.execute conn "DELETE FROM tasks WHERE id = ?" (SQL.Only tid) +-- Add a comment to a task +addComment :: Text -> Text -> IO Task +addComment tid commentText = + withTaskLock <| do + tasks <- loadTasks + case findTask tid tasks of + Nothing -> panic "Task not found" + Just task -> do + now <- getCurrentTime + let newComment = Comment {commentText = commentText, commentCreatedAt = now} + updatedTask = task {taskComments = taskComments task ++ [newComment], taskUpdatedAt = now} + saveTask updatedTask + pure updatedTask + -- List tasks listTasks :: Maybe TaskType -> Maybe Text -> Maybe Status -> Maybe Text -> IO [Task] listTasks maybeType maybeParent maybeStatus maybeNamespace = do @@ -963,6 +1005,11 @@ showTaskDetailed t = do let indented = T.unlines <| map (" " <>) (T.lines (taskDescription t)) putText indented + unless (null (taskComments t)) <| do + putText "" + putText "Comments:" + traverse_ printComment (taskComments t) + putText "" where priorityDesc = case taskPriority t of @@ -975,6 +1022,9 @@ showTaskDetailed t = do printDependency dep = putText <| " - " <> depId dep <> " [" <> T.pack (show (depType dep)) <> "]" + printComment c = + putText <| " [" <> T.pack (show (commentCreatedAt c)) <> "] " <> commentText c + red, green, yellow, blue, magenta, cyan, gray, bold :: Text -> Text red t = "\ESC[31m" <> t <> "\ESC[0m" green t = "\ESC[32m" <> t <> "\ESC[0m" -- cgit v1.2.3