packages feed

A-gent 0.11.0.6 → 0.11.0.7

raw patch · 7 files changed

+676/−192 lines, 7 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Agent.IO.Restricted: readFileStrict :: FilePath -> RIO String
- Agent.LLM: [next] :: History -> [String]
- Agent.LLM: [prev] :: History -> [String]
+ Agent.IO.Restricted: instance GHC.Classes.Eq Agent.IO.Restricted.Position
+ Agent.IO.Restricted: instance GHC.Classes.Ord Agent.IO.Restricted.Position
+ Agent.IO.Restricted: instance GHC.Read.Read Agent.IO.Restricted.Position
+ Agent.IO.Restricted: instance GHC.Show.Show Agent.IO.Restricted.MultiLine
+ Agent.IO.Restricted: instance GHC.Show.Show Agent.IO.Restricted.Position
+ Agent.LLM: [chat] :: History -> Chat
+ Agent.LLM: [chit] :: History -> Chit
+ Agent.Utils.Common: chats :: Bool -> [String] -> [String]
+ Agent.Utils.Common: chits :: Bool -> [String] -> [String]
+ Agent.Utils.Common: split :: Char -> String -> [String]
- Agent.LLM: History :: Bool -> [String] -> [String] -> History
+ Agent.LLM: History :: View -> Chit -> Chat -> History
- Agent.LLM: [view] :: History -> Bool
+ Agent.LLM: [view] :: History -> View
- Agent.LLM: type Load a = Data a => Maybe a
+ Agent.LLM: type Load a = (Data a, Show a) => Maybe a
- Agent.Utils.Code: file :: LlmCodeRead rio => Maybe String -> Int -> Bool -> rio (Either String String)
+ Agent.Utils.Code: file :: LlmCodeRead rio => Maybe String -> Int -> Bool -> rio (Either String [String])
- Agent.Utils.Common: file :: Bool -> String -> String
+ Agent.Utils.Common: file :: Bool -> String -> [String]
- Agent.Utils.Plan: file :: LlmPlanRead rio => Maybe String -> Int -> Bool -> rio (Either String String)
+ Agent.Utils.Plan: file :: LlmPlanRead rio => Maybe String -> Int -> Bool -> rio (Either String [String])

Files

A-gent.cabal view
@@ -9,7 +9,7 @@ build-type: Simple                                                name: A-gent-version: 0.11.0.6+version: 0.11.0.7  synopsis: Polite & well educated LLM agent with excellent manners that always behaves well description: Polite and well educated LLM agent with excellent manners that always behaves well
CHANGELOG.md view
@@ -1,5 +1,26 @@ # Revision history for Λ-gent +## 0.11.0.7  -- 2026-03-30++* We seem to have fixed multi-line issues. I guess we can say that we are+  probably as good as `GHCi`.+  +  > **NOTE:** A thorough refactoring needs to be done to enforce `DRY`.++* Added support for:++  * `CTRL + L` clear screen.+    +  * `CTRL + U` clear text from current position to start of input.+    +  * `CTRL + K` clear text from current position to end of input.+    +  * `CTRL + A` equivalent to `HOME`.+    +  * `CTRL + E` equivalent to `END`.+    +  * `CTRL + D` equivalent to `DELETE`.+ ## 0.11.0.6  -- 2026-03-20  * Added a `todo.org` file in the root folder of the project. This will allow to
src/Agent/IO/Restricted.hs view
@@ -67,8 +67,6 @@   , llmPlanSeq, llmPlanGet   , llmPlanKey, llmPlanAPI   , llmPlanWeb-    -- * Temporary-  , readFileStrict   ) where @@ -76,7 +74,7 @@  import           Data.Either              ( partitionEithers ) import           Data.List                ( isInfixOf )-import           Data.Maybe               ( fromMaybe )+import           Data.Maybe               ( fromMaybe, maybeToList ) import qualified System.Environment.Blank as ENV import           System.Exit   ( ExitCode (ExitFailure, ExitSuccess)@@ -88,9 +86,12 @@   , readCreateProcessWithExitCode   , readProcessWithExitCode   )+import           Text.Read                ( readMaybe )  import qualified Agent.IO.Effects         as EFF+import qualified Agent.Utils.Common       as COM + --------------------------------------------------------------------------------  newtype RIO a = RestrictedIO { run :: IO a }@@ -108,6 +109,48 @@ -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- +data Position =+  Position Int Int+  deriving (Eq, Show)++instance Ord Position where+  (<=) (Position y1 x1) (Position y2 x2) =+    case compare y1 y2 of+      GT -> False+      EQ -> x1 <= x2+      LT -> True++instance Read Position where+  readsPrec _ str =+    -- NOTE:+    -- "\ESC[#;#R" => "\ESC" and "[#;#R" (only parse last)+    maybeToList+    ( readMaybe (f str) >>= \ y ->+      readMaybe (g str) >>= \ x ->+        Just+          ( Position y x+          , h str+          )+    )+    where+      f = takeWhile (/= ';') . drop 1 . dropWhile (/= '[')+      g = takeWhile (/= 'R') . drop 1 . dropWhile (/= ';')+      h = drop 1 . dropWhile (/= 'R') . dropWhile (/= ';') . dropWhile (/= '[')++--------------------------------------------------------------------------------++data MultiLine =+  MultiLine+    { stx :: Maybe Position+    , cur :: Maybe Position+    , etx :: Maybe Position+    , col :: Int+    }+  deriving Show++--------------------------------------------------------------------------------+--------------------------------------------------------------------------------+ class StdIn m where   input     :: [ String ]@@ -124,165 +167,502 @@ instance StdIn RIO where   -- BUG: Issue with multiline text   input prev next =-    -- TODO: CTRL+U (erase from cursor to start of line) and CTRL+K (erase from-    --       cursor to end of line-    RestrictedIO $ aux prev next [] []+    -- NOTE: "\ESC7" - save cursor position (SCO)+    -- - https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797#cursor-controls+    RestrictedIO $ putStr "\ESC7" >> aux gen prev next [] []     where-      aux hps hns bcs acs =+      msv = 32767 :: Int+      gen =+        MultiLine+          { stx = Nothing+          , cur = Nothing+          , etx = Nothing+          , col = 0+          }+      aux mlp hps hns bcs acs =         key >>= \ k ->-        case (k < [ '\32' ], k) of-          -- NOTE: To view keystrokes, use: `ghc -e getLine` (hit + ENTER)-          (True , '\008':[]) ->+        case (k < ['\32'], k) of+          (True , ['\^H']) ->             case bcs of-              -- NOTE: Backspace (remove char if any and re-write after chars)-              [    ] -> aux hps hns bcs acs+              -- NOTE: CTRL + H or Backspace (remove char if any and re-write+              -- after chars)+              [    ] -> aux mlp hps hns bcs acs               (_:cs) ->-                putChar '\008' >> hFlush stdout >>-                putChar '\032' >> hFlush stdout >>-                putStr  es     >> hFlush stdout >>-                putStr  rm     >> hFlush stdout >>-                putChar '\008' >> hFlush stdout >>-                putStr  acs    >> hFlush stdout >>-                putStr  rm     >> hFlush stdout >>-                aux hps hns cs  acs-                where-                  la = [ '\ESC', '[', 'D' ]-                  lc = length acs-                  es = concatMap id $ take lc $ repeat sp-                  rm = concatMap id $ take lc $ repeat la-                  sp = [ ' ' ]-          (True , '\010':[]) ->-            -- NOTE: ENTER (only if something is typed)+                hFlush stdout >>+                -- NOTE: 0) Print "\ESC[6n" position before action+                putStr "\ESC[6n" >>+                -- NOTE: Remove char+                putChar '\^H'    >>+                -- NOTE: Save position+                putStr "\ESC7"   >>+                -- NOTE: Clear rest of screen from position+                putStr "\ESC[0J" >>+                -- NOTE: Print after chars+                putStr  acs      >>+                -- NOTE: 1) Print "\ESC[6n" end position after action+                putStr "\ESC[6n" >>+                -- NOTE: 2) Print "\ESC[6n" column witdh+                putStr ("\ESC[" ++ show msv ++ "C") >>+                putStr "\ESC[6n"                    >>+                -- NOTE: Go back to saved position+                putStr "\ESC8"   >>+                -- NOTE: 3) Print "\ESC[6n" current position after action+                putStr "\ESC[6n" >> hFlush stdout >>+                aux mlp hps hns cs acs+          (True , ['\^J']) ->+            -- NOTE: ENTER (line feed) only if something is typed             case (bcs, acs) of               ([], []) ->-                aux hps hns bcs acs+                aux mlp hps hns bcs acs               ________ ->                 pure $ (++ acs) $ reverse $ bcs-          (True , '\012':[]) ->-            -- NOTE: Form Feed becomes "/w" (clear screen)-            pure $ "/w"-          (True , '\ESC':'[':'A':[]) ->+          (True , ['\^L']) ->+            -- NOTE: CTRL + L (form feed) becomes "/w" (clear screen)+            pure $ "/wipe"+          (True , ['\^U']) ->+            -- NOTE: CTRL + U cut text to start+            hFlush stdout >>+            -- NOTE: 0) Print "\ESC[6n" position before action+            putStr "\ESC[6n" >>+            -- NOTE: Go back to start of line+            putStr hom >> hFlush stdout >>+            -- NOTE: Save position+            putStr "\ESC7"   >>+            -- NOTE: Clear rest of screen from position+            putStr "\ESC[0J" >>+            -- NOTE: Print after chars+            putStr  acs      >>+            -- NOTE: 1) Print "\ESC[6n" end position after action+            putStr "\ESC[6n" >>+            -- NOTE: 2) Print "\ESC[6n" column witdh+            putStr ("\ESC[" ++ show msv ++ "C") >>+            putStr "\ESC[6n"                    >>+            -- NOTE: Go back to saved position+            putStr "\ESC8"   >>+            -- NOTE: 3) Print "\ESC[6n" current position after action+            putStr "\ESC[6n" >> hFlush stdout >>+            aux mlp hps hns [] acs+            where+              hom =+                case mlp of+                  MultiLine { stx = Just (Position y x) } ->+                    "\ESC[" ++ show y ++ ";" ++ show x ++ "H"+                  _______________________________________ ->+                    "\ESC8"+          (True , ['\^K']) ->+            -- NOTE: CTRL + K cut text to end+            hFlush stdout >>+            -- NOTE: 0) Print "\ESC[6n" position before action+            putStr "\ESC[6n" >>+            -- NOTE: Save position+            putStr "\ESC7"   >>+            -- NOTE: Clear rest of screen from position+            putStr "\ESC[0J" >>+            -- NOTE: 1) Print "\ESC[6n" end position after action+            putStr "\ESC[6n" >>+            -- NOTE: 2) Print "\ESC[6n" column witdh+            putStr ("\ESC[" ++ show msv ++ "C") >>+            putStr "\ESC[6n"                    >>+            -- NOTE: Go back to saved position+            putStr "\ESC8"   >>+            -- NOTE: 3) Print "\ESC[6n" current position after action+            putStr "\ESC[6n" >> hFlush stdout >>+            aux mlp hps hns bcs []+          (True , ['\ESC','[','H']) ->+            -- NOTE: HOME+            --+            -- NOTE: Move to start of text+            putStr hom       >>+            -- NOTE: Update current position+            putStr "\ESC[6n" >> hFlush stdout >>+            aux mln hps hns [] (reverse bcs ++ acs)+            where+              (mln, hom) =+                case mlp of+                  MultiLine { stx = Just (Position y x) } ->+                    ( mlp { cur = Just (Position y x) }+                    , "\ESC[" ++ show y ++ ";" ++ show x ++ "H"+                    )+                  _______________________________________ ->+                    ( mlp+                    , "\ESC8"+                    )+          (True , ['\ESC','[','F']) ->+            -- NOTE: END+            --+            -- NOTE: Move to end of text+            hFlush stdout >>+            putStr hom       >>+            -- NOTE: Update current position+            putStr "\ESC[6n" >> hFlush stdout >>+            aux mln hps hns ((++ bcs) $ reverse $ acs) []+            where+              (mln, hom) =+                case mlp of+                  MultiLine { etx = Just (Position y x) } ->+                    ( mlp { cur = Just (Position y x) }+                    , "\ESC[" ++ show y ++ ";" ++ show x ++ "H"+                    )+                  _______________________________________ ->+                    ( mlp+                    , "\ESC8"+                    )+          (True , ['\^A']) ->+            -- NOTE: CTRL + A to move to start of text+            --+            -- TODO: DRY as equal to HOME+            -- NOTE: Move to start of text+            hFlush stdout >>+            putStr hom       >>+            -- NOTE: Update current position+            putStr "\ESC[6n" >> hFlush stdout >>+            aux mln hps hns [] ((reverse bcs) ++ acs)+            where+              (mln, hom) =+                case mlp of+                  MultiLine { stx = Just (Position y x) } ->+                    ( mlp { cur = Just (Position y x) }+                    , "\ESC[" ++ show y ++ ";" ++ show x ++ "H"+                    )+                  _______________________________________ ->+                    ( mlp+                    , "\ESC8"+                    )+          (True , ['\^E']) ->+            -- NOTE: CTRL + E to move to end of text+            --+            -- TODO: DRY as equal to END+            -- NOTE: Move to end of text+            hFlush stdout >>+            putStr hom    >>+            -- NOTE: Update current position+            putStr "\ESC[6n" >> hFlush stdout >>+            aux mln hps hns ((++ bcs) $ reverse acs) []+            where+              (mln, hom) =+                case mlp of+                  MultiLine { etx = Just (Position y x) } ->+                    ( mlp { cur = Just (Position y x) }+                    , "\ESC[" ++ show y ++ ";" ++ show x ++ "H"+                    )+                  _______________________________________ ->+                    ( mlp+                    , "\ESC8"+                    )+          (True , ['\ESC','[','A']) ->             -- NOTE: Allowed escaped sequences: Arrow "↑"-            case (txt, hps, hns) of-              (_, [  ], _) -> aux hps hns bcs acs-              (_, p:ps, _) ->-                putStr rm >> hFlush stdout >>-                putStr es >> hFlush stdout >>-                putStr rm >> hFlush stdout >>-                putStr p  >> hFlush stdout >>-                aux ps (p:hns) rev []+            case (hps, hns) of+              ([  ], _) -> aux mlp hps hns bcs acs+              (p:ps, _) ->+                hFlush stdout >>+                -- NOTE: 0) Print "\ESC[6n" position before action+                putStr "\ESC[6n" >>+                -- NOTE: Move to start of text+                putStr hom       >>+                -- NOTE: Print previous line+                putStr p >>+                -- NOTE: Save position+                putStr "\ESC7"   >>+                -- NOTE: Clear rest of screen from position+                putStr "\ESC[0J" >>+                -- NOTE: Print after chars+                putStr  []       >>+                -- NOTE: 1) Print "\ESC[6n" end position after action+                putStr "\ESC[6n" >>+                -- NOTE: 2) Print "\ESC[6n" column witdh+                putStr ("\ESC[" ++ show msv ++ "C") >>+                putStr "\ESC[6n"                    >>+                -- NOTE: Go back to saved position+                putStr "\ESC8"   >>+                -- NOTE: 3) Print "\ESC[6n" current position after action+                putStr "\ESC[6n" >> hFlush stdout >>+                aux mlp ps (p:hns) rev []                 where                   rev = reverse p-            where-              sp = [ ' ' ]-              la = [ '\ESC', '[', 'D' ]-              lc = length txt-              es = concatMap id $ take lc $ repeat sp-              rm = concatMap id $ take lc $ repeat la-              txt = (++ acs) $ reverse $ bcs-          (True , '\ESC':'[':'B':[]) ->+                  hom =+                    case mlp of+                      MultiLine { stx = Just (Position y x) } ->+                        "\ESC[" ++ show y ++ ";" ++ show x ++ "H"+                      _______________________________________ ->+                        "\ESC8"+          (True , ['\ESC','[','B']) ->             -- NOTE: Allowed escaped sequences: Arrow "↓"-            case (txt, hps, hns) of-              (_, _, [  ]) -> aux hps hns bcs acs-              (_, _, p:ps) ->-                putStr rm >> hFlush stdout >>-                putStr es >> hFlush stdout >>-                putStr rm >> hFlush stdout >>-                putStr p  >> hFlush stdout >>-                aux (p:hps) ps rev []+            case (hps, hns) of+              (_, [  ]) -> aux mlp hps hns bcs acs+              (_, p:ps) ->+                hFlush stdout >>+                -- NOTE: 0) Print "\ESC[6n" position before action+                putStr "\ESC[6n" >>+                -- NOTE: Move to start of text+                putStr hom       >>+                -- NOTE: Print previous line+                putStr p >>+                -- NOTE: Save position+                putStr "\ESC7"   >>+                -- NOTE: Clear rest of screen from position+                putStr "\ESC[0J" >>+                -- NOTE: Print after chars+                putStr  []       >>+                -- NOTE: 1) Print "\ESC[6n" end position after action+                putStr "\ESC[6n" >>+                -- NOTE: 2) Print "\ESC[6n" column witdh+                putStr ("\ESC[" ++ show msv ++ "C") >>+                putStr "\ESC[6n"                    >>+                -- NOTE: Go back to saved position+                putStr "\ESC8"   >>+                -- NOTE: 3) Print "\ESC[6n" current position after action+                putStr "\ESC[6n" >> hFlush stdout >>+                aux mlp (p:hps) ps rev []                 where                   rev = reverse p-            where-              sp = [ ' ' ]-              la = [ '\ESC', '[', 'D' ]-              lc = length txt-              es = concatMap id $ take lc $ repeat sp-              rm = concatMap id $ take lc $ repeat la-              txt = (++ acs) $ reverse $ bcs-          (True , '\ESC':'[':'C':[]) ->+                  hom =+                    case mlp of+                      MultiLine { stx = Just (Position y x) } ->+                        "\ESC[" ++ show y ++ ";" ++ show x ++ "H"+                      _______________________________________ ->+                        "\ESC8"+          (True , ['\ESC','[','C']) ->             -- NOTE: Allowed escaped sequences: Arrow "→"             case (bcs, acs) of               (_, [  ]) ->-                aux hps hns    bcs  acs+                aux mlp hps hns    bcs  acs               (_, c:cs) ->-                putStr k >> hFlush stdout >>-                aux hps hns (c:bcs)  cs-          (True , '\ESC':'[':'D':[    ]) ->+                hFlush stdout >>+                ( if pl then+                    putStr ("\ESC[" ++ "1" ++ "E")+                  else+                    putStr k+                ) >>+                -- NOTE: Update current position+                putStr "\ESC[6n" >> hFlush stdout >>+                aux mlp hps hns (c:bcs) cs+                where+                  pl =+                    case mlp of+                      MultiLine+                        { cur = Just (Position cy cx)+                        , etx = Just (Position ey __)+                        , col = cw+                        } -> cy <= ey && cx == cw+                      ___ -> False+          (True , ['\ESC','[','D']) ->             -- NOTE: Allowed escaped sequences: Arrow "←"             case (bcs, acs) of               ([  ], _) ->-                aux hps hns bcs    acs+                aux mlp hps hns bcs    acs               (c:cs, _) ->-                putStr k >> hFlush stdout >>-                aux hps hns  cs (c:acs)-          (True , '\ESC':'[':'1':';':'5':'C':[]) ->+                hFlush stdout >>+                ( if pl then+                    putStr ("\ESC[" ++      "1" ++ "F") >>+                    putStr ("\ESC[" ++ show msv ++ "C")+                  else+                    putStr k+                ) >>+                -- NOTE: Update current position+                putStr "\ESC[6n" >> hFlush stdout >>+                aux mlp hps hns  cs (c:acs)+                where+                  pl =+                    case mlp of+                      MultiLine+                        { stx = Just (Position sy __)+                        , cur = Just (Position cy 01)+                        } -> sy < cy+                      ___ -> False++          (True , ['\ESC','[','1',';','5','C']) ->             -- NOTE: Allowed escaped sequences: CTRL + Arrow "→"-            putStr rm >> hFlush stdout >>-            (aux hps hns (ts ++ bcs) ds)+            -- NOTE: Move to end of text+            hFlush stdout    >>+            putStr np        >>+            -- NOTE: Update current position+            putStr "\ESC[6n" >> hFlush stdout >>+            aux mlp hps hns (ts ++ bcs) ds             where               ds = dropWhile (== ' ') $ dropWhile (/= ' ') acs               ts = drop (length ds) $ reverse acs-              la = [ '\ESC', '[', 'C' ]               lc = length ts-              rm = concatMap id $ take lc $ repeat la-          (True , '\ESC':'[':'1':';':'5':'D':[]) ->+              np =+                case mlp of+                  MultiLine+                    { cur = Just (Position cy cx)+                    , etx = Just (Position ey _ )+                    , col = cw+                    } ->+                    case (cy < ey, compare (cw - cx) lc) of+                      (True, LT) ->+                        "\ESC[" ++ show y ++ ";" ++ show x ++ "H"+                        where+                          y = cy + 1+                          x = lc - (cw - cx)+                      __________ ->+                        "\ESC[" ++ show y ++ ";" ++ show x ++ "H"+                        where+                          y = cy+                          x = cx + lc+                  ___ -> []++          (True , ['\ESC','[','1',';','5','D']) ->             -- NOTE: Allowed escaped sequences: CTRL + Arrow "←"-            putStr rm >> hFlush stdout >>-            (aux hps hns ds (ts ++ acs))+            hFlush stdout    >>+            putStr np        >>+            -- NOTE: Update current position+            putStr "\ESC[6n" >> hFlush stdout >>+            aux mlp hps hns ds (ts ++ acs)             where               ds = dropWhile (== ' ') $ dropWhile (/= ' ') bcs               ts = drop (length ds) $ reverse bcs-              la = [ '\ESC', '[', 'D' ]               lc = length ts-              rm = concatMap id $ take lc $ repeat la-          (True , '\ESC':'[':'H':[]) ->-            -- NOTE: HOME-            -- HERE:-            putStr rm >> hFlush stdout >>-            (aux hps hns [] ((reverse bcs) ++ acs))-            where-              la = [ '\ESC', '[', 'D' ]-              lc = length bcs-              rm = concatMap id $ take lc $ repeat la-          (True , '\ESC':'[':'F':[]) ->-            -- NOTE: END-            putStr rm >> hFlush stdout >>-            (aux hps hns ((++ bcs) $ reverse $ acs) [])-            where-              la = [ '\ESC', '[', 'C' ]-              lc = length acs-              rm = concatMap id $ take lc $ repeat la-          (True , '\ESC':'[':'3':'~':[]) ->+              np =+                case mlp of+                  MultiLine+                    { stx = Just (Position sy _ )+                    , cur = Just (Position cy cx)+                    , col = cw+                    } ->+                    case (sy < cy, compare cx lc) of+                      (True, LT) ->+                        "\ESC[" ++ show y ++ ";" ++ show x ++ "H"+                        where+                          y = cy - 1+                          x = cw - (lc - cx)+                      __________ ->+                        "\ESC[" ++ show y ++ ";" ++ show x ++ "H"+                        where+                          y = cy+                          x = cx - lc+                  ___ -> []+          (True , ['\ESC','[','3','~']) ->+            -- NOTE: CTRL + D behave as delete key+            -- TODO: Use DRY             case acs of               -- NOTE: Delete (remove char if any and re-write after chars)-              [    ] -> aux hps hns bcs acs+              [    ] -> aux mlp hps hns bcs acs               (_:cs) ->-                putStr  cs  >> hFlush stdout >>-                putChar ' ' >> hFlush stdout >>-                putStr  rm  >> hFlush stdout >>-                (aux hps hns bcs cs)-                where-                  la = [ '\ESC', '[', 'D' ]-                  lc = length acs-                  rm = concatMap id $ take lc $ repeat la-          (True , '\ESC':__) ->-            -- NOTE: Other escaped sequences (skip)-            aux hps hns bcs acs-          (True ,  _) ->-            -- NOTE: Skip all other unhandled control keys-            aux hps hns bcs acs-          (False, '\DEL':[]) ->+                hFlush stdout >>+                -- NOTE: 0) Print "\ESC[6n" position before action+                putStr "\ESC[6n" >>+                -- NOTE: Save position+                putStr "\ESC7"   >>+                -- NOTE: Clear rest of screen from position+                putStr "\ESC[0J" >>+                -- NOTE: Print after chars+                putStr  cs       >>+                -- NOTE: 1) Print "\ESC[6n" end position after action+                putStr "\ESC[6n" >>+                -- NOTE: 2) Print "\ESC[6n" column witdh+                putStr ("\ESC[" ++ show msv ++ "C") >>+                putStr "\ESC[6n"                    >>+                -- NOTE: Go back to saved position+                putStr "\ESC8"   >>+                -- NOTE: 3) Print "\ESC[6n" current position after action+                putStr "\ESC[6n" >> hFlush stdout >>+                aux mlp hps hns bcs cs+          (True , ['\^D']) ->+            -- NOTE: CTRL + D behave as delete key+            -- TODO: Use DRY+            case acs of+              -- NOTE: Delete (remove char if any and re-write after chars)+              [    ] -> aux mlp hps hns bcs acs+              (_:cs) ->+                hFlush stdout >>+                -- NOTE: 0) Print "\ESC[6n" position before action+                putStr "\ESC[6n" >>+                -- NOTE: Save position+                putStr "\ESC7"   >>+                -- NOTE: Clear rest of screen from position+                putStr "\ESC[0J" >>+                -- NOTE: Print after chars+                putStr  cs       >>+                -- NOTE: 1) Print "\ESC[6n" end position after action+                putStr "\ESC[6n" >>+                -- NOTE: 2) Print "\ESC[6n" column witdh+                putStr ("\ESC[" ++ show msv ++ "C") >>+                putStr "\ESC[6n"                    >>+                -- NOTE: Go back to saved position+                putStr "\ESC8"   >>+                -- NOTE: 3) Print "\ESC[6n" current position after action+                putStr "\ESC[6n" >> hFlush stdout >>+                aux mlp hps hns bcs cs+          (True , '\ESC':ps) ->+            -- NOTE: Other escaped sequences (skip unless positions)+            aux mln hps hns bcs acs+            where+              ops = map readMaybe $ COM.split '\ESC' ps :: [Maybe Position]+              mln =+                -- NOTE: 0) Print "\ESC[6n" position before action (unused)+                -- NOTE: 1) Print "\ESC[6n" end position after action+                -- NOTE: 2) Print "\ESC[6n" column witdh+                -- NOTE: 3) Print "\ESC[6n" current position after action+                case (mlp, ops) of+                  (___________________________,+                   [                                           ]) ->+                    mlp+                  (MultiLine { stx = Nothing },+                   [Just (Position y x)                        ]) ->+                    mlp+                      { stx = Just sp+                      , cur = Just sp+                      , etx = Just sp+                      , col = x+                      }+                    where+                      sp = Position y x+                  (MultiLine { etx = Just ep },+                   [Just cp                                    ]) ->+                    if cp > ep then+                      mlp+                        { cur = Just cp+                        , etx = Just cp+                        }+                    else+                      mlp+                        { cur = Just cp+                        }+                  (MultiLine { stx = Nothing },+                   [Just sp,Just ep,Just (Position _ w),Just cp]) ->+                    mlp+                      { stx = Just sp+                      , cur = Just cp+                      , etx = Just ep+                      , col = w+                      }+                  (___________________________,+                   [Just __,Just ep,Just (Position _ w),Just cp]) ->+                    mlp+                      { cur = Just cp+                      , etx = Just ep+                      , col = w+                      }+                  _______________________________________________ ->+                    mlp+          (True , _) ->+            aux mlp hps hns bcs acs+          (False, ['\DEL']) ->             -- NOTE: DELETE (skip)-            aux hps hns bcs acs+            aux mlp hps hns bcs acs           (False, _) ->-            putStr k   >> hFlush stdout >>-            putStr acs >> hFlush stdout >>-            putStr rm  >> hFlush stdout >>-            (aux hps hns (reverse k ++ bcs) acs)-            where-              lc = length acs-              rm = concatMap id $ take lc $ repeat la-              la = ['\ESC','[','D']+            hFlush stdout >>+            -- NOTE: 0) Print "\ESC[6n" position before action+            putStr "\ESC[6n" >>+            -- NOTE: Type key or text+            putStr k         >>+            -- NOTE: Save position+            putStr "\ESC7"   >>+            -- NOTE: Clear rest of screen from position+            putStr "\ESC[0J" >>+            -- NOTE: Print after chars+            putStr  acs      >>+            -- NOTE: 1) Print "\ESC[6n" end position after action+            putStr "\ESC[6n" >>+            -- NOTE: 2) Print "\ESC[6n" column witdh+            putStr ("\ESC[" ++ show msv ++ "C") >>+            putStr "\ESC[6n"                    >>+            -- NOTE: Go back to saved position+            putStr "\ESC8"   >>+            -- NOTE: 3) Print "\ESC[6n" current position after action+            putStr "\ESC[6n" >> hFlush stdout >>+            aux mlp hps hns (reverse k ++ bcs) acs       key =         -- NOTE: https://stackoverflow.com/a/38553473         reverse <$> nxt []@@ -539,7 +919,7 @@       realPath (fromMaybe [] ocwd ++ "/" ++ pdir) >>= \ efrp ->       case efrp of         Right rp ->-          if isInfixOf rp path then+          if rp `isInfixOf` path then             Right <$> readFileStrict path           else             pure $ Left $ "File: " ++ path ++ " is not in: " ++ rp@@ -626,8 +1006,8 @@   ( \case       Right str ->         Right-        $ map (filter (/= '\NUL'))-        $ map (drop (length path)) -- NOTE: Relative paths instead of full+        -- NOTE: Relative paths instead of full+        $ map (filter (/= '\NUL') . drop (length path))         $ lines str       Left  err ->         Left err@@ -696,8 +1076,8 @@   --   -- - http://woshub.com/too-many-open-files-error-linux/   RestrictedIO $-  readFile path >>= \bs ->-  length bs `seq` pure bs+  readFile path >>= \cs ->+  length cs `seq` pure cs  realPath   :: String
src/Agent/LLM.hs view
@@ -47,7 +47,7 @@ import           Data.Char                            ( toLower, toUpper ) import           GHC.IO.Encoding                      ( setLocaleEncoding ) import           System.IO-  ( BufferMode (NoBuffering)+  ( BufferMode (LineBuffering, NoBuffering)   , hFlush   , hSetBuffering   , hSetEcho@@ -65,6 +65,7 @@   , input   , output   )+import qualified Agent.Utils.Common                   as COM  -------------------------------------------------------------------------------- @@ -79,14 +80,31 @@   deriving (Bounded, Enum, Eq, Read, Show)  type Load a =-  Data a+  ( Data a+  , Show a+  )   => Maybe a +data View+  = None+  | Both+  | Chit'+  | Chat'++data Chit =+  Chit+    { prev :: [String]+    -- TODO: add: yank :: Maybe String to store cutted text?+    , next :: [String]+    }++type Chat = [String]+ data History =   History-    { view :: Bool-    , prev :: [String]-    , next :: [String]+    { view :: View+    , chit :: Chit+    , chat :: Chat     }  type Index = Int@@ -128,7 +146,7 @@     setLocaleEncoding utf8     hSetBuffering stderr NoBuffering     hSetBuffering stdin  NoBuffering-    hSetBuffering stdout NoBuffering+    hSetBuffering stdout LineBuffering -- NOTE: logic based on `hFlush stdout`     -- NOTE: No default output when typing     hSetEcho      stdin  False     putStrLn head >> hFlush stdout@@ -141,9 +159,13 @@           , load = Nothing           , hist =             History-              { view = False-              , prev = []-              , next = []+              { view = None+              , chit =+                Chit+                  { prev = []+                  , next = []+                  }+              , chat = []               }           , list = Nothing           , safe = Nothing@@ -170,10 +192,20 @@   case ctx of     Context { exit = True }                    ->       return ()-    Context { hist = History { view = True } } ->-      eval    ctx [ ] >>= \ (upd, res) ->-      printLn res     >>-      loop (upd { hist = ((hist ctx) { view = False }) }) eval+    Context { hist = History { view = Both } } ->+      printLn "* Chits:" >>+      (mapM_ printLn $ COM.chits True $ prev $ chit $ hist ctx) >>+      printLn "* Chats:" >>+      (mapM_ printLn $ COM.chats True $        chat $ hist ctx) >>+      loop (ctx { hist = ((hist ctx) { view = None }) }) eval+    Context { hist = History { view = Chit' } } ->+      printLn "* Chits:" >>+      (mapM_ printLn $ COM.chits True $ prev $ chit $ hist ctx) >>+      loop (ctx { hist = ((hist ctx) { view = None }) }) eval+    Context { hist = History { view = Chat' } } ->+      printLn "* Chats:" >>+      (mapM_ printLn $ COM.chats True $        chat $ hist ctx) >>+      loop (ctx { hist = ((hist ctx) { view = None }) }) eval     Context { list = Just _ } ->       eval    ctx [ ] >>= \ (upd, res) ->       printLn res     >>@@ -197,8 +229,10 @@         '/':'l'                :mfil -> caseList txt mfil         "/safe"                      -> caseSafe txt         "/s"                         -> caseSafe txt-        "/hist"                      -> caseHist txt-        "/h"                         -> caseHist txt+        "/hist"                      -> caseHist txt Both+        "/h"                         -> caseHist txt Both+        "/chit"                      -> caseHist txt Chit'+        "/chat"                      -> caseHist txt Chat'         "/wipe"                      -> caseWipe txt         "/w"                         -> caseWipe txt         "/help"                      -> caseHelp txt@@ -207,38 +241,52 @@         "/e"                         -> caseExit txt         '/':cmd                      ->           printLn msg >>-          loop (nxt txt ctx) eval+          loop (nxt txt ctx Nothing) eval           where             msg = "Command not recognized: " ++ cmd         ____________________________ ->           eval    ctx txt >>= \ (upd, res) ->           printLn res     >>-          loop (nxt txt upd) eval+          loop (nxt txt upd (Just res)) eval       where-        his     = hist ctx-        hps     = prev his-        hns     = next his-        nxt p c = c { hist = (hist c) { prev = p : hps } }+        his       = hist ctx+        chi       = chit his+        hps       = prev chi+        hns       = next chi+        nxt p c o =+          c+            { hist =+              (hist c)+                { chit =+                  (chit $ hist c)+                    { prev = p : hps+                    }+                , chat =+                  case o of+                    Nothing -> (       chat $ hist c)+                    Just  r -> ((r:) $ chat $ hist c)+                }+            }         -- NOTE: Enforce DRY in cases         caseMode txt c cs =           case mmod of             Just mod ->               printLn ("Changed to " ++ low ++ "-mode") >>-              loop (nxt txt (ctx { mode = mod, safe = Nothing })) eval+              loop (nxt txt (ctx { mode = mod, safe = Nothing }) Nothing) eval               where                 low = map toLower $ show mod             Nothing ->               printLn ("Invalid mode: " ++ c:cs) >>-              loop (nxt txt ctx) eval+              loop (nxt txt ctx Nothing) eval           where             mmod = readMaybe (toUpper c : map toLower cs) :: Maybe Mode         caseFile txt midx =           case lst of             Just _ ->-              loop (nxt txt (ctx { file = (, False) <$> idx })) eval+              loop (nxt txt (ctx { file = (, False) <$> idx }) Nothing) eval             Nothing ->               printLn ("Invalid index") >>-              loop (nxt txt ctx) eval+              loop (nxt txt ctx Nothing) eval           where             lst = safe ctx             idx =@@ -249,10 +297,10 @@         caseNums txt midx =           case lst of             Just _ ->-              loop (nxt txt (ctx { file = (, True) <$> idx })) eval+              loop (nxt txt (ctx { file = (, True) <$> idx }) Nothing) eval             Nothing ->               printLn ("Invalid index") >>-              loop (nxt txt ctx) eval+              loop (nxt txt ctx Nothing) eval           where             lst = safe ctx             idx =@@ -261,7 +309,7 @@                 ' ':cs -> readMaybe cs :: Maybe Int                 ______ -> Nothing         caseList txt mfil =-          loop (nxt txt (ctx { list = fil })) eval+          loop (nxt txt (ctx { list = fil }) Nothing) eval           where             fil =               case mfil of@@ -269,20 +317,20 @@                 ' ':cs -> Just cs                 ______ -> Nothing         caseSafe txt =-          loop (nxt txt (ctx { list = safe ctx })) eval-        caseHist txt =-          loop (nxt txt (ctx { hist = his { view = True } })) eval+          loop (nxt txt (ctx { list = safe ctx }) Nothing) eval+        caseHist txt v =+          loop (nxt txt (ctx { hist = his { view = v } }) Nothing) eval         caseWipe txt =-          -- NOTE: Clear screen & Move top-left           printLn clear >>-          loop (nxt txt ctx) eval+          loop (nxt txt ctx Nothing) eval         caseHelp txt =           printLn help >>-          loop (nxt txt ctx) eval+          loop (nxt txt ctx Nothing) eval         caseExit txt =           printLn "Λ-gent will shutdown" >>-          loop (nxt txt (ctx { exit = True })) eval+          loop (nxt txt (ctx { exit = True }) Nothing) eval     where+      -- NOTE: Clear screen & Move top-left       clear     = "\ESC[H\ESC[2J" -- NOTE: See `infocmp -x`       prompt    = "Λ-" ++ (map toLower $ show $ mode ctx) ++ "> "       read      = input@@ -303,8 +351,10 @@   "/?   or /help   | This message\n" ++   "/e   or /exit   | Exit Λ-gent\n" ++   "/w   or /wipe   | Clear screen\n" ++-  "/m   or /mode   | Change to {" ++ ms ++ "}. Ex: /m code\n" ++-  "/h   or /hist   | View input history\n" +++  "/m m or /mode m | Change to {" ++ ms ++ "}. Ex: /m code\n" +++  "/h   or /hist   | View chit-chat (input and output) history\n" +++  "        /chit   | View chit (input) history\n" +++  "        /chat   | View chat (output) history\n" ++   "/s   or /safe   | Show stored list of files. See /list below\n" ++   "/l f or /list f | List of files, limited by mode, file masks and filter\n" ++   "/f i or /file i | Show file with the given index. See, /safe above\n" ++
src/Agent/Utils/Code.hs view
@@ -43,7 +43,7 @@   => Maybe String   -> Int   -> Bool-  -> rio (Either String String)+  -> rio (Either String [String]) file mfilter index nums =   filesHelper mfilter False >>= \ fs ->   if length fs > index && index >= 0 then
src/Agent/Utils/Common.hs view
@@ -17,9 +17,13 @@   ( -- * Files     files   , file+    -- * History+  , chits+  , chats     -- * Helpers   , leftpad   , index+  , split   ) where @@ -42,39 +46,68 @@ index rep idx =   leftpad '0' rep $ show idx +split+  :: Char+  ->  String+  -> [String]+split sep str =+  case dropWhile (== sep) str of+    [] -> []+    cs ->+      b : split sep bs+      where+        (b, bs) = break (== sep) cs++--------------------------------------------------------------------------------+ files   :: Bool   -> [ String ]   -> [ String ] files nums fs =-  if null fs then-    []-  else-    map (\ (idx, rel) -> f idx rel)-    $ zip [0..] fs-    where-      len   = length $ show $ length fs-      f i x =-        if nums then-          index len i ++ ": " ++ x-        else-          x+  linesOptNums nums 0 fs  file-  :: Bool-  -> String-  -> String+  ::  Bool+  ->  String+  -> [String] file nums f =-  if null f then+  linesOptNums nums 1 $ lines f++--------------------------------------------------------------------------------++chits+  :: Bool+  -> [ String ]+  -> [ String ]+chits nums fs =+  linesOptNums nums 0 $ reverse fs++chats+  :: Bool+  -> [ String ]+  -> [ String ]+chats nums fs =+  linesOptNums nums 0 $ reverse fs++--------------------------------------------------------------------------------++-- HELPERS (private)++linesOptNums+  :: Bool+  -> Int+  -> [ String ]+  -> [ String ]+linesOptNums nums origo xs =+  if null xs then     []   else-    unlines-    $ map (\ (idx, rel) -> g idx rel)-    $ zip [1..] ls+    map (\ (idx, rel) -> f idx rel)+    $ zip [origo..] xs     where-      ls    = lines f-      len   = length $ show $ length ls-      g i x =+      len   = length $ show $ length xs+      f i x =         if nums then           index len i ++ ": " ++ x         else
src/Agent/Utils/Plan.hs view
@@ -43,7 +43,7 @@   => Maybe String   -> Int   -> Bool-  -> rio (Either String String)+  -> rio (Either String [String]) file mfilter index nums =   filesHelper mfilter False >>= \ fs ->   if length fs > index && index >= 0 then