feat(cli): add nib add + nib ls commands

Default command delegation: `nib "..."` routes to `nib add`. Capture
bar parses grammar, creates entities. `nib ls` lists with date
grouping, tag filter, 48h default window. Display glyphs for all
entity types.
This commit is contained in:
2026-05-14 11:17:27 -04:00
parent 51cbf86d77
commit a6fda5d1ee
6 changed files with 312 additions and 2 deletions
+131
View File
@@ -0,0 +1,131 @@
package cmd
import (
"fmt"
"strings"
"time"
"github.com/lerko/nib/internal/db"
"github.com/lerko/nib/internal/display"
"github.com/spf13/cobra"
)
var (
lsTag string
lsDate string
lsAll bool
)
var lsCmd = &cobra.Command{
Use: "ls",
Short: "list entities in stream order",
RunE: runLs,
}
func init() {
lsCmd.Flags().StringVar(&lsTag, "tag", "", "filter by tag")
lsCmd.Flags().StringVar(&lsDate, "date", "", "filter by date (YYYY-MM-DD)")
lsCmd.Flags().BoolVar(&lsAll, "all", false, "include deleted entities")
}
func runLs(_ *cobra.Command, _ []string) error {
store, err := openStore()
if err != nil {
return err
}
defer store.Close()
p := db.DefaultListParams()
p.IncludeDeleted = lsAll
if lsTag != "" {
p.Tag = &lsTag
}
if lsDate != "" {
p.Date = &lsDate
} else {
since := time.Now().UTC().Add(-48 * time.Hour)
p.Since = &since
}
entities, err := store.List(p)
if err != nil {
return err
}
if len(entities) == 0 {
return nil
}
groups := groupByDate(entities)
for _, g := range groups {
fmt.Printf("── %s ──\n", g.label)
for _, e := range g.entities {
printEntity(e)
}
fmt.Println()
}
return nil
}
type dateGroup struct {
label string
entities []*db.Entity
}
func groupByDate(entities []*db.Entity) []dateGroup {
var groups []dateGroup
var current *dateGroup
for _, e := range entities {
label := formatDateLabel(e.CreatedAt)
if current == nil || current.label != label {
if current != nil {
groups = append(groups, *current)
}
current = &dateGroup{label: label}
}
current.entities = append(current.entities, e)
}
if current != nil {
groups = append(groups, *current)
}
return groups
}
func formatDateLabel(t time.Time) string {
return strings.ToLower(t.Format("Jan 2"))
}
func printEntity(e *db.Entity) {
glyph := display.DisplayGlyph(e.Glyph, e.CardType)
shortID := display.FormatID(e.ID)
var line strings.Builder
fmt.Fprintf(&line, "%s %-40s", glyph, e.Body)
if e.TimeAnchor != nil {
fmt.Fprintf(&line, " @%-5s", *e.TimeAnchor)
} else {
line.WriteString(" ")
}
var tagStr string
for _, tag := range e.Tags {
tagStr += " #" + tag
}
if tagStr != "" {
fmt.Fprintf(&line, " %-16s", strings.TrimSpace(tagStr))
} else {
line.WriteString(" ")
}
fmt.Fprintf(&line, " %s", shortID)
if e.UseCount > 0 {
fmt.Fprintf(&line, " (%d×)", e.UseCount)
}
fmt.Println(line.String())
}