package cmd import ( "encoding/json" "fmt" "os" "strings" "github.com/david/knox/internal/db" "github.com/david/knox/internal/index" "github.com/spf13/cobra" ) func paginate(entries []db.Entry, page, limit int) []db.Entry { start := (page - 1) * limit if start >= len(entries) { return nil } end := start + limit if end > len(entries) { end = len(entries) } return entries[start:end] } func NewStatusCmd(kdb *db.KnoxDB) *cobra.Command { var jsonOut bool cmd := &cobra.Command{ Use: "status", Short: "Show knowledge index statistics", RunE: func(c *cobra.Command, args []string) error { stats, err := kdb.Stats() if err != nil { return err } if jsonOut { b, _ := json.MarshalIndent(stats, "", " ") fmt.Println(string(b)) return nil } fmt.Println("Knox Knowledge Index") fmt.Println("====================") for k, v := range stats { switch val := v.(type) { case int: fmt.Printf(" %-24s %d\n", k+":", val) case string: fmt.Printf(" %-24s %s\n", k+":", val) default: fmt.Printf(" %-24s %v\n", k+":", val) } } return nil }, } cmd.Flags().BoolVar(&jsonOut, "json", false, "Output as JSON") return cmd } func NewQueryCmd(kdb *db.KnoxDB) *cobra.Command { var page, limit int cmd := &cobra.Command{ Use: "query [search text]", Short: "Search the knowledge index", Long: "Search the knowledge index. Reads from stdin pipe if no positional arg given.", RunE: func(c *cobra.Command, args []string) error { query := "" if len(args) > 0 { query = args[0] } else { stdin, _ := os.ReadFile(os.Stdin.Name()) query = strings.TrimSpace(string(stdin)) } if query == "" { return fmt.Errorf("search query required (positional arg or stdin pipe)") } results, err := kdb.Search(query, limit) if err != nil { return err } results = paginate(results, page, limit) if len(results) == 0 { fmt.Println("No results found.") return nil } fmt.Printf("Found %d results for %q (page %d, %d per page):\n\n", len(results), query, page, limit) for _, r := range results { fmt.Printf(" %-8s %-30s [%s] %s\n", shortFP(r.Fingerprint), truncateStr(r.Title, 30), r.Project, r.SourceID) if r.Summary != "" { fmt.Printf(" %-8s %s\n", "", truncateStr(r.Summary, 80)) } fmt.Println() } return nil }, } cmd.Flags().IntVarP(&page, "page", "p", 1, "Page number (1-based)") cmd.Flags().IntVarP(&limit, "limit", "l", 20, "Results per page") return cmd } func NewRecentCmd(kdb *db.KnoxDB) *cobra.Command { var page, limit int cmd := &cobra.Command{ Use: "recent", Short: "Show recent knowledge entries", RunE: func(c *cobra.Command, args []string) error { all, err := kdb.RecentEntries(limit * 10) if err != nil { return err } entries := paginate(all, page, limit) if len(entries) == 0 { fmt.Println("No entries yet. Run `knox watch` to start ingesting.") return nil } fmt.Printf("Recent %d entries (page %d, %d per page):\n\n", len(entries), page, limit) for _, e := range entries { fmt.Printf(" %-8s %-30s [%s] %s\n", shortFP(e.Fingerprint), truncateStr(e.Title, 30), e.Project, e.SourceID) } return nil }, } cmd.Flags().IntVarP(&page, "page", "p", 1, "Page number (1-based)") cmd.Flags().IntVarP(&limit, "limit", "l", 20, "Results per page") return cmd } func NewReflectCmd(kdb *db.KnoxDB) *cobra.Command { return &cobra.Command{ Use: "reflect", Short: "Process pending sessions for reflection", Long: "Scans for sessions not yet indexed, runs reflection, and reports findings.", RunE: func(c *cobra.Command, args []string) error { reflector := index.NewReflector(kdb) results, err := reflector.Reflect() if err != nil { return err } if len(results) == 0 { fmt.Println("All caught up — no pending sessions to reflect on.") return nil } fmt.Printf("Reflected %d sessions:\n\n", len(results)) fmt.Print(reflector.FormatResult(results)) gaps, _ := reflector.Gaps() if len(gaps) > 0 { fmt.Printf("\nProjects with indexed knowledge:\n") for _, g := range gaps { fmt.Printf(" %-20s %d entries\n", g.Project, g.TotalEntries) } } return nil }, } } func getHomeDir() (string, error) { return os.UserHomeDir() }