mirror of
https://github.com/Dvorinka/Devour.git
synced 2026-06-03 20:13:03 +00:00
131 lines
3.2 KiB
Go
131 lines
3.2 KiB
Go
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 <source>' to index documentation")
|
|
fmt.Println(" 2. Run 'devour query \"<topic>\"' to search indexed docs")
|
|
} else {
|
|
fmt.Println(" 1. Run 'devour query \"<topic>\"' for local docs search")
|
|
fmt.Println(" 2. Run 'devour ask --lang <lang> \"<question>\"' 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]
|
|
}
|
|
}
|
|
}
|