packages feed

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

module Seihou.CLI.Browse
  ( handleBrowse,
  )
where

import Data.Text qualified as T
import Data.Text.IO qualified as TIO
import Seihou.CLI.BrowseFormat (formatBrowseRegistry, formatBrowseSingleBlueprint, formatBrowseSingleModule, formatBrowseSinglePrompt)
import Seihou.CLI.Commands (BrowseOpts (..))
import Seihou.CLI.Registry.Sync (checkRegistryVersionDrift)
import Seihou.CLI.Shared (logIO)
import Seihou.Core.Install (parseModuleName)
import Seihou.Core.Registry (EntryKind (..), Registry (..), RegistryEntry (..), RepoContents (..), discoverRepoContents)
import Seihou.Core.Types
import Seihou.Dhall.Eval (evalAgentPromptFromFile, evalBlueprintFromFile, evalModuleFromFile, evalRecipeFromFile, evalRegistryFromFile)
import Seihou.Effect.Logger (logError, logWarn)
import Seihou.Prelude
import System.Exit (ExitCode (..), exitFailure)
import System.IO.Temp (withSystemTempDirectory)
import System.Process (readProcessWithExitCode)

handleBrowse :: BrowseOpts -> IO ()
handleBrowse bopts = do
  let source = bopts.browseSource

  withSystemTempDirectory "seihou-browse" $ \tmpDir -> do
    let repoName = parseModuleName source
        cloneDir = tmpDir </> repoName

    -- Shallow clone
    (exitCode, _stdout, stderr) <- readProcessWithExitCode "git" ["clone", "--depth", "1", T.unpack source, cloneDir] ""
    case exitCode of
      ExitFailure _ -> do
        logIO LogNormal $ do
          logError $ "git clone failed for '" <> source <> "'."
          logError $ "  " <> T.pack stderr
        exitFailure
      ExitSuccess -> pure ()

    contents <- discoverRepoContents evalRegistryFromFile cloneDir
    case contents of
      EmptyRepo -> do
        logIO LogNormal (logError "repository contains neither seihou-registry.dhall nor module.dhall.")
        exitFailure
      SingleModule rootDir -> do
        let dhallFile = rootDir </> "module.dhall"
        decoded <- evalModuleFromFile dhallFile
        case decoded of
          Left err -> do
            logIO LogNormal (logError $ "failed to load module: " <> T.pack (show err))
            exitFailure
          Right m ->
            TIO.putStr $ formatBrowseSingleModule source m.name.unModuleName m.description
      SingleRecipe rootDir -> do
        let dhallFile = rootDir </> "recipe.dhall"
        decoded <- evalRecipeFromFile dhallFile
        case decoded of
          Left err -> do
            logIO LogNormal (logError $ "failed to load recipe: " <> T.pack (show err))
            exitFailure
          Right r ->
            TIO.putStr $ formatBrowseSingleModule source r.name.unRecipeName r.description
      SingleBlueprint rootDir -> do
        let dhallFile = rootDir </> "blueprint.dhall"
        decoded <- evalBlueprintFromFile dhallFile
        case decoded of
          Left err -> do
            logIO LogNormal (logError $ "failed to load blueprint: " <> T.pack (show err))
            exitFailure
          Right b -> do
            let bpName = case b of Blueprint nm _ _ _ _ _ _ _ _ _ -> nm
                bpDesc = case b of Blueprint _ _ d _ _ _ _ _ _ _ -> d
            TIO.putStr $ formatBrowseSingleBlueprint source bpName.unModuleName bpDesc
      SinglePrompt rootDir -> do
        let dhallFile = rootDir </> "prompt.dhall"
        decoded <- evalAgentPromptFromFile dhallFile
        case decoded of
          Left err -> do
            logIO LogNormal (logError $ "failed to load prompt: " <> T.pack (show err))
            exitFailure
          Right p ->
            TIO.putStr $ formatBrowseSinglePrompt source p.name.unModuleName p.description
      MultiModule registry -> do
        driftWarnings <- checkRegistryVersionDrift cloneDir registry
        logIO LogNormal (mapM_ logWarn driftWarnings)
        let matchTag e = case bopts.browseTag of
              Nothing -> True
              Just tag -> tag `elem` e.tags
            tagged =
              [(ModuleEntry, e) | e <- registry.modules, matchTag e]
                ++ [(RecipeEntry, e) | e <- registry.recipes, matchTag e]
                ++ [(BlueprintEntry, e) | e <- registry.blueprints, matchTag e]
                ++ [(PromptEntry, e) | e <- registry.prompts, matchTag e]
        TIO.putStr $ formatBrowseRegistry source registry tagged bopts.browseTag