feat(lss): adds --no-header and --short flags, and lowercases all flag descriptions

This commit is contained in:
Lewis Wynne 2026-02-11 19:29:14 +00:00
parent 4e5064d07a
commit 15c1d6733c
22 changed files with 161 additions and 84 deletions

View file

@ -292,8 +292,14 @@ pda set alice@birthdays 11/11/1998
# See which stores have contents.
pda list-stores
# @default
# Keys Size Store
# 2 1.8k @birthdays
# 12 4.2k @default
# Just the names.
pda list-stores --short
# @birthdays
# @default
# Check out a specific store.
pda ls @birthdays --no-header --no-ttl

View file

@ -90,7 +90,7 @@ func executeDeletion(path string) error {
}
func init() {
delStoreCmd.Flags().BoolP("interactive", "i", false, "Prompt yes/no for each deletion")
delStoreCmd.Flags().BoolP("yes", "y", false, "Skip all confirmation prompts")
delStoreCmd.Flags().BoolP("interactive", "i", false, "prompt yes/no for each deletion")
delStoreCmd.Flags().BoolP("yes", "y", false, "skip all confirmation prompts")
rootCmd.AddCommand(delStoreCmd)
}

View file

@ -123,9 +123,9 @@ func del(cmd *cobra.Command, args []string) error {
}
func init() {
delCmd.Flags().BoolP("interactive", "i", false, "Prompt yes/no for each deletion")
delCmd.Flags().BoolP("yes", "y", false, "Skip all confirmation prompts")
delCmd.Flags().StringSliceP("key", "k", nil, "Delete keys matching glob pattern (repeatable)")
delCmd.Flags().BoolP("interactive", "i", false, "prompt yes/no for each deletion")
delCmd.Flags().BoolP("yes", "y", false, "skip all confirmation prompts")
delCmd.Flags().StringSliceP("key", "k", nil, "delete keys matching glob pattern (repeatable)")
rootCmd.AddCommand(delCmd)
}

View file

@ -39,7 +39,7 @@ var exportCmd = &cobra.Command{
}
func init() {
exportCmd.Flags().StringSliceP("key", "k", nil, "Filter keys with glob pattern (repeatable)")
exportCmd.Flags().StringSliceP("value", "v", nil, "Filter values with regex pattern (repeatable)")
exportCmd.Flags().StringSliceP("key", "k", nil, "filter keys with glob pattern (repeatable)")
exportCmd.Flags().StringSliceP("value", "v", nil, "filter values with glob pattern (repeatable)")
rootCmd.AddCommand(exportCmd)
}

View file

@ -70,8 +70,8 @@ func identityRun(cmd *cobra.Command, args []string) error {
}
func init() {
identityCmd.Flags().Bool("new", false, "Generate a new identity (errors if one already exists)")
identityCmd.Flags().Bool("path", false, "Print only the identity file path")
identityCmd.Flags().Bool("new", false, "generate a new identity (errors if one already exists)")
identityCmd.Flags().Bool("path", false, "print only the identity file path")
identityCmd.MarkFlagsMutuallyExclusive("new", "path")
rootCmd.AddCommand(identityCmd)
}

View file

@ -40,7 +40,7 @@ var initCmd = &cobra.Command{
}
func init() {
initCmd.Flags().Bool("clean", false, "Remove .git from stores directory before initialising")
initCmd.Flags().Bool("clean", false, "remove .git from stores directory before initialising")
rootCmd.AddCommand(initCmd)
}

View file

@ -24,6 +24,8 @@ package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
@ -43,12 +45,77 @@ func listStores(cmd *cobra.Command, args []string) error {
if err != nil {
return fmt.Errorf("cannot list stores: %v", err)
}
short, err := cmd.Flags().GetBool("short")
if err != nil {
return fmt.Errorf("cannot list stores: %v", err)
}
if short {
for _, db := range dbs {
fmt.Println("@" + db)
}
return nil
}
type storeInfo struct {
name string
keys int
size string
}
rows := make([]storeInfo, 0, len(dbs))
nameW, keysW, sizeW := len("Store"), len("Keys"), len("Size")
for _, db := range dbs {
fmt.Println("@" + db)
p, err := store.storePath(db)
if err != nil {
return fmt.Errorf("cannot list stores: %v", err)
}
fi, err := os.Stat(p)
if err != nil {
return fmt.Errorf("cannot list stores: %v", err)
}
entries, err := readStoreFile(p, nil)
if err != nil {
return fmt.Errorf("cannot list stores: %v", err)
}
name := "@" + db
keysStr := fmt.Sprintf("%d", len(entries))
sizeStr := formatSize(int(fi.Size()))
if len(name) > nameW {
nameW = len(name)
}
if len(keysStr) > keysW {
keysW = len(keysStr)
}
if len(sizeStr) > sizeW {
sizeW = len(sizeStr)
}
rows = append(rows, storeInfo{name: name, keys: len(entries), size: sizeStr})
}
underline := func(s string) string {
if stdoutIsTerminal() {
return "\033[4m" + s + "\033[0m"
}
return s
}
noHeader, _ := cmd.Flags().GetBool("no-header")
if !noHeader {
fmt.Printf("%*s%s %*s%s %s\n",
keysW-len("Keys"), "", underline("Keys"),
sizeW-len("Size"), "", underline("Size"),
underline("Store"))
}
for _, r := range rows {
fmt.Printf("%*d %*s %s\n", keysW, r.keys, sizeW, r.size, r.name)
}
return nil
}
func init() {
listStoresCmd.Flags().Bool("short", false, "only print store names")
listStoresCmd.Flags().Bool("no-header", false, "suppress the header row")
rootCmd.AddCommand(listStoresCmd)
}

View file

@ -486,7 +486,7 @@ func init() {
listCmd.Flags().BoolVarP(&listFull, "full", "f", false, "show full values without truncation")
listCmd.Flags().BoolVar(&listNoHeader, "no-header", false, "suppress the header row")
listCmd.Flags().VarP(&listFormat, "format", "o", "output format (table|tsv|csv|markdown|html|ndjson)")
listCmd.Flags().StringSliceP("key", "k", nil, "Filter keys with glob pattern (repeatable)")
listCmd.Flags().StringSliceP("value", "v", nil, "Filter values with regex pattern (repeatable)")
listCmd.Flags().StringSliceP("key", "k", nil, "filter keys with glob pattern (repeatable)")
listCmd.Flags().StringSliceP("value", "v", nil, "filter values with glob pattern (repeatable)")
rootCmd.AddCommand(listCmd)
}

View file

@ -121,9 +121,9 @@ func mvStore(cmd *cobra.Command, args []string) error {
}
func init() {
mvStoreCmd.Flags().Bool("copy", false, "Copy instead of move (keeps source)")
mvStoreCmd.Flags().BoolP("interactive", "i", false, "Prompt before overwriting destination")
mvStoreCmd.Flags().BoolP("yes", "y", false, "Skip all confirmation prompts")
mvStoreCmd.Flags().Bool("safe", false, "Do not overwrite if the destination store already exists")
mvStoreCmd.Flags().Bool("copy", false, "copy instead of move (keeps source)")
mvStoreCmd.Flags().BoolP("interactive", "i", false, "prompt before overwriting destination")
mvStoreCmd.Flags().BoolP("yes", "y", false, "skip all confirmation prompts")
mvStoreCmd.Flags().Bool("safe", false, "do not overwrite if the destination store already exists")
rootCmd.AddCommand(mvStoreCmd)
}

View file

@ -191,13 +191,13 @@ func mvImpl(cmd *cobra.Command, args []string, keepSource bool) error {
}
func init() {
mvCmd.Flags().Bool("copy", false, "Copy instead of move (keeps source)")
mvCmd.Flags().BoolP("interactive", "i", false, "Prompt before overwriting destination")
mvCmd.Flags().BoolP("yes", "y", false, "Skip all confirmation prompts")
mvCmd.Flags().Bool("safe", false, "Do not overwrite if the destination already exists")
mvCmd.Flags().Bool("copy", false, "copy instead of move (keeps source)")
mvCmd.Flags().BoolP("interactive", "i", false, "prompt before overwriting destination")
mvCmd.Flags().BoolP("yes", "y", false, "skip all confirmation prompts")
mvCmd.Flags().Bool("safe", false, "do not overwrite if the destination already exists")
rootCmd.AddCommand(mvCmd)
cpCmd.Flags().BoolP("interactive", "i", false, "Prompt before overwriting destination")
cpCmd.Flags().BoolP("yes", "y", false, "Skip all confirmation prompts")
cpCmd.Flags().Bool("safe", false, "Do not overwrite if the destination already exists")
cpCmd.Flags().BoolP("interactive", "i", false, "prompt before overwriting destination")
cpCmd.Flags().BoolP("yes", "y", false, "skip all confirmation prompts")
cpCmd.Flags().Bool("safe", false, "do not overwrite if the destination already exists")
rootCmd.AddCommand(cpCmd)
}

View file

@ -226,9 +226,9 @@ func restoreEntries(decoder *json.Decoder, storePath string, opts restoreOpts) (
}
func init() {
restoreCmd.Flags().StringP("file", "f", "", "Path to an NDJSON dump (defaults to stdin)")
restoreCmd.Flags().StringSliceP("key", "k", nil, "Restore keys matching glob pattern (repeatable)")
restoreCmd.Flags().BoolP("interactive", "i", false, "Prompt before overwriting existing keys")
restoreCmd.Flags().Bool("drop", false, "Drop existing entries before restoring (full replace)")
restoreCmd.Flags().StringP("file", "f", "", "path to an NDJSON dump (defaults to stdin)")
restoreCmd.Flags().StringSliceP("key", "k", nil, "restore keys matching glob pattern (repeatable)")
restoreCmd.Flags().BoolP("interactive", "i", false, "prompt before overwriting existing keys")
restoreCmd.Flags().Bool("drop", false, "drop existing entries before restoring (full replace)")
rootCmd.AddCommand(restoreCmd)
}

View file

@ -180,9 +180,9 @@ func set(cmd *cobra.Command, args []string) error {
func init() {
rootCmd.AddCommand(setCmd)
setCmd.Flags().DurationP("ttl", "t", 0, "Expire the key after the provided duration (e.g. 24h, 30m)")
setCmd.Flags().BoolP("interactive", "i", false, "Prompt before overwriting an existing key")
setCmd.Flags().BoolP("encrypt", "e", false, "Encrypt the value at rest using age")
setCmd.Flags().Bool("safe", false, "Do not overwrite if the key already exists")
setCmd.Flags().StringP("file", "f", "", "Read value from a file")
setCmd.Flags().DurationP("ttl", "t", 0, "expire the key after the provided duration (e.g. 24h, 30m)")
setCmd.Flags().BoolP("interactive", "i", false, "prompt before overwriting an existing key")
setCmd.Flags().BoolP("encrypt", "e", false, "encrypt the value at rest using age")
setCmd.Flags().Bool("safe", false, "do not overwrite if the key already exists")
setCmd.Flags().StringP("file", "f", "", "read value from a file")
}

View file

@ -86,19 +86,19 @@ func (s *Store) formatBytes(base64Flag bool, v []byte) string {
func formatSize(n int) string {
const (
kb = 1024
mb = 1024 * kb
gb = 1024 * mb
ki = 1024
mi = 1024 * ki
gi = 1024 * mi
)
switch {
case n < kb:
return fmt.Sprintf("%d B", n)
case n < mb:
return fmt.Sprintf("%.1f KB", float64(n)/float64(kb))
case n < gb:
return fmt.Sprintf("%.1f MB", float64(n)/float64(mb))
case n < ki:
return fmt.Sprintf("%d", n)
case n < mi:
return fmt.Sprintf("%.1fk", float64(n)/float64(ki))
case n < gi:
return fmt.Sprintf("%.1fM", float64(n)/float64(mi))
default:
return fmt.Sprintf("%.1f GB", float64(n)/float64(gb))
return fmt.Sprintf("%.1fG", float64(n)/float64(gi))
}
}

View file

@ -40,7 +40,7 @@ var syncCmd = &cobra.Command{
}
func init() {
syncCmd.Flags().StringP("message", "m", "", "Custom commit message (defaults to timestamp)")
syncCmd.Flags().StringP("message", "m", "", "custom commit message (defaults to timestamp)")
rootCmd.AddCommand(syncCmd)
}

View file

@ -45,6 +45,6 @@ var versionCmd = &cobra.Command{
}
func init() {
versionCmd.Flags().Bool("short", false, "Print only the version string")
versionCmd.Flags().Bool("short", false, "print only the version string")
rootCmd.AddCommand(versionCmd)
}

View file

@ -7,8 +7,8 @@ Usage:
Flags:
-h, --help help for export
-k, --key strings Filter keys with glob pattern (repeatable)
-v, --value strings Filter values with regex pattern (repeatable)
-k, --key strings filter keys with glob pattern (repeatable)
-v, --value strings filter values with glob pattern (repeatable)
Export store as NDJSON (alias for list --format ndjson)
Usage:
@ -16,5 +16,5 @@ Usage:
Flags:
-h, --help help for export
-k, --key strings Filter keys with glob pattern (repeatable)
-v, --value strings Filter values with regex pattern (repeatable)
-k, --key strings filter keys with glob pattern (repeatable)
-v, --value strings filter values with glob pattern (repeatable)

View file

@ -6,19 +6,19 @@ Usage:
pda import [STORE] [flags]
Flags:
--drop Drop existing entries before restoring (full replace)
-f, --file string Path to an NDJSON dump (defaults to stdin)
--drop drop existing entries before restoring (full replace)
-f, --file string path to an NDJSON dump (defaults to stdin)
-h, --help help for import
-i, --interactive Prompt before overwriting existing keys
-k, --key strings Restore keys matching glob pattern (repeatable)
-i, --interactive prompt before overwriting existing keys
-k, --key strings restore keys matching glob pattern (repeatable)
Restore key/value pairs from an NDJSON dump
Usage:
pda import [STORE] [flags]
Flags:
--drop Drop existing entries before restoring (full replace)
-f, --file string Path to an NDJSON dump (defaults to stdin)
--drop drop existing entries before restoring (full replace)
-f, --file string path to an NDJSON dump (defaults to stdin)
-h, --help help for import
-i, --interactive Prompt before overwriting existing keys
-k, --key strings Restore keys matching glob pattern (repeatable)
-i, --interactive prompt before overwriting existing keys
-k, --key strings restore keys matching glob pattern (repeatable)

View file

@ -9,7 +9,9 @@ Aliases:
list-stores, lss
Flags:
-h, --help help for list-stores
-h, --help help for list-stores
--no-header suppress the header row
--short only print store names
List all stores
Usage:
@ -19,4 +21,6 @@ Aliases:
list-stores, lss
Flags:
-h, --help help for list-stores
-h, --help help for list-stores
--no-header suppress the header row
--short only print store names

View file

@ -14,12 +14,12 @@ Flags:
-o, --format format output format (table|tsv|csv|markdown|html|ndjson) (default table)
-f, --full show full values without truncation
-h, --help help for list
-k, --key strings Filter keys with glob pattern (repeatable)
-k, --key strings filter keys with glob pattern (repeatable)
--no-header suppress the header row
--no-keys suppress the key column
--no-ttl suppress the TTL column
--no-values suppress the value column
-v, --value strings Filter values with regex pattern (repeatable)
-v, --value strings filter values with glob pattern (repeatable)
List the contents of a store
Usage:
@ -34,9 +34,9 @@ Flags:
-o, --format format output format (table|tsv|csv|markdown|html|ndjson) (default table)
-f, --full show full values without truncation
-h, --help help for list
-k, --key strings Filter keys with glob pattern (repeatable)
-k, --key strings filter keys with glob pattern (repeatable)
--no-header suppress the header row
--no-keys suppress the key column
--no-ttl suppress the TTL column
--no-values suppress the value column
-v, --value strings Filter values with regex pattern (repeatable)
-v, --value strings filter values with glob pattern (repeatable)

View file

@ -10,8 +10,8 @@ Aliases:
Flags:
-h, --help help for remove-store
-i, --interactive Prompt yes/no for each deletion
-y, --yes Skip all confirmation prompts
-i, --interactive prompt yes/no for each deletion
-y, --yes skip all confirmation prompts
Delete a store
Usage:
@ -22,5 +22,5 @@ Aliases:
Flags:
-h, --help help for remove-store
-i, --interactive Prompt yes/no for each deletion
-y, --yes Skip all confirmation prompts
-i, --interactive prompt yes/no for each deletion
-y, --yes skip all confirmation prompts

View file

@ -10,9 +10,9 @@ Aliases:
Flags:
-h, --help help for remove
-i, --interactive Prompt yes/no for each deletion
-k, --key strings Delete keys matching glob pattern (repeatable)
-y, --yes Skip all confirmation prompts
-i, --interactive prompt yes/no for each deletion
-k, --key strings delete keys matching glob pattern (repeatable)
-y, --yes skip all confirmation prompts
Delete one or more keys
Usage:
@ -23,6 +23,6 @@ Aliases:
Flags:
-h, --help help for remove
-i, --interactive Prompt yes/no for each deletion
-k, --key strings Delete keys matching glob pattern (repeatable)
-y, --yes Skip all confirmation prompts
-i, --interactive prompt yes/no for each deletion
-k, --key strings delete keys matching glob pattern (repeatable)
-y, --yes skip all confirmation prompts

20
testdata/help-set.ct vendored
View file

@ -21,12 +21,12 @@ Aliases:
set, s
Flags:
-e, --encrypt Encrypt the value at rest using age
-f, --file string Read value from a file
-e, --encrypt encrypt the value at rest using age
-f, --file string read value from a file
-h, --help help for set
-i, --interactive Prompt before overwriting an existing key
--safe Do not overwrite if the key already exists
-t, --ttl duration Expire the key after the provided duration (e.g. 24h, 30m)
-i, --interactive prompt before overwriting an existing key
--safe do not overwrite if the key already exists
-t, --ttl duration expire the key after the provided duration (e.g. 24h, 30m)
Set a key to a given value or stdin. Optionally specify a store.
Pass --encrypt to encrypt the value at rest using age. An identity file
@ -48,9 +48,9 @@ Aliases:
set, s
Flags:
-e, --encrypt Encrypt the value at rest using age
-f, --file string Read value from a file
-e, --encrypt encrypt the value at rest using age
-f, --file string read value from a file
-h, --help help for set
-i, --interactive Prompt before overwriting an existing key
--safe Do not overwrite if the key already exists
-t, --ttl duration Expire the key after the provided duration (e.g. 24h, 30m)
-i, --interactive prompt before overwriting an existing key
--safe do not overwrite if the key already exists
-t, --ttl duration expire the key after the provided duration (e.g. 24h, 30m)