packages feed

seihou-cli-0.4.0.0: src-exe/Seihou/CLI/AgentLaunchExec.hs

module Seihou.CLI.AgentLaunchExec
  ( launchConfiguredAgent,
    launchConfiguredAgentAddingDirs,
    launchConfiguredAgentWith,
  )
where

import Baikai.Interactive
  ( CodexApprovalPolicy (CodexApprovalOnRequest),
    CodexSandboxMode (CodexWorkspaceWrite),
    InteractiveLaunchResult (..),
    InteractiveSafety (ClaudeAllowedTools, CodexSandbox),
    extraDirs,
    interactiveLaunchRequest,
    modelId,
    safety,
    systemPrompt,
    workingDir,
  )
import Baikai.Kit.Session qualified as KitSession
import Baikai.Provider.Claude.Interactive
  ( defaultClaudeInteractiveConfig,
    launchClaudeInteractive,
  )
import Baikai.Provider.OpenAI.Interactive
  ( defaultCodexInteractiveConfig,
    launchCodexInteractive,
  )
import Data.Text qualified as T
import Data.Text.IO qualified as TIO
import Seihou.CLI.AgentCompletion (AgentModelConfig (..), AgentProvider (..))
import Seihou.CLI.Kit (seihouKitConfig)
import Seihou.Prelude
import System.Directory (findExecutable, getCurrentDirectory)
import System.Exit (ExitCode (..), exitFailure)

launchConfiguredAgent :: AgentModelConfig -> [String] -> Bool -> Text -> Maybe Text -> IO ExitCode
launchConfiguredAgent = launchConfiguredAgentAddingDirs []

launchConfiguredAgentAddingDirs :: [FilePath] -> AgentModelConfig -> [String] -> Bool -> Text -> Maybe Text -> IO ExitCode
launchConfiguredAgentAddingDirs extraDirs modelConfig tools debug systemPrompt initialPrompt = do
  sessionDirs <- KitSession.agentDirsForSession seihouKitConfig
  launchConfiguredAgentWith (sessionDirs <> extraDirs) modelConfig tools debug systemPrompt initialPrompt

launchConfiguredAgentWith :: [FilePath] -> AgentModelConfig -> [String] -> Bool -> Text -> Maybe Text -> IO ExitCode
launchConfiguredAgentWith addDirs modelConfig tools debug systemPrompt initialPrompt
  | debug = do
      TIO.putStr systemPrompt
      pure ExitSuccess
  | otherwise =
      case modelConfig.agentProvider of
        AgentProviderClaudeCli ->
          launchClaude addDirs tools modelConfig.agentModel systemPrompt initialPrompt
        AgentProviderCodexCli ->
          launchCodex addDirs modelConfig.agentModel systemPrompt initialPrompt
        AgentProviderAnthropic ->
          unsupportedInteractiveProvider "anthropic"
        AgentProviderOpenAI ->
          unsupportedInteractiveProvider "openai"

launchClaude :: [FilePath] -> [String] -> Maybe Text -> Text -> Maybe Text -> IO ExitCode
launchClaude addDirs tools model systemPrompt initialPrompt = do
  claudePath <- findExecutable "claude"
  case claudePath of
    Nothing -> do
      TIO.putStrLn "Error: 'claude' CLI (Claude Code) not found on PATH."
      TIO.putStrLn "Install it from: https://docs.anthropic.com/en/docs/claude-code"
      exitFailure
    Just _ -> do
      cwd <- getCurrentDirectory
      InteractiveLaunchResult {exitCode} <-
        launchClaudeInteractive
          defaultClaudeInteractiveConfig
          (interactiveLaunchRequest (promptOrEmpty initialPrompt))
            { systemPrompt = Just systemPrompt,
              modelId = model,
              workingDir = Just cwd,
              extraDirs = addDirs,
              safety = ClaudeAllowedTools (map T.pack tools)
            }
      pure exitCode

launchCodex :: [FilePath] -> Maybe Text -> Text -> Maybe Text -> IO ExitCode
launchCodex addDirs model systemPrompt initialPrompt = do
  codexPath <- findExecutable "codex"
  case codexPath of
    Nothing -> do
      TIO.putStrLn "Error: 'codex' CLI not found on PATH."
      TIO.putStrLn "Install and authenticate Codex CLI, then retry."
      exitFailure
    Just _ -> do
      cwd <- getCurrentDirectory
      InteractiveLaunchResult {exitCode} <-
        launchCodexInteractive
          defaultCodexInteractiveConfig
          (interactiveLaunchRequest (promptOrEmpty initialPrompt))
            { systemPrompt = Just systemPrompt,
              modelId = model,
              workingDir = Just cwd,
              extraDirs = addDirs,
              safety = CodexSandbox CodexWorkspaceWrite CodexApprovalOnRequest
            }
      pure exitCode

promptOrEmpty :: Maybe Text -> Text
promptOrEmpty = maybe "" id

unsupportedInteractiveProvider :: Text -> IO ExitCode
unsupportedInteractiveProvider providerName = do
  TIO.putStrLn $
    "Error: provider '"
      <> providerName
      <> "' is an API provider and cannot start an interactive local agent session."
  TIO.putStrLn "Use --provider claude-cli or --provider codex-cli for interactive agent sessions."
  exitFailure