From 4db459ce45c48edfefd8cd3b3922fdc02359ff65 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 26 Apr 2026 23:21:40 -0700 Subject: [PATCH] feat: add threads command --- internal/cli/app.go | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/internal/cli/app.go b/internal/cli/app.go index 6d711ee..43e3819 100644 --- a/internal/cli/app.go +++ b/internal/cli/app.go @@ -86,7 +86,9 @@ func (a *App) Run(ctx context.Context, args []string) error { return a.runDoctor(ctx, rest[1:]) case "sync": return a.runSync(ctx, rest[1:]) - case "configure", "refresh", "summarize", "key-summaries", "embed", "cluster", "cluster-experiment", "threads", "runs", "clusters", "durable-clusters", "cluster-detail", "cluster-explain", "neighbors", "search", "close-thread", "close-cluster", "exclude-cluster-member", "include-cluster-member", "set-cluster-canonical", "merge-clusters", "split-cluster", "export-sync", "import-sync", "validate-sync", "portable-size", "sync-status", "optimize", "tui", "completion": + case "threads": + return a.runThreads(ctx, rest[1:]) + case "configure", "refresh", "summarize", "key-summaries", "embed", "cluster", "cluster-experiment", "runs", "clusters", "durable-clusters", "cluster-detail", "cluster-explain", "neighbors", "search", "close-thread", "close-cluster", "exclude-cluster-member", "include-cluster-member", "set-cluster-canonical", "merge-clusters", "split-cluster", "export-sync", "import-sync", "validate-sync", "portable-size", "sync-status", "optimize", "tui", "completion": _ = ctx return notImplemented(rest[0]) default: @@ -94,6 +96,45 @@ func (a *App) Run(ctx context.Context, args []string) error { } } +func (a *App) runThreads(ctx context.Context, args []string) error { + fs := flag.NewFlagSet("threads", flag.ContinueOnError) + fs.SetOutput(io.Discard) + includeClosed := fs.Bool("include-closed", false, "include locally closed rows") + if err := fs.Parse(args); err != nil { + return usageErr(err) + } + if fs.NArg() != 1 { + return usageErr(fmt.Errorf("threads requires owner/repo")) + } + owner, repoName, err := parseOwnerRepo(fs.Arg(0)) + if err != nil { + return usageErr(err) + } + + cfg, err := config.Load(a.configPath) + if err != nil { + return err + } + st, err := store.Open(ctx, cfg.DBPath) + if err != nil { + return err + } + defer st.Close() + + repo, err := st.RepositoryByFullName(ctx, owner+"/"+repoName) + if err != nil { + return err + } + threads, err := st.ListThreads(ctx, repo.ID, *includeClosed) + if err != nil { + return err + } + return a.writeOutput("threads", map[string]any{ + "repository": repo.FullName, + "threads": threads, + }, true) +} + func (a *App) runSync(ctx context.Context, args []string) error { fs := flag.NewFlagSet("sync", flag.ContinueOnError) fs.SetOutput(io.Discard)