summaryrefslogtreecommitdiff
path: root/Omni/Task/Core.hs
diff options
context:
space:
mode:
Diffstat (limited to 'Omni/Task/Core.hs')
-rw-r--r--Omni/Task/Core.hs173
1 files changed, 90 insertions, 83 deletions
diff --git a/Omni/Task/Core.hs b/Omni/Task/Core.hs
index 066ad95..1441a54 100644
--- a/Omni/Task/Core.hs
+++ b/Omni/Task/Core.hs
@@ -144,9 +144,10 @@ withTaskWriteLock action =
tasksFile <- getTasksFilePath
let lockFile = tasksFile <> ".lock"
bracket
- (do
- h <- IO.openFile lockFile IO.ReadWriteMode
- handleToFd h)
+ ( do
+ h <- IO.openFile lockFile IO.ReadWriteMode
+ handleToFd h
+ )
closeFd
( \fd -> do
waitToSetLock fd (WriteLock, AbsoluteSeek, 0, 0)
@@ -164,9 +165,10 @@ withTaskReadLock action =
tasksFile <- getTasksFilePath
let lockFile = tasksFile <> ".lock"
bracket
- (do
- h <- IO.openFile lockFile IO.ReadWriteMode
- handleToFd h)
+ ( do
+ h <- IO.openFile lockFile IO.ReadWriteMode
+ handleToFd h
+ )
closeFd
( \fd -> do
waitToSetLock fd (ReadLock, AbsoluteSeek, 0, 0)
@@ -191,9 +193,10 @@ generateId = do
-- Generate a child ID based on parent ID (e.g. "t-abc.1", "t-abc.1.2")
-- Finds the next available sequential suffix among existing children.
generateChildId :: Text -> IO Text
-generateChildId parentId = withTaskReadLock <| do
- tasks <- loadTasksInternal
- pure <| computeNextChildId tasks parentId
+generateChildId parentId =
+ withTaskReadLock <| do
+ tasks <- loadTasksInternal
+ pure <| computeNextChildId tasks parentId
computeNextChildId :: [Task] -> Text -> Text
computeNextChildId tasks parentId =
@@ -302,43 +305,45 @@ saveTaskInternal task = do
-- Create a new task
createTask :: Text -> TaskType -> Maybe Text -> Maybe Text -> Priority -> [Dependency] -> IO Task
-createTask title taskType parent namespace priority deps = withTaskWriteLock <| do
- tid <- case parent of
- Nothing -> generateId
- Just pid -> do
- tasks <- loadTasksInternal
- pure <| computeNextChildId tasks pid
- now <- getCurrentTime
- let task =
- Task
- { taskId = tid,
- taskTitle = title,
- taskType = taskType,
- taskParent = parent,
- taskNamespace = namespace,
- taskStatus = Open,
- taskPriority = priority,
- taskDependencies = deps,
- taskCreatedAt = now,
- taskUpdatedAt = now
- }
- saveTaskInternal task
- pure task
+createTask title taskType parent namespace priority deps =
+ withTaskWriteLock <| do
+ tid <- case parent of
+ Nothing -> generateId
+ Just pid -> do
+ tasks <- loadTasksInternal
+ pure <| computeNextChildId tasks pid
+ now <- getCurrentTime
+ let task =
+ Task
+ { taskId = tid,
+ taskTitle = title,
+ taskType = taskType,
+ taskParent = parent,
+ taskNamespace = namespace,
+ taskStatus = Open,
+ taskPriority = priority,
+ taskDependencies = deps,
+ taskCreatedAt = now,
+ taskUpdatedAt = now
+ }
+ saveTaskInternal task
+ pure task
-- Update task status
updateTaskStatus :: Text -> Status -> IO ()
-updateTaskStatus tid newStatus = withTaskWriteLock <| do
- tasks <- loadTasksInternal
- now <- getCurrentTime
- let updatedTasks = map updateIfMatch tasks
- updateIfMatch t =
- if matchesId (taskId t) tid
- then t {taskStatus = newStatus, taskUpdatedAt = now}
- else t
- -- Rewrite the entire file (simple approach for MVP)
- tasksFile <- getTasksFilePath
- TIO.writeFile tasksFile ""
- traverse_ saveTaskInternal updatedTasks
+updateTaskStatus tid newStatus =
+ withTaskWriteLock <| do
+ tasks <- loadTasksInternal
+ now <- getCurrentTime
+ let updatedTasks = map updateIfMatch tasks
+ updateIfMatch t =
+ if matchesId (taskId t) tid
+ then t {taskStatus = newStatus, taskUpdatedAt = now}
+ else t
+ -- Rewrite the entire file (simple approach for MVP)
+ tasksFile <- getTasksFilePath
+ TIO.writeFile tasksFile ""
+ traverse_ saveTaskInternal updatedTasks
-- List tasks, optionally filtered by type, parent, status, or namespace
listTasks :: Maybe TaskType -> Maybe Text -> Maybe Status -> Maybe Text -> IO [Task]
@@ -501,7 +506,7 @@ showTaskTree maybeId = do
InProgress -> "[~]"
Review -> "[?]"
Done -> "[✓]"
-
+
coloredStatusStr = case taskType task of
Epic -> magenta statusStr
WorkTask -> case taskStatus task of
@@ -513,7 +518,7 @@ showTaskTree maybeId = do
nsStr = case taskNamespace task of
Nothing -> ""
Just ns -> "[" <> ns <> "] "
-
+
coloredNsStr = case taskNamespace task of
Nothing -> ""
Just _ -> gray nsStr
@@ -525,7 +530,7 @@ showTaskTree maybeId = do
if T.length (taskTitle task) > availableWidth
then T.take (availableWidth - 3) (taskTitle task) <> "..."
else taskTitle task
-
+
coloredTitle = if taskType task == Epic then bold truncatedTitle else truncatedTitle
putText <| prefix <> cyan (taskId task) <> " " <> coloredStatusStr <> " " <> coloredNsStr <> coloredTitle
@@ -552,16 +557,16 @@ printTask t = do
completed = length <| filter (\child -> taskStatus child == Done) children
in " [" <> T.pack (show completed) <> "/" <> T.pack (show total) <> "]"
else ""
-
+
parentInfo = case taskParent t of
Nothing -> ""
Just p -> " (parent: " <> p <> ")"
-
+
namespaceInfo = case taskNamespace t of
Nothing -> ""
Just ns -> " [" <> ns <> "]"
- coloredStatus =
+ coloredStatus =
let s = "[" <> T.pack (show (taskStatus t)) <> "]"
in case taskStatus t of
Open -> bold s
@@ -570,13 +575,13 @@ printTask t = do
Done -> green s
coloredTitle = if taskType t == Epic then bold (taskTitle t) else taskTitle t
-
+
coloredProgress = if taskType t == Epic then magenta progressInfo else progressInfo
-
+
coloredNamespace = case taskNamespace t of
Nothing -> ""
Just _ -> gray namespaceInfo
-
+
coloredParent = case taskParent t of
Nothing -> ""
Just _ -> gray parentInfo
@@ -653,12 +658,13 @@ bold t = "\ESC[1m" <> t <> "\ESC[0m"
-- Export tasks: Consolidate JSONL file (remove duplicates, keep latest version)
exportTasks :: IO ()
-exportTasks = withTaskWriteLock <| do
- tasks <- loadTasksInternal
- -- Rewrite the entire file with deduplicated tasks
- tasksFile <- getTasksFilePath
- TIO.writeFile tasksFile ""
- traverse_ saveTaskInternal tasks
+exportTasks =
+ withTaskWriteLock <| do
+ tasks <- loadTasksInternal
+ -- Rewrite the entire file with deduplicated tasks
+ tasksFile <- getTasksFilePath
+ TIO.writeFile tasksFile ""
+ traverse_ saveTaskInternal tasks
-- Task statistics
data TaskStats = TaskStats
@@ -778,31 +784,32 @@ showTaskStats maybeEpicId = do
-- Import tasks: Read from another JSONL file and merge with existing tasks
importTasks :: FilePath -> IO ()
-importTasks filePath = withTaskWriteLock <| do
- exists <- doesFileExist filePath
- unless exists <| panic (T.pack filePath <> " does not exist")
-
- -- Load tasks from import file
- content <- TIO.readFile filePath
- let importLines = T.lines content
- importedTasks = mapMaybe decodeTask importLines
-
- -- Load existing tasks
- existingTasks <- loadTasksInternal
-
- -- Create a map of existing task IDs for quick lookup
- let existingIds = map taskId existingTasks
- -- Filter to only new tasks (not already in our database)
- newTasks = filter (\t -> not (any (`matchesId` taskId t) existingIds)) importedTasks
- -- For tasks that exist, update them with imported data
- updatedTasks = map (updateWithImported importedTasks) existingTasks
- -- Combine: updated existing tasks + new tasks
- allTasks = updatedTasks ++ newTasks
-
- -- Rewrite tasks.jsonl with merged data
- tasksFile <- getTasksFilePath
- TIO.writeFile tasksFile ""
- traverse_ saveTaskInternal allTasks
+importTasks filePath =
+ withTaskWriteLock <| do
+ exists <- doesFileExist filePath
+ unless exists <| panic (T.pack filePath <> " does not exist")
+
+ -- Load tasks from import file
+ content <- TIO.readFile filePath
+ let importLines = T.lines content
+ importedTasks = mapMaybe decodeTask importLines
+
+ -- Load existing tasks
+ existingTasks <- loadTasksInternal
+
+ -- Create a map of existing task IDs for quick lookup
+ let existingIds = map taskId existingTasks
+ -- Filter to only new tasks (not already in our database)
+ newTasks = filter (\t -> not (any (`matchesId` taskId t) existingIds)) importedTasks
+ -- For tasks that exist, update them with imported data
+ updatedTasks = map (updateWithImported importedTasks) existingTasks
+ -- Combine: updated existing tasks + new tasks
+ allTasks = updatedTasks ++ newTasks
+
+ -- Rewrite tasks.jsonl with merged data
+ tasksFile <- getTasksFilePath
+ TIO.writeFile tasksFile ""
+ traverse_ saveTaskInternal allTasks
where
decodeTask :: Text -> Maybe Task
decodeTask line =