packages feed

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

{-# LANGUAGE TemplateHaskell #-}

module Seihou.CLI.Bootstrap
  ( handleBootstrap,
  )
where

import Data.FileEmbed (embedFile)
import Data.Text qualified as T
import Data.Text.Encoding qualified as TE
import Data.Text.IO qualified as TIO
import Seihou.CLI.AgentCompletion
  ( AgentModelConfig (..),
    AgentProvider (..),
    buildAgentCompletionRequest,
    runAgentCompletion,
  )
import Seihou.CLI.AgentLaunch
  ( AgentContext (..),
    bootstrapAllowedTools,
    formatAvailableModules,
    formatLocalModules,
    formatManifestState,
    formatModuleDhallState,
    formatSeihouProjectState,
    gatherAgentContext,
    substitute,
  )
import Seihou.CLI.AgentLaunchExec (launchConfiguredAgent)
import Seihou.CLI.Commands (BootstrapOpts (..))
import Seihou.Prelude
import System.Exit (exitFailure, exitWith)

-- | The prompt template, embedded at compile time from data/bootstrap-prompt.md.
promptTemplate :: Text
promptTemplate = TE.decodeUtf8 $(embedFile "data/bootstrap-prompt.md")

handleBootstrap :: Bool -> AgentModelConfig -> BootstrapOpts -> IO ()
handleBootstrap debug modelConfig bootstrapOpts = do
  ctx <- gatherAgentContext
  let systemPrompt = renderPrompt ctx bootstrapOpts
  runRenderedAgentPrompt debug modelConfig systemPrompt bootstrapOpts.bootstrapPrompt

renderPrompt :: AgentContext -> BootstrapOpts -> Text
renderPrompt ctx bootstrapOpts =
  substitute
    [ ("cwd", ctx.cwd),
      ("seihou_project_state", formatSeihouProjectState ctx),
      ("manifest_state", formatManifestState ctx),
      ("module_dhall_state", formatModuleDhallState ctx),
      ("local_modules", formatLocalModules ctx),
      ("available_modules", formatAvailableModules ctx),
      ("bootstrap_mode", bootstrapMode bootstrapOpts)
    ]
    promptTemplate

runRenderedAgentPrompt :: Bool -> AgentModelConfig -> Text -> Maybe Text -> IO ()
runRenderedAgentPrompt debug modelConfig systemPrompt initialPrompt
  | debug = TIO.putStr systemPrompt
  | modelConfig.agentProvider == AgentProviderClaudeCli || modelConfig.agentProvider == AgentProviderCodexCli = do
      exitCode <- launchConfiguredAgent modelConfig bootstrapAllowedTools debug systemPrompt initialPrompt
      exitWith exitCode
  | otherwise = do
      result <- runAgentCompletion (buildAgentCompletionRequest modelConfig systemPrompt initialPrompt)
      case result of
        Right assistantText -> TIO.putStrLn assistantText
        Left err -> do
          TIO.putStrLn $ "Error: " <> err
          exitFailure

bootstrapMode :: BootstrapOpts -> Text
bootstrapMode opts
  | opts.bootstrapRepo =
      T.unlines
        [ "**Mode: Multi-module repository**",
          "",
          "You are bootstrapping a multi-module repository. This means:",
          "- Create a `seihou-registry.dhall` at the repository root",
          "- Organize modules under a `modules/` directory",
          "- Each module gets its own `module.dhall` and `files/` subdirectory",
          "- Modules can declare dependencies on each other",
          "- Add meaningful tags to each registry entry for discoverability",
          "",
          "Start by asking what modules the repository should contain, then create",
          "them one by one before writing the registry file."
        ]
  | otherwise =
      T.unlines
        [ "**Mode: Single module**",
          "",
          "You are bootstrapping a single Seihou module. Ask the user what kind of",
          "project they want to scaffold, then create a complete module with variables,",
          "templates, prompts, and validation."
        ]