@@ -27,6 +27,7 @@ type runContext struct {
2727 envName string // Target environment from yapi.config.yml
2828 jsonOutput bool // If true, output structured JSON instead of formatted output
2929 strictEnv bool // If true, error on missing env files and disable OS env fallback
30+ verbose bool // If true, show verbose output (request details, timing, headers)
3031}
3132
3233func (app * rootCommand ) runInteractiveE (cmd * cobra.Command , args []string ) error {
@@ -47,7 +48,8 @@ func (app *rootCommand) runE(cmd *cobra.Command, args []string) error {
4748 envName , _ := cmd .Flags ().GetString ("env" )
4849 jsonOutput , _ := cmd .Flags ().GetBool ("json" )
4950 strictEnv , _ := cmd .Flags ().GetBool ("strict-env" )
50- return app .runConfigPathWithOptionsE (path , envName , jsonOutput , strictEnv )
51+ verbose , _ := cmd .Flags ().GetBool ("verbose" )
52+ return app .runConfigPathWithOptionsE (path , envName , jsonOutput , strictEnv , verbose )
5153}
5254
5355func (app * rootCommand ) watchE (cmd * cobra.Command , args []string ) error {
@@ -128,37 +130,77 @@ func printWatchHeader(path string) {
128130 fmt .Printf ("%s\n \n " , color .Dim ("[" + time .Now ().Format ("15:04:05" )+ "]" ))
129131}
130132
133+ // OutputSavedError is returned when output is too large for terminal and was saved to file.
134+ type OutputSavedError struct {
135+ Path string
136+ }
137+
138+ func (e * OutputSavedError ) Error () string {
139+ return fmt .Sprintf ("output saved to %s (too large for terminal)\n view with: cat %s | jq" , e .Path , e .Path )
140+ }
141+
142+ // printResultOptions configures printResult behavior.
143+ type printResultOptions struct {
144+ skipMeta bool // Don't print URL/Time/Size (already shown in verbose mode)
145+ }
146+
131147// printResult outputs a single result with optional expectation.
132- func (app * rootCommand ) printResult (result * runner.Result , expectRes * runner.ExpectationResult ) {
148+ // configPath is used for generating auto-save filenames when output is too large.
149+ // Returns OutputSavedError if output was saved to file instead of printed.
150+ func (app * rootCommand ) printResult (result * runner.Result , expectRes * runner.ExpectationResult , configPath string , opts printResultOptions ) error {
151+ var savedPath string
133152 if result != nil {
134153 // Check if stdout is a TTY (terminal)
135154 isTTY := isTerminal (os .Stdout )
136155
137156 // Check if content is binary
138157 isBinary := utils .IsBinaryContent (result .Body )
139158
140- // Skip dumping binary output unless explicitly requested with --binary-output
141- if isBinary && ! app .binaryOutput {
159+ switch {
160+ case isBinary && ! app .binaryOutput :
161+ // Skip dumping binary output unless explicitly requested with --binary-output
142162 if isTTY {
143163 fmt .Fprintf (os .Stderr , "\n %s\n " , color .Yellow ("Binary content detected. Output hidden to prevent terminal corruption." ))
144164 fmt .Fprintf (os .Stderr , "%s\n " , color .Dim ("To display binary output, use --binary-output flag or pipe to a file." ))
145165 }
146166 // In non-TTY (CI/piped), silently skip binary output
147- } else {
148- body := strings .TrimRight (output .Highlight (result .Body , result .ContentType , app .noColor ), "\n \r " )
149- fmt .Println (body )
167+ case result .OutputFile != "" :
168+ // Output was already saved via output_file config - don't write again
169+ if len (result .Body ) > maxOutputSize {
170+ savedPath = result .OutputFile
171+ } else {
172+ // Small enough to print, but also saved to file
173+ body := output .Highlight (result .Body , result .ContentType , app .noColor )
174+ fmt .Println (strings .TrimRight (body , "\n \r " ))
175+ }
176+ default :
177+ // No output_file specified - render normally (may auto-save if large)
178+ body := result .Body
179+ if len (body ) <= maxOutputSize {
180+ body = output .Highlight (body , result .ContentType , app .noColor )
181+ }
182+ outputResult := renderOutput (body , configPath )
183+ savedPath = outputResult .SavedPath
150184 }
151185
152- printResultMeta (result )
186+ if ! opts .skipMeta {
187+ printResultMeta (result )
188+ }
153189 }
154190 if expectRes != nil {
155191 printExpectationResult (expectRes )
156192 }
193+ if savedPath != "" {
194+ return & OutputSavedError {Path : savedPath }
195+ }
196+ return nil
157197}
158198
159199// executeRunE is the unified execution pipeline for both Run and Watch modes.
160200// Returns error for middleware to capture.
161201func (app * rootCommand ) executeRunE (ctx runContext ) error {
202+ log := NewLogger (ctx .verbose )
203+
162204 opts := runner.Options {
163205 URLOverride : app .urlOverride ,
164206 NoColor : app .noColor ,
@@ -168,6 +210,8 @@ func (app *rootCommand) executeRunE(ctx runContext) error {
168210 StrictEnv : ctx .strictEnv ,
169211 }
170212
213+ log .Verbose ("Loading config: %s" , ctx .path )
214+
171215 // Load project and environment configuration
172216 projEnv , err := loadProjectAndEnv (ctx .path , ctx .envName , true )
173217 if err != nil {
@@ -180,15 +224,25 @@ func (app *rootCommand) executeRunE(ctx runContext) error {
180224
181225 // Apply project settings if found
182226 if projEnv != nil {
227+ log .Verbose ("Project: %s" , projEnv .projectRoot )
183228 opts .ProjectRoot = projEnv .projectRoot
184229 if projEnv .envVars != nil {
230+ log .Verbose ("Environment: %s (%d vars)" , projEnv .envName , len (projEnv .envVars ))
185231 opts .EnvOverrides = projEnv .envVars
186232 opts .ProjectEnv = projEnv .envName
187233 }
188234 }
189235
236+ log .Verbose ("Sending request..." )
190237 runRes := app .engine .RunConfig (context .Background (), ctx .path , opts )
191238
239+ // Log response details if available
240+ if runRes .Result != nil {
241+ log .Response (runRes .Result .StatusCode , runRes .Result .Headers , runRes .Result .Duration , runRes .Result .BodyBytes )
242+ } else if runRes .Error != nil {
243+ log .Verbose ("Request failed: %v" , runRes .Error )
244+ }
245+
192246 // Handle validation/parse errors first
193247 if runRes .Error != nil && runRes .Analysis == nil {
194248 if ctx .strict || ctx .returnErrors {
@@ -236,7 +290,9 @@ func (app *rootCommand) executeRunE(ctx runContext) error {
236290 })
237291 }
238292
239- app .printResult (runRes .Result , runRes .ExpectRes )
293+ if err := app .printResult (runRes .Result , runRes .ExpectRes , ctx .path , printResultOptions {skipMeta : ctx .verbose }); err != nil {
294+ return err
295+ }
240296
241297 if runRes .Error != nil {
242298 if ctx .strict || ctx .returnErrors {
@@ -261,14 +317,17 @@ func (app *rootCommand) executeChain(ctx runContext, runRes *core.RunConfigResul
261317 }
262318
263319 // Print results from all completed steps (even if chain failed)
320+ var outputSavedErr error
264321 if chainResult != nil {
265322 for i , stepResult := range chainResult .Results {
266323 fmt .Fprintf (os .Stderr , "\n --- Step %d: %s ---\n " , i + 1 , chainResult .StepNames [i ])
267324 var expectRes * runner.ExpectationResult
268325 if i < len (chainResult .ExpectationResults ) {
269326 expectRes = chainResult .ExpectationResults [i ]
270327 }
271- app .printResult (stepResult , expectRes )
328+ if err := app .printResult (stepResult , expectRes , ctx .path , printResultOptions {}); err != nil {
329+ outputSavedErr = err
330+ }
272331 }
273332 }
274333
@@ -283,7 +342,7 @@ func (app *rootCommand) executeChain(ctx runContext, runRes *core.RunConfigResul
283342 fmt .Fprintln (os .Stderr , "\n Chain completed successfully." )
284343 out , noColor := app .io (ctx .strict )
285344 validation .PrintWarnings (runRes .Analysis , out , noColor )
286- return nil
345+ return outputSavedErr
287346}
288347
289348// runConfigPathE runs a config file in strict mode (returns error)
@@ -299,8 +358,8 @@ func (app *rootCommand) runConfigPathWithEnvAndJSONE(path string, envName string
299358}
300359
301360// runConfigPathWithOptionsE runs a config file with all options
302- func (app * rootCommand ) runConfigPathWithOptionsE (path string , envName string , jsonOutput bool , strictEnv bool ) error {
303- return app .executeRunE (runContext {path : path , strict : true , envName : envName , jsonOutput : jsonOutput , strictEnv : strictEnv })
361+ func (app * rootCommand ) runConfigPathWithOptionsE (path string , envName string , jsonOutput bool , strictEnv bool , verbose bool ) error {
362+ return app .executeRunE (runContext {path : path , strict : true , envName : envName , jsonOutput : jsonOutput , strictEnv : strictEnv , verbose : verbose })
304363}
305364
306365// printExpectationResult prints expectation results to stderr
0 commit comments