feat: parse all Go CLIs with flaggy; dump bash complete. (#36)
Tests / Test (push) Skipped
Tests / OCR (tesseract fixture) (push) Skipped
Tests / Release (semver) (push) Skipped

stdlib flag dropped --hop after the query. One wrapper in internal/cli,
source <(./bin/cli/complete.go bash). Gitea #34.
This commit is contained in:
2026-08-14 12:12:38 +01:00
committed by GitHub
co-authored by GitHub
parent 0065655e03
commit 0c4bb87001
34 changed files with 1005 additions and 420 deletions
+15 -21
View File
@@ -3,21 +3,22 @@ package chats
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
cliparse "github.com/eSlider/2dph/internal/cli"
)
type ooContact struct {
ID int `json:"id"`
ID int `json:"id"`
DisplayName string `json:"displayName"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
About string `json:"about"`
CommonData []struct {
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
About string `json:"about"`
CommonData []struct {
InfoType int `json:"infoType"`
Data string `json:"data"`
Category string `json:"categoryName"`
@@ -25,16 +26,9 @@ type ooContact struct {
}
func RunApply(args []string) int {
fs := flag.NewFlagSet("chats apply", flag.ContinueOnError)
dryRun := fs.Bool("dry-run", false, "show what would be done without writing")
help := fs.Bool("help", false, "")
fs.SetOutput(os.Stderr)
if err := fs.Parse(args); err != nil {
return 2
}
if *help {
fmt.Fprintln(os.Stderr, "usage: chats apply [--dry-run]")
return 0
dryRun, err := parseApplyFlags(args)
if err != nil {
return cliparse.Fail(err)
}
ooCLI := findOO()
@@ -60,10 +54,10 @@ func RunApply(args []string) int {
emailFacts = dedupeFacts(emailFacts)
type resolvedFact struct {
Fact ExtractedFact
OoID int
OoName string
Action string // "info-add" or "persons-create"
Fact ExtractedFact
OoID int
OoName string
Action string // "info-add" or "persons-create"
}
var resolved []resolvedFact
@@ -128,7 +122,7 @@ func RunApply(args []string) int {
fmt.Printf("\nchats apply: %d actions to apply\n", len(resolved))
if *dryRun {
if dryRun {
for _, r := range resolved {
switch r.Action {
case "info-add":
+74
View File
@@ -0,0 +1,74 @@
package chats
import (
cliparse "github.com/eSlider/2dph/internal/cli"
"github.com/integrii/flaggy"
)
type syncTelegramFlags struct {
Limit int
Phone string
}
type syncLinkedInFlags struct {
Limit int
Refresh bool
}
func SyncParser() *flaggy.Parser {
p := cliparse.New("chats-sync")
p.Description = "download chats to var/chats"
tg := flaggy.NewSubcommand("telegram")
li := flaggy.NewSubcommand("linkedin")
var limit int
var phone string
var refresh bool
tg.Int(&limit, "", "limit", "max messages per chat")
tg.String(&phone, "", "phone", "phone (default TELEGRAM_PHONE)")
li.Int(&limit, "", "limit", "max messages per conversation")
li.Bool(&refresh, "", "refresh", "refresh webtop session")
p.AttachSubcommand(tg, 1)
p.AttachSubcommand(li, 1)
return p
}
func ImportParser() *flaggy.Parser {
return cliparse.New("chats-import")
}
func FactsParser() *flaggy.Parser {
return cliparse.New("chats-facts")
}
func ApplyParser() *flaggy.Parser {
p := cliparse.New("chats-apply")
dry := false
p.Bool(&dry, "", "dry-run", "show without writing")
return p
}
func parseTelegramFlags(args []string) (syncTelegramFlags, error) {
var f syncTelegramFlags
p := cliparse.New("chats-sync-telegram")
p.Int(&f.Limit, "", "limit", "max messages per chat")
p.String(&f.Phone, "", "phone", "phone (default TELEGRAM_PHONE)")
return f, cliparse.Parse(p, args)
}
func parseLinkedInFlags(args []string) (syncLinkedInFlags, error) {
var f syncLinkedInFlags
p := cliparse.New("chats-sync-linkedin")
p.Int(&f.Limit, "", "limit", "max messages per conversation")
p.Bool(&f.Refresh, "", "refresh", "refresh webtop session")
return f, cliparse.Parse(p, args)
}
func parseApplyFlags(args []string) (dryRun bool, err error) {
p := cliparse.New("chats-apply")
p.Bool(&dryRun, "", "dry-run", "show without writing")
return dryRun, cliparse.Parse(p, args)
}
func parseNoFlags(name string, args []string) error {
return cliparse.Parse(cliparse.New(name), args)
}
+4 -10
View File
@@ -3,12 +3,13 @@ package chats
import (
"bufio"
"encoding/json"
"flag"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
cliparse "github.com/eSlider/2dph/internal/cli"
)
var (
@@ -76,15 +77,8 @@ type ExtractedFact struct {
}
func RunFacts(args []string) int {
fs := flag.NewFlagSet("chats facts", flag.ContinueOnError)
help := fs.Bool("help", false, "")
fs.SetOutput(os.Stderr)
if err := fs.Parse(args); err != nil {
return 2
}
if *help {
fmt.Fprintln(os.Stderr, "usage: chats facts")
return 0
if err := parseNoFlags("chats-facts", args); err != nil {
return cliparse.Fail(err)
}
root := Dir()
+4 -10
View File
@@ -4,25 +4,19 @@ import (
"bufio"
"bytes"
"encoding/json"
"flag"
"fmt"
"html"
"os"
"path/filepath"
"sort"
"strings"
cliparse "github.com/eSlider/2dph/internal/cli"
)
func RunImport(args []string) int {
fs := flag.NewFlagSet("chats import", flag.ContinueOnError)
help := fs.Bool("help", false, "")
fs.SetOutput(os.Stderr)
if err := fs.Parse(args); err != nil {
return 2
}
if *help {
fmt.Fprintln(os.Stderr, "usage: chats import")
return 0
if err := parseNoFlags("chats-import", args); err != nil {
return cliparse.Fail(err)
}
root := Dir()
+9 -14
View File
@@ -2,12 +2,13 @@ package chats
import (
"context"
"flag"
"fmt"
"os"
"os/exec"
"path/filepath"
"time"
cliparse "github.com/eSlider/2dph/internal/cli"
)
func checkLinkedInSession(userDataDir string) (bool, error) {
@@ -29,18 +30,12 @@ func checkLinkedInSession(userDataDir string) (bool, error) {
}
func RunSyncLinkedIn(args []string) int {
fs := flag.NewFlagSet("chats sync linkedin", flag.ContinueOnError)
limit := fs.Int("limit", 0, "max messages per conversation (0 = all)")
refresh := fs.Bool("refresh", false, "refresh session from live webtop browser before sync")
help := fs.Bool("help", false, "")
fs.SetOutput(os.Stderr)
if err := fs.Parse(args); err != nil {
return 2
}
if *help {
fmt.Fprintln(os.Stderr, "usage: chats sync linkedin [--limit N] [--refresh]")
return 0
f, err := parseLinkedInFlags(args)
if err != nil {
return cliparse.Fail(err)
}
limit := f.Limit
refresh := f.Refresh
userDataDir := envVar("LINKEDIN_USER_DATA_DIR", "")
if userDataDir == "" {
@@ -48,7 +43,7 @@ func RunSyncLinkedIn(args []string) int {
userDataDir = home + "/.linkedin-mcp/profile"
}
if *refresh {
if refresh {
if code := refreshLinkedInSession(userDataDir); code != 0 {
return code
}
@@ -72,7 +67,7 @@ func RunSyncLinkedIn(args []string) int {
defer cancel()
start := time.Now()
if err := src.Sync(ctx, Dir(), *limit); err != nil {
if err := src.Sync(ctx, Dir(), limit); err != nil {
fmt.Fprintf(os.Stderr, "chats sync linkedin: %v\n", err)
return 1
}
+9 -14
View File
@@ -2,33 +2,28 @@ package chats
import (
"context"
"flag"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"time"
cliparse "github.com/eSlider/2dph/internal/cli"
)
func RunSyncTelegram(args []string) int {
fs := flag.NewFlagSet("chats sync telegram", flag.ContinueOnError)
limit := fs.Int("limit", 0, "max messages per chat (0 = all)")
phone := fs.String("phone", "", "phone number (default env TELEGRAM_PHONE)")
help := fs.Bool("help", false, "")
fs.SetOutput(os.Stderr)
if err := fs.Parse(args); err != nil {
return 2
}
if *help {
fmt.Fprintln(os.Stderr, "usage: chats sync telegram [--limit N] [--phone PHONE]")
return 0
f, err := parseTelegramFlags(args)
if err != nil {
return cliparse.Fail(err)
}
limit := f.Limit
phone := f.Phone
apiIDStr := envVar("TELEGRAM_API_ID", "")
apiHash := envVar("TELEGRAM_API_HASH", "")
sessionStr := envVar("TELEGRAM_SESSION_STRING", "")
phoneNum := *phone
phoneNum := phone
if phoneNum == "" {
phoneNum = envVar("TELEGRAM_PHONE", "")
}
@@ -78,7 +73,7 @@ func RunSyncTelegram(args []string) int {
defer cancel()
start := time.Now()
if err := src.Sync(ctx, Dir(), *limit); err != nil {
if err := src.Sync(ctx, Dir(), limit); err != nil {
fmt.Fprintf(os.Stderr, "chats sync telegram: %v\n", err)
return 1
}