@@ -3,6 +3,7 @@ package commands
33import (
44 "context"
55 "fmt"
6+ "os"
67 "sync"
78 "time"
89
@@ -15,6 +16,7 @@ import (
1516 "github.com/compozy/gograph/pkg/errors"
1617 "github.com/compozy/gograph/pkg/logger"
1718 "github.com/compozy/gograph/pkg/progress"
19+ "github.com/mattn/go-isatty"
1820 "github.com/spf13/cobra"
1921)
2022
@@ -116,6 +118,14 @@ The resulting graph allows you to:
116118 return runAnalysisWithoutProgress (projectPath , projectID , parserConfig , analyzerConfig , neo4jConfig )
117119 }
118120
121+ // Check if we're in TTY mode and suppress logging if so
122+ isTTY := isatty .IsTerminal (os .Stdout .Fd ()) || isatty .IsCygwinTerminal (os .Stdout .Fd ())
123+ if isTTY {
124+ // Suppress all logging output to avoid conflicts with TUI
125+ logger .Disable ()
126+ defer logger .Enable () // Re-enable after completion
127+ }
128+
119129 return runAnalysisWithProgress (projectPath , projectID , parserConfig , analyzerConfig , neo4jConfig )
120130 })
121131 },
@@ -206,89 +216,156 @@ func runAnalysisWithProgress(
206216 neo4jConfig * infra.Neo4jConfig ,
207217) error {
208218 ctx := context .Background ()
209- startTime := time .Now ()
210219
211- var parseResult * parser.ParseResult
212- var report * analyzer.AnalysisReport
213- var graphResult * core.AnalysisResult
220+ // Initialize adaptive progress
221+ progressIndicator := progress .NewAdaptiveProgress (os .Stdout )
222+ phases := []progress.PhaseInfo {
223+ {Name : "Parsing" , Description : "Scanning and parsing Go source files" , Weight : 0.3 },
224+ {Name : "Analysis" , Description : "Analyzing code structure and relationships" , Weight : 0.3 },
225+ {Name : "Graph Building" , Description : "Building graph representation" , Weight : 0.2 },
226+ {Name : "Storage" , Description : "Storing results in Neo4j" , Weight : 0.2 },
227+ }
228+ progressIndicator .SetPhases (phases )
229+ progressIndicator .Start (fmt .Sprintf ("Analyzing project: %s" , projectPath ))
214230
215- // -----
216- // Parsing Phase
217- // -----
218- err := progress .WithProgress ("Parsing project files" , func () error {
219- parserService := parser .NewService (parserConfig )
220- var err error
221- parseResult , err = parserService .ParseProject (ctx , projectPath , parserConfig )
222- return err
223- })
231+ // Parse project
232+ parseResult , err := runParsingPhase (ctx , projectPath , parserConfig , progressIndicator )
224233 if err != nil {
225- return fmt . Errorf ( "failed to parse project: %w" , err )
234+ return err
226235 }
227236
228- // -----
229- // Analysis Phase
230- // -----
231- err = progress .WithProgress ("Analyzing code structure" , func () error {
232- analyzerService := analyzer .NewAnalyzer (analyzerConfig )
233- analysisInput := & analyzer.AnalysisInput {
234- ProjectID : projectID .String (),
235- Files : parseResult .Files ,
236- }
237- var err error
238- report , err = analyzerService .AnalyzeProject (ctx , analysisInput )
239- return err
240- })
237+ // Analyze project
238+ report , err := runAnalysisPhase (ctx , projectID , parseResult , analyzerConfig , progressIndicator )
241239 if err != nil {
242- return fmt . Errorf ( "failed to analyze project: %w" , err )
240+ return err
243241 }
244242
245- // -----
246- // Graph Building Phase
247- // -----
248- err = progress .WithProgress ("Building graph representation" , func () error {
249- builder := graph .NewBuilder (nil ) // Use default config
250- var err error
251- graphResult , err = builder .BuildFromAnalysis (ctx , projectID , parseResult , report )
252- return err
253- })
243+ // Build graph
244+ graphResult , err := runGraphBuildingPhase (ctx , projectID , parseResult , report , progressIndicator )
254245 if err != nil {
255- return fmt . Errorf ( "failed to build graph: %w" , err )
246+ return err
256247 }
257248
258- // -----
259- // Storage Phase
260- // -----
261- err = progress .WithProgressSteps ("Storing in Neo4j" , func (update func (string ), _ func (int , int )) error {
262- update ("Connecting to database" )
263- repo , err := infra .NewNeo4jRepository (neo4jConfig )
264- if err != nil {
265- return fmt .Errorf ("failed to create Neo4j repository: %w" , err )
266- }
267- defer repo .Close ()
268-
269- update ("Storing nodes and relationships" )
270- return repo .StoreAnalysis (ctx , graphResult )
271- })
249+ // Store results
250+ err = runStoragePhase (ctx , graphResult , neo4jConfig , progressIndicator )
272251 if err != nil {
273- return fmt . Errorf ( "failed to store analysis: %w" , err )
252+ return err
274253 }
275254
276- duration := time .Since (startTime )
255+ // Success with detailed statistics
256+ successMsg := "Analysis completed successfully!"
257+
258+ // Create detailed statistics
259+ stats := progress.AnalysisStats {
260+ Files : len (parseResult .Files ),
261+ Nodes : len (graphResult .Nodes ),
262+ Relationships : len (graphResult .Relationships ),
263+ Interfaces : len (report .InterfaceImplementations ),
264+ CallChains : len (report .CallChains ),
265+ ProjectID : projectID .String (),
266+ }
277267
278- // Final summary
279- logger .Info ("✓ Analysis completed successfully" )
280- logger .Info ("Summary:" ,
281- "files" , len (parseResult .Files ),
282- "nodes" , len (graphResult .Nodes ),
283- "relationships" , len (graphResult .Relationships ),
284- "duration" , duration .Round (time .Millisecond ),
285- "project_id" , projectID )
268+ progressIndicator .SuccessWithStats (successMsg , stats )
286269
287270 return nil
288271}
289272
290273var initAnalyzeOnce sync.Once
291274
275+ func runParsingPhase (
276+ ctx context.Context ,
277+ projectPath string ,
278+ parserConfig * parser.Config ,
279+ progressIndicator * progress.AdaptiveProgress ,
280+ ) (* parser.ParseResult , error ) {
281+ progressIndicator .UpdatePhase ("Parsing" )
282+ progressIndicator .UpdateProgress (0.0 , "Initializing parser" )
283+
284+ parserService := parser .NewService (parserConfig )
285+ progressIndicator .UpdateProgress (0.1 , "Scanning project files" )
286+
287+ parseResult , err := parserService .ParseProject (ctx , projectPath , parserConfig )
288+ if err != nil {
289+ progressIndicator .Error (fmt .Errorf ("failed to parse project: %w" , err ))
290+ return nil , fmt .Errorf ("failed to parse project: %w" , err )
291+ }
292+ progressIndicator .UpdateProgress (0.25 , fmt .Sprintf ("Parsed %d files" , len (parseResult .Files )))
293+ return parseResult , nil
294+ }
295+
296+ func runAnalysisPhase (
297+ ctx context.Context ,
298+ projectID core.ID ,
299+ parseResult * parser.ParseResult ,
300+ analyzerConfig * analyzer.Config ,
301+ progressIndicator * progress.AdaptiveProgress ,
302+ ) (* analyzer.AnalysisReport , error ) {
303+ progressIndicator .UpdatePhase ("Analysis" )
304+ progressIndicator .UpdateProgress (0.3 , "Initializing analyzer" )
305+
306+ analyzerService := analyzer .NewAnalyzer (analyzerConfig )
307+ analysisInput := & analyzer.AnalysisInput {
308+ ProjectID : projectID .String (),
309+ Files : parseResult .Files ,
310+ }
311+ progressIndicator .UpdateProgress (0.4 , "Analyzing structure and dependencies" )
312+
313+ report , err := analyzerService .AnalyzeProject (ctx , analysisInput )
314+ if err != nil {
315+ progressIndicator .Error (fmt .Errorf ("failed to analyze project: %w" , err ))
316+ return nil , fmt .Errorf ("failed to analyze project: %w" , err )
317+ }
318+ progressIndicator .UpdateProgress (0.55 , fmt .Sprintf ("Found %d interfaces, %d call chains" ,
319+ len (report .InterfaceImplementations ), len (report .CallChains )))
320+ return report , nil
321+ }
322+
323+ func runGraphBuildingPhase (
324+ ctx context.Context ,
325+ projectID core.ID ,
326+ parseResult * parser.ParseResult ,
327+ report * analyzer.AnalysisReport ,
328+ progressIndicator * progress.AdaptiveProgress ,
329+ ) (* core.AnalysisResult , error ) {
330+ progressIndicator .UpdatePhase ("Graph Building" )
331+ progressIndicator .UpdateProgress (0.6 , "Building graph nodes and relationships" )
332+
333+ builder := graph .NewBuilder (nil ) // Use default config
334+ graphResult , err := builder .BuildFromAnalysis (ctx , projectID , parseResult , report )
335+ if err != nil {
336+ progressIndicator .Error (fmt .Errorf ("failed to build graph: %w" , err ))
337+ return nil , fmt .Errorf ("failed to build graph: %w" , err )
338+ }
339+ progressIndicator .UpdateProgress (0.75 , fmt .Sprintf ("Built %d nodes, %d relationships" ,
340+ len (graphResult .Nodes ), len (graphResult .Relationships )))
341+ return graphResult , nil
342+ }
343+
344+ func runStoragePhase (
345+ ctx context.Context ,
346+ graphResult * core.AnalysisResult ,
347+ neo4jConfig * infra.Neo4jConfig ,
348+ progressIndicator * progress.AdaptiveProgress ,
349+ ) error {
350+ progressIndicator .UpdatePhase ("Storage" )
351+ progressIndicator .UpdateProgress (0.8 , "Connecting to Neo4j database" )
352+
353+ repo , err := infra .NewNeo4jRepository (neo4jConfig )
354+ if err != nil {
355+ progressIndicator .Error (fmt .Errorf ("failed to create Neo4j repository: %w" , err ))
356+ return fmt .Errorf ("failed to create Neo4j repository: %w" , err )
357+ }
358+ defer repo .Close ()
359+
360+ progressIndicator .UpdateProgress (0.9 , "Storing nodes and relationships" )
361+ err = repo .StoreAnalysis (ctx , graphResult )
362+ if err != nil {
363+ progressIndicator .Error (fmt .Errorf ("failed to store analysis: %w" , err ))
364+ return fmt .Errorf ("failed to store analysis: %w" , err )
365+ }
366+ return nil
367+ }
368+
292369// InitAnalyzeCommand registers the analyze command
293370func InitAnalyzeCommand () {
294371 initAnalyzeOnce .Do (func () {
0 commit comments