never executed always true always false
1 module PureClaw.Channels.CLI
2 ( -- * CLI channel
3 mkCLIChannelHandle
4 ) where
5
6 import Data.Maybe qualified
7 import Data.Text qualified as T
8 import Data.Text.IO qualified as TIO
9 import System.Console.Haskeline qualified as HL
10 import System.Directory (createDirectoryIfMissing)
11 import System.IO (hFlush, stderr, stdout)
12
13 import PureClaw.CLI.Config (getPureclawDir)
14 import PureClaw.Core.Types
15 import PureClaw.Handles.Channel
16
17 -- | Create a channel handle that uses haskeline for line editing.
18 -- Provides readline-style input: backspace, arrow keys, up/down history,
19 -- Ctrl-A/E, etc. History is persisted to @~\/.pureclaw\/history@.
20 -- Accepts an optional completion function for tab completion of slash commands.
21 mkCLIChannelHandle :: Maybe (HL.CompletionFunc IO) -> IO ChannelHandle
22 mkCLIChannelHandle mCompleter = do
23 histPath <- haskelineHistoryPath
24 let settings = (HL.defaultSettings :: HL.Settings IO)
25 { HL.historyFile = Just histPath
26 , HL.complete = Data.Maybe.fromMaybe HL.completeFilename mCompleter
27 }
28 pure ChannelHandle
29 { _ch_receive = do
30 mLine <- HL.runInputT settings (HL.getInputLine "> ")
31 case mLine of
32 Nothing -> ioError (userError "EOF") -- Ctrl-D: agent loop catches this
33 Just line -> pure IncomingMessage
34 { _im_userId = UserId "cli-user"
35 , _im_content = T.pack line
36 }
37 , _ch_send = \msg -> do
38 TIO.putStrLn ""
39 TIO.putStrLn (_om_content msg)
40 TIO.putStrLn ""
41 , _ch_sendError = \err ->
42 TIO.hPutStrLn stderr $ "Error: " <> T.pack (show err)
43 , _ch_sendChunk = \case
44 ChunkText t -> do
45 TIO.putStr t
46 hFlush stdout
47 ChunkDone -> TIO.putStrLn ""
48 , _ch_streaming = True
49 , _ch_readSecret = do
50 mLine <- HL.runInputT settings (HL.getPassword Nothing "")
51 pure (maybe "" T.pack mLine)
52 , _ch_prompt = \promptText -> do
53 mLine <- HL.runInputT settings (HL.getInputLine (T.unpack promptText))
54 pure (maybe "" T.pack mLine)
55 , _ch_promptSecret = \promptText -> do
56 mLine <- HL.runInputT settings (HL.getPassword Nothing (T.unpack promptText))
57 pure (maybe "" T.pack mLine)
58 }
59
60 -- | Path to the haskeline history file: @~\/.pureclaw\/history@.
61 -- Creates the directory if it does not exist.
62 haskelineHistoryPath :: IO FilePath
63 haskelineHistoryPath = do
64 dir <- getPureclawDir
65 createDirectoryIfMissing True dir
66 pure (dir <> "/history")