packages feed

seihou-cli-0.6.0.0: src/Seihou/CLI/AgentConfigShow.hs

module Seihou.CLI.AgentConfigShow
  ( handleAgentConfigShow,
    formatResolvedAgentConfig,
  )
where

import Data.Generics.Labels ()
import Data.Text qualified as T
import Data.Text.IO qualified as TIO
import Seihou.CLI.AgentCompletion (effortToText, providerToText, traceToText)
import Seihou.CLI.AgentConfig
  ( AgentField (..),
    ResolvedAgentField (..),
    ResolvedCommandConfig (..),
    agentCommandLabel,
    agentConfigSourceLabel,
    loadResolvedAgentConfig,
  )
import Seihou.Prelude
import System.Exit (exitFailure)

-- | @seihou agent config@: read the real environment and config, resolve the
-- provider, model, reasoning effort, and trace destination for every agent
-- command, and print a table labelling the source that supplied each value,
-- followed by the precedence legend.
handleAgentConfigShow :: IO ()
handleAgentConfigShow = do
  result <- loadResolvedAgentConfig
  case result of
    Left err -> do
      TIO.putStrLn $ "Error: " <> err
      exitFailure
    Right resolved -> TIO.putStr (formatResolvedAgentConfig resolved)

-- | Render the resolved per-command configuration as the displayed block. Pure,
-- so it is unit-testable without touching the filesystem.
formatResolvedAgentConfig :: [ResolvedCommandConfig] -> Text
formatResolvedAgentConfig resolved =
  T.unlines $
    [ "Resolved agent provider, model, effort, and trace per command",
      "(highest-precedence source wins; see precedence list below)",
      ""
    ]
      <> concatMap renderCommand resolved
      <> ["", precedenceLegend]
  where
    labelWidth = maximum (0 : map (\rcc -> T.length (agentCommandLabel (rcc ^. #command))) resolved)
    valueWidth = maximum (0 : concatMap commandValueWidths resolved)

    commandValueWidths rcc =
      [ T.length (providerValue rcc),
        T.length (modelValue rcc),
        T.length (effortValue rcc),
        T.length (traceValue rcc)
      ]

    providerValue rcc = providerToText (rcc ^. #provider . #value)
    modelValue rcc = maybe "(default)" id (rcc ^. #model . #value)
    effortValue rcc = maybe "(default)" effortToText (rcc ^. #effort . #value)
    traceValue rcc = traceToText (rcc ^. #trace . #value)

    renderCommand rcc =
      let cmd = (rcc ^. #command)
          label = agentCommandLabel cmd
       in [ row
              (padRight labelWidth label)
              "provider"
              (providerValue rcc)
              (agentConfigSourceLabel cmd ProviderField (rcc ^. #provider . #source)),
            row
              (padRight labelWidth "")
              "model   "
              (modelValue rcc)
              (agentConfigSourceLabel cmd ModelField (rcc ^. #model . #source)),
            row
              (padRight labelWidth "")
              "effort  "
              (effortValue rcc)
              (agentConfigSourceLabel cmd EffortField (rcc ^. #effort . #source)),
            row
              (padRight labelWidth "")
              "trace   "
              (traceValue rcc)
              (agentConfigSourceLabel cmd TraceField (rcc ^. #trace . #source))
          ]

    row label field value sourceLabel =
      "  "
        <> label
        <> "  "
        <> field
        <> "  "
        <> padRight valueWidth value
        <> "  ["
        <> sourceLabel
        <> "]"

padRight :: Int -> Text -> Text
padRight width value = value <> T.replicate (max 0 (width - T.length value)) " "

precedenceLegend :: Text
precedenceLegend =
  T.intercalate
    "\n"
    [ "Precedence, highest first:",
      "  1. --provider / --model / --effort / --trace flag on the subcommand",
      "  2. --provider / --model / --effort / --trace flag on `seihou agent`",
      "  3. SEIHOU_AGENT_PROVIDER / SEIHOU_AGENT_MODEL / SEIHOU_AGENT_EFFORT /",
      "     SEIHOU_AGENT_TRACE environment variables",
      "  4. blueprint.dhall / prompt.dhall       launch.{provider,model,effort}",
      "  5. local  .seihou/config.dhall          agent.<command>.{provider,model,effort,trace}",
      "  6. local  .seihou/config.dhall          agent.{provider,model,effort,trace}",
      "  7. global ~/.config/seihou/config.dhall  agent.<command>.{provider,model,effort,trace}",
      "  8. global ~/.config/seihou/config.dhall  agent.{provider,model,effort,trace}",
      "  9. built-in default: provider claude-cli; model pinned per provider",
      "     (claude-cli -> claude-opus-4-8, codex-cli -> gpt-5.6-terra); effort unset",
      "     (the CLI/provider chooses its own reasoning effort); trace off",
      "",
      "Tier 4 is per-artifact: it depends on which blueprint or prompt you run, so",
      "the table above cannot show it. Run with --verbose to see the resolved",
      "settings and their sources for a specific run. `trace` has no tier-4",
      "declaration: no blueprint or prompt schema field feeds it.",
      "",
      "The trace file path is set with agent.tracePath (local, then global). It is",
      "free-form and has no flag, environment variable, or per-command variant;",
      "unset means .seihou/trace.jsonl."
    ]