packages feed

baikai-kit-0.4.0.0: src/Baikai/Kit/Session.hs

module Baikai.Kit.Session
  ( agentDirsForSession,
    codexSessionArgs,
  )
where

import Baikai.Interactive (InteractiveProvider (InteractiveCodex))
import Baikai.Kit.CodexConfig (enableSkillsArgs)
import Baikai.Kit.Config (KitConfig, KitScope (..), projectAgentsDir, providerAgentsBase, sidecarFileName, userAgentsDir)
import Baikai.Kit.Sidecar (readSidecar)
import Baikai.Prelude
import Control.Monad (filterM, forM)
import Data.List (nub, sort)
import Data.Maybe (fromMaybe)
import Data.Text qualified as Text
import System.Directory (canonicalizePath, doesDirectoryExist, listDirectory)
import System.FilePath ((</>))

agentDirsForSession :: KitConfig -> IO [FilePath]
agentDirsForSession config = do
  userDir <- userAgentsDir config
  projectDir <- projectAgentsDir config
  filterM doesDirectoryExist [userDir, projectDir]

-- | Add these to a tool's Codex launch request's extraArgs. Other providers
-- use agentDirsForSession instead. Only this tool's sidecars contribute.
codexSessionArgs :: KitConfig -> IO [Text]
codexSessionArgs config
  | InteractiveCodex `notElem` (config ^. #providers) = pure []
  | otherwise = do
      paths <- fmap concat . forM [UserScope, ProjectScope] $ \scope -> do
        base <- providerAgentsBase config InteractiveCodex scope
        let root = base </> ".agents/skills"
        exists <- doesDirectoryExist root
        names <- if exists then sort <$> listDirectory root else pure []
        fmap concat . forM names $ \name -> do
          meta <- readSidecar (root </> name </> Text.unpack (sidecarFileName config))
          let tracked = map Text.unpack (fromMaybe [] (meta >>= (^. #codexDisabledSkills)))
              hidden = [root </> name </> "SKILL.md" | (meta >>= (^. #visibility)) == Just "tool-only"]
          pure (hidden ++ tracked)
      canonical <- traverse canonicalizePath paths
      pure (enableSkillsArgs (nub canonical))