gachi-cli-0.1.0.0: src/Gachi/CLI/Retrofit.hs
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- | Module: Gachi.CLI.Retrofit
-- Description: Command execution engine for 'gachi retrofit'
--
-- This module coordinates the diagnostic inspection and non-destructive overlay
-- workflows for the @gachi retrofit@ subcommand (Milestones 4.1 & 4.2, Phase 4).
--
-- = Architectural Context & Invariants
--
-- Per Note [Non-Destructive Skeleton Overlay] [ref:note-nondestructive-retrofit],
-- retrofitting operations adopt legacy, single-package, or external repositories
-- into the canonical ecosystem skeleton without altering domain code.
--
-- == Operational Workflow
--
-- The retrofitting execution lifecycle unfolds in two stages:
--
-- 1. __Diagnostic Inspection (Milestone 4.1):__
-- Executes the comprehensive 'DiagnosticReport' scanner, evaluating:
-- - Programming language distribution and file percentages.
-- - Build tool manifests and workspace topography.
-- - Test infrastructure and declared test suites.
-- - Documentation gaps relative to @DOC_STRATEGY.md@.
-- - Git governance, hook executability, and RFC 2822 @Assisted-by:@ trailer validation.
-- - Physical verification harnesses and quality gates.
--
-- 2. __Non-Destructive Overlay (Milestone 4.2):__
-- Computes the 'OverlayPlan', categorizing missing standardized assets as 'ActionCreate'
-- and pre-existing files as 'ActionPreserve'. In live execution mode, writes missing
-- documentation, agent rules, Git hooks, and verification runners with executable permissions
-- and configures Git governance without touching domain source files.
--
-- == Formatting & Execution Modes
--
-- * __Standard Terminal Mode (Default):__ Emits an 80-column aligned ASCII terminal
-- dashboard featuring boxed sections, Unicode score progress bars, and aligned checklists.
-- * __Markdown Mode (@--markdown@ / @-m@):__ Emits clean GitHub-Flavored Markdown tables
-- suitable for piping into @RETROFIT_PLAN.md@ or CI workflow artifacts.
-- * __Simulation Mode (@--dry-run@):__ Previews the diagnostic inspection report and
-- the proposed non-destructive overlay plan without modifying any filesystem files.
-- * __Materialization Mode (Live):__ Materializes the overlay plan and activates Git
-- governance hooks in the target repository.
--
-- = Worked Example
--
-- >>> import Gachi.CLI.Options (RetrofitOptions(..))
-- >>> let opts = RetrofitOptions (Just ".") True False
-- >>> code <- runRetrofit opts
-- >>> code == ExitSuccess
-- True
module Gachi.CLI.Retrofit
( -- * High-Level Subcommand Runner
runRetrofit
-- * Operational Execution Routines
, runInspectionAndOverlay
) where
import Control.Exception (SomeException, catch)
import Control.Monad (unless)
import Data.Maybe (fromMaybe)
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import System.Directory (doesDirectoryExist)
import System.Exit (ExitCode(..))
import System.IO (stderr)
import Gachi.CLI.Options (RetrofitOptions(..))
import Gachi.Retrofit
( OverlayResult(..)
, computeOverlayPlan
, evaluateQualityGates
, executeOverlayPlan
, formatDiagnosticMarkdown
, formatDiagnosticReport
, formatOverlayPlan
, formatOverlayPlanMarkdown
, formatOverlayResult
, formatQualityGateMarkdown
, formatQualityGateReport
, scanRepository
)
--------------------------------------------------------------------------------
-- 1. High-Level Subcommand Runner
--------------------------------------------------------------------------------
-- | Executes the @gachi retrofit@ subcommand from parsed 'RetrofitOptions'.
--
-- === Execution Steps
--
-- 1. Resolves target directory from 'retrofitTargetDir', defaulting to current working
-- directory (@.@) when omitted.
-- 2. Verifies physical existence of the target directory on disk, failing cleanly
-- with 'ExitFailure 1' if missing.
-- 3. Dispatches to 'runInspectionAndOverlay' to execute diagnostic analysis, compute
-- the non-destructive overlay plan, and optionally apply disk materializations.
--
-- === Exit Codes
--
-- * 'ExitSuccess': Inspection and overlay completed successfully.
-- * 'ExitFailure 1': Target directory does not exist or was inaccessible.
runRetrofit :: RetrofitOptions -> IO ExitCode
runRetrofit opts = do
let targetDir = fromMaybe "." (retrofitTargetDir opts)
isDir <- doesDirectoryExist targetDir `catch` (\(_ :: SomeException) -> pure False)
if not isDir
then do
TIO.hPutStrLn stderr ("gachi retrofit error: Target directory '" <> T.pack targetDir <> "' does not exist.")
pure (ExitFailure 1)
else runInspectionAndOverlay targetDir (retrofitDryRun opts) (retrofitMarkdown opts)
--------------------------------------------------------------------------------
-- 2. Operational Routines
--------------------------------------------------------------------------------
-- | Performs the static diagnostic analysis of the target directory, computes
-- the non-destructive overlay plan, and conditionally executes the materialization.
--
-- Observes Note [Non-Destructive Skeleton Overlay] [ref:note-nondestructive-retrofit]:
-- in simulation mode (@--dry-run@), displays the proposed overlay actions without disk mutations;
-- in materialization mode, writes missing standardized assets and pins Git governance.
runInspectionAndOverlay :: FilePath -> Bool -> Bool -> IO ExitCode
runInspectionAndOverlay targetDir isDryRun isMarkdown = do
-- 1. Milestone 4.1 Diagnostic Inspection
report <- scanRepository targetDir
if isMarkdown
then TIO.putStr (formatDiagnosticMarkdown report)
else TIO.putStr (formatDiagnosticReport report)
-- 2. Milestone 4.2 Non-Destructive Overlay Plan Computation
plan <- computeOverlayPlan targetDir report
-- 3. Execution or Simulation Branching
if isDryRun
then do
if isMarkdown
then TIO.putStr (formatOverlayPlanMarkdown plan)
else TIO.putStr (formatOverlayPlan plan)
TIO.putStrLn " [*] Simulation mode (--dry-run): No filesystem changes planned or applied."
else do
unlessMarkdown $ TIO.putStr (formatOverlayPlan plan)
result <- executeOverlayPlan plan
if isMarkdown
then do
TIO.putStrLn ""
TIO.putStrLn "## Overlay Materialization Result"
TIO.putStrLn ""
TIO.putStrLn ("- **Files Created:** " <> T.pack (show (length (resultCreatedFiles result))))
TIO.putStrLn ("- **Files Preserved:** " <> T.pack (show (length (resultPreservedFiles result))))
TIO.putStrLn ("- **Git Actions:** " <> T.pack (show (length (resultGitActions result))))
else TIO.putStr (formatOverlayResult result)
TIO.putStrLn " [+] Milestone 4.2 Non-Destructive Skeleton Overlay applied successfully."
-- 4. Milestone 4.3 Progressive Quality Gate Evaluation
qgReport <- evaluateQualityGates targetDir
if isMarkdown
then TIO.putStr (formatQualityGateMarkdown qgReport)
else TIO.putStr (formatQualityGateReport qgReport)
pure ExitSuccess
where
unlessMarkdown = unless isMarkdown