@@ -17,12 +17,16 @@ import (
1717
1818func newBrowseCmd (stdout , stderr io.Writer ) * cobra.Command {
1919 var (
20- project string
21- status string
22- itemType string
23- priority int
24- limit int
25- jsonOut bool
20+ project string
21+ status string
22+ itemType string
23+ priority int
24+ limit int
25+ jsonOut bool
26+ ephemeral bool
27+ postedBy string
28+ claimedBy string
29+ search string
2630 )
2731
2832 cmd := & cobra.Command {
@@ -31,9 +35,8 @@ func newBrowseCmd(stdout, stderr io.Writer) *cobra.Command {
3135 Args : cobra .NoArgs ,
3236 Long : `Browse the Wasteland wanted board.
3337
34- Clones the upstream commons database to a temporary directory, queries it,
35- then deletes the clone. Works with all provider types (DoltHub, GitHub,
36- file, git).
38+ Pulls the latest upstream changes into your local clone and queries it.
39+ Use --ephemeral to clone to a temp dir instead (slower, for edge cases).
3740
3841EXAMPLES:
3942 wl browse # All open wanted items
@@ -42,9 +45,22 @@ EXAMPLES:
4245 wl browse --status claimed # Claimed items
4346 wl browse --priority 0 # Critical priority only
4447 wl browse --limit 5 # Show 5 items
45- wl browse --json # JSON output` ,
48+ wl browse --json # JSON output
49+ wl browse --posted-by alice # Items posted by alice
50+ wl browse --claimed-by bob # Items claimed by bob
51+ wl browse --search auth # Search in title
52+ wl browse --ephemeral # Clone upstream (slow)` ,
4653 RunE : func (cmd * cobra.Command , _ []string ) error {
47- return runBrowse (cmd , stdout , stderr , project , status , itemType , priority , limit , jsonOut )
54+ return runBrowse (cmd , stdout , stderr , BrowseFilter {
55+ Status : status ,
56+ Project : project ,
57+ Type : itemType ,
58+ Priority : priority ,
59+ Limit : limit ,
60+ PostedBy : postedBy ,
61+ ClaimedBy : claimedBy ,
62+ Search : search ,
63+ }, jsonOut , ephemeral )
4864 },
4965 }
5066
@@ -54,6 +70,10 @@ EXAMPLES:
5470 cmd .Flags ().IntVar (& priority , "priority" , - 1 , "Filter by priority (0=critical, 2=medium, 4=backlog)" )
5571 cmd .Flags ().IntVar (& limit , "limit" , 50 , "Maximum items to display" )
5672 cmd .Flags ().BoolVar (& jsonOut , "json" , false , "Output as JSON" )
73+ cmd .Flags ().BoolVar (& ephemeral , "ephemeral" , false , "Clone upstream to temp dir instead of querying local (slow)" )
74+ cmd .Flags ().StringVar (& postedBy , "posted-by" , "" , "Filter by poster's rig handle" )
75+ cmd .Flags ().StringVar (& claimedBy , "claimed-by" , "" , "Filter by claimer's rig handle" )
76+ cmd .Flags ().StringVar (& search , "search" , "" , "Search in title" )
5777 _ = cmd .RegisterFlagCompletionFunc ("status" , func (_ * cobra.Command , _ []string , _ string ) ([]string , cobra.ShellCompDirective ) {
5878 return []string {"open" , "claimed" , "in_review" , "completed" , "withdrawn" }, cobra .ShellCompDirectiveNoFileComp
5979 })
@@ -64,21 +84,55 @@ EXAMPLES:
6484 return cmd
6585}
6686
67- func runBrowse (cmd * cobra.Command , stdout , _ io.Writer , project , status , itemType string , priority , limit int , jsonOut bool ) error {
87+ func runBrowse (cmd * cobra.Command , stdout , _ io.Writer , filter BrowseFilter , jsonOut , ephemeral bool ) error {
6888 cfg , err := resolveWasteland (cmd )
6989 if err != nil {
7090 return fmt .Errorf ("loading wasteland config: %w" , err )
7191 }
7292
73- doltPath , err := exec .LookPath ("dolt" )
93+ if err := requireDolt (); err != nil {
94+ return err
95+ }
96+
97+ query := buildBrowseQuery (filter )
98+
99+ if ephemeral {
100+ return runBrowseEphemeral (stdout , cfg , query , jsonOut )
101+ }
102+
103+ return runBrowseLocal (stdout , cfg , query , jsonOut )
104+ }
105+
106+ func runBrowseLocal (stdout io.Writer , cfg * federation.Config , query string , jsonOut bool ) error {
107+ fmt .Fprintf (stdout , "Syncing with upstream...\n " )
108+
109+ if err := commons .PullUpstream (cfg .LocalDir ); err != nil {
110+ return fmt .Errorf ("pulling upstream: %w" , err )
111+ }
112+
113+ if jsonOut {
114+ doltPath , _ := exec .LookPath ("dolt" )
115+ sqlCmd := exec .Command (doltPath , "sql" , "-q" , query , "-r" , "json" )
116+ sqlCmd .Dir = cfg .LocalDir
117+ sqlCmd .Stdout = stdout
118+ sqlCmd .Stderr = os .Stderr
119+ return sqlCmd .Run ()
120+ }
121+
122+ csvData , err := commons .DoltSQLQuery (cfg .LocalDir , query )
74123 if err != nil {
75- return fmt .Errorf ("dolt not found in PATH — install from https://docs.dolthub.com/introduction/installation" )
124+ return fmt .Errorf ("querying local database: %w" , err )
76125 }
77126
127+ return renderBrowseCSV (stdout , csvData )
128+ }
129+
130+ func runBrowseEphemeral (stdout io.Writer , cfg * federation.Config , query string , jsonOut bool ) error {
131+ doltPath , _ := exec .LookPath ("dolt" )
132+
78133 _ , commonsDB , _ := federation .ParseUpstream (cfg .Upstream )
79134 cloneURL := cfg .UpstreamURL
80135 if cloneURL == "" {
81- // Backward compat: old configs without UpstreamURL.
82136 cloneURL = cfg .Upstream
83137 }
84138
@@ -99,14 +153,6 @@ func runBrowse(cmd *cobra.Command, stdout, _ io.Writer, project, status, itemTyp
99153 }
100154 fmt .Fprintf (stdout , "%s Cloned successfully\n \n " , style .Bold .Render ("✓" ))
101155
102- query := buildBrowseQuery (BrowseFilter {
103- Status : status ,
104- Project : project ,
105- Type : itemType ,
106- Priority : priority ,
107- Limit : limit ,
108- })
109-
110156 if jsonOut {
111157 sqlCmd := exec .Command (doltPath , "sql" , "-q" , query , "-r" , "json" )
112158 sqlCmd .Dir = cloneDir
@@ -120,11 +166,14 @@ func runBrowse(cmd *cobra.Command, stdout, _ io.Writer, project, status, itemTyp
120166
121167// BrowseFilter holds filter parameters for building a browse query.
122168type BrowseFilter struct {
123- Status string
124- Project string
125- Type string
126- Priority int
127- Limit int
169+ Status string
170+ Project string
171+ Type string
172+ Priority int
173+ Limit int
174+ PostedBy string
175+ ClaimedBy string
176+ Search string
128177}
129178
130179func buildBrowseQuery (f BrowseFilter ) string {
@@ -142,6 +191,15 @@ func buildBrowseQuery(f BrowseFilter) string {
142191 if f .Priority >= 0 {
143192 conditions = append (conditions , fmt .Sprintf ("priority = %d" , f .Priority ))
144193 }
194+ if f .PostedBy != "" {
195+ conditions = append (conditions , fmt .Sprintf ("posted_by = '%s'" , commons .EscapeSQL (f .PostedBy )))
196+ }
197+ if f .ClaimedBy != "" {
198+ conditions = append (conditions , fmt .Sprintf ("claimed_by = '%s'" , commons .EscapeSQL (f .ClaimedBy )))
199+ }
200+ if f .Search != "" {
201+ conditions = append (conditions , fmt .Sprintf ("title LIKE '%%%s%%'" , commons .EscapeSQL (f .Search )))
202+ }
145203
146204 query := "SELECT id, title, project, type, priority, posted_by, status, effort_level FROM wanted"
147205 if len (conditions ) > 0 {
@@ -153,19 +211,8 @@ func buildBrowseQuery(f BrowseFilter) string {
153211 return query
154212}
155213
156- func renderBrowseTable (stdout io.Writer , doltPath , cloneDir , query string ) error {
157- sqlCmd := exec .Command (doltPath , "sql" , "-q" , query , "-r" , "csv" )
158- sqlCmd .Dir = cloneDir
159- output , err := sqlCmd .Output ()
160- if err != nil {
161- var exitErr * exec.ExitError
162- if errors .As (err , & exitErr ) {
163- return fmt .Errorf ("query failed: %s" , string (exitErr .Stderr ))
164- }
165- return fmt .Errorf ("running query: %w" , err )
166- }
167-
168- rows := wlParseCSV (string (output ))
214+ func renderBrowseCSV (stdout io.Writer , csvData string ) error {
215+ rows := wlParseCSV (csvData )
169216 if len (rows ) <= 1 {
170217 fmt .Fprintln (stdout , "No wanted items found matching your filters." )
171218 return nil
@@ -196,6 +243,21 @@ func renderBrowseTable(stdout io.Writer, doltPath, cloneDir, query string) error
196243 return nil
197244}
198245
246+ func renderBrowseTable (stdout io.Writer , doltPath , cloneDir , query string ) error {
247+ sqlCmd := exec .Command (doltPath , "sql" , "-q" , query , "-r" , "csv" )
248+ sqlCmd .Dir = cloneDir
249+ output , err := sqlCmd .Output ()
250+ if err != nil {
251+ var exitErr * exec.ExitError
252+ if errors .As (err , & exitErr ) {
253+ return fmt .Errorf ("query failed: %s" , string (exitErr .Stderr ))
254+ }
255+ return fmt .Errorf ("running query: %w" , err )
256+ }
257+
258+ return renderBrowseCSV (stdout , string (output ))
259+ }
260+
199261func wlParseCSV (data string ) [][]string {
200262 var rows [][]string
201263 for _ , line := range strings .Split (strings .TrimSpace (data ), "\n " ) {
0 commit comments