package cmd import ( "context" "fmt" "time" "github.com/spf13/cobra" "github.com/yourorg/devour/internal/projectstate" "github.com/yourorg/devour/internal/search" "github.com/yourorg/devour/internal/ui" ) var statusCmd = &cobra.Command{ Use: "status", Short: "Show index status and statistics", Long: `Display the current status of the Devour index. Shows: - Index health - Document count - Last update time - Source status - Storage usage`, RunE: runStatus, } func runStatus(cmd *cobra.Command, args []string) error { cfg, err := loadAppConfig() if err != nil { return err } // Print the small character mascot ui.PrintCharacterSmall() fmt.Println() ui.PrintHeader("Devour Status") docsStats, err := projectstate.CollectDocsStats(cfg.Storage.DocsDir) if err != nil { return err } engine := search.NewEngine(cfg) indexStats, indexErr := engine.EnsureIndexed(context.Background()) indexHealth := "✓ Healthy" if indexErr != nil { if docsStats.DocumentCount == 0 { indexHealth = "⚠️ No docs indexed yet" } else { indexHealth = "✗ Index error" } } lastUpdated := "Never" if !docsStats.LastUpdated.IsZero() { lastUpdated = docsStats.LastUpdated.Format(time.RFC3339) } chunks := 0 if indexStats != nil { chunks = indexStats.Documents } ui.PrintKeyValue("Index Health", indexHealth) ui.PrintKeyValue("Documents", fmt.Sprintf("%d indexed", docsStats.DocumentCount)) ui.PrintKeyValue("Chunks", fmt.Sprintf("%d total", chunks)) ui.PrintKeyValue("Vector Dimension", fmt.Sprintf("%d", cfg.Embeddings.Dimensions)) ui.PrintKeyValue("Last Updated", lastUpdated) ui.PrintKeyValue("Storage Used", humanSize(docsStats.StorageBytes)) fmt.Println() ui.PrintSection("Sources") state, stateErr := projectstate.LoadSourceState(cfg.Storage.MetadataDir) if stateErr != nil || len(state.Sources) == 0 { ui.PrintInfo(" None tracked yet") } else { keys := make([]string, 0, len(state.Sources)) for k := range state.Sources { keys = append(keys, k) } sortStrings(keys) for _, k := range keys { s := state.Sources[k] last := "never" if !s.LastSync.IsZero() { last = s.LastSync.Format("2006-01-02 15:04:05") } fmt.Printf(" • %s (%s): %d docs, last sync %s\n", s.Name, s.Type, s.DocCount, last) } } fmt.Println() ui.PrintSection("Next Steps") if docsStats.DocumentCount == 0 { fmt.Println(" 1. Run 'devour scrape ' to index documentation") fmt.Println(" 2. Run 'devour query \"\"' to search indexed docs") } else { fmt.Println(" 1. Run 'devour query \"\"' for local docs search") fmt.Println(" 2. Run 'devour ask --lang \"\"' for structured answers") } if indexErr != nil { fmt.Printf(" ⚠️ Index note: %v\n", indexErr) } // Show when check happened fmt.Printf("\nStatus as of: %s\n", time.Now().Format(time.RFC3339)) return nil } func humanSize(b int64) string { const mb = 1024 * 1024 if b < mb { return fmt.Sprintf("%d KB", b/1024) } return fmt.Sprintf("%.2f MB", float64(b)/float64(mb)) } func sortStrings(values []string) { if len(values) < 2 { return } for i := 1; i < len(values); i++ { for j := i; j > 0 && values[j] < values[j-1]; j-- { values[j], values[j-1] = values[j-1], values[j] } } }