Skip to content

Commit 109c013

Browse files
julianknutsenclaude
andcommitted
Add wl status command for detailed wanted item view
Read-only command that displays full lifecycle state of a wanted item, conditionally showing completion and stamp details based on status. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent fe2f8ef commit 109c013

8 files changed

Lines changed: 669 additions & 11 deletions

File tree

cmd/wl/cmd_fake_test.go

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,16 @@ type fakeWLCommonsStore struct {
1515
stamps map[string]*commons.Stamp
1616

1717
// Error injection fields
18-
InsertWantedErr error
19-
ClaimWantedErr error
20-
SubmitCompletionErr error
21-
QueryWantedErr error
22-
QueryCompletionErr error
23-
AcceptCompletionErr error
24-
UpdateWantedErr error
25-
DeleteWantedErr error
18+
InsertWantedErr error
19+
ClaimWantedErr error
20+
SubmitCompletionErr error
21+
QueryWantedErr error
22+
QueryWantedDetailErr error
23+
QueryCompletionErr error
24+
QueryStampErr error
25+
AcceptCompletionErr error
26+
UpdateWantedErr error
27+
DeleteWantedErr error
2628
}
2729

2830
func newFakeWLCommonsStore() *fakeWLCommonsStore {
@@ -124,6 +126,29 @@ func (f *fakeWLCommonsStore) QueryWanted(wantedID string) (*commons.WantedItem,
124126
return &cp, nil
125127
}
126128

129+
func (f *fakeWLCommonsStore) QueryWantedDetail(wantedID string) (*commons.WantedItem, error) {
130+
if f.QueryWantedDetailErr != nil {
131+
return nil, f.QueryWantedDetailErr
132+
}
133+
return f.QueryWanted(wantedID)
134+
}
135+
136+
func (f *fakeWLCommonsStore) QueryStamp(stampID string) (*commons.Stamp, error) {
137+
if f.QueryStampErr != nil {
138+
return nil, f.QueryStampErr
139+
}
140+
141+
f.mu.Lock()
142+
defer f.mu.Unlock()
143+
144+
stamp, ok := f.stamps[stampID]
145+
if !ok {
146+
return nil, fmt.Errorf("stamp %q not found", stampID)
147+
}
148+
cp := *stamp
149+
return &cp, nil
150+
}
151+
127152
func (f *fakeWLCommonsStore) QueryCompletion(wantedID string) (*commons.CompletionRecord, error) {
128153
if f.QueryCompletionErr != nil {
129154
return nil, f.QueryCompletionErr
@@ -140,7 +165,7 @@ func (f *fakeWLCommonsStore) QueryCompletion(wantedID string) (*commons.Completi
140165
return &cp, nil
141166
}
142167

143-
func (f *fakeWLCommonsStore) AcceptCompletion(wantedID, _, _ string, stamp *commons.Stamp) error {
168+
func (f *fakeWLCommonsStore) AcceptCompletion(wantedID, _, rigHandle string, stamp *commons.Stamp) error {
144169
if f.AcceptCompletionErr != nil {
145170
return f.AcceptCompletionErr
146171
}
@@ -159,6 +184,11 @@ func (f *fakeWLCommonsStore) AcceptCompletion(wantedID, _, _ string, stamp *comm
159184

160185
stored := *stamp
161186
f.stamps[stamp.ID] = &stored
187+
188+
if rec, ok := f.completions[wantedID]; ok {
189+
rec.StampID = stamp.ID
190+
rec.ValidatedBy = rigHandle
191+
}
162192
return nil
163193
}
164194

cmd/wl/cmd_status.go

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"strings"
7+
8+
"github.com/spf13/cobra"
9+
"github.com/steveyegge/wasteland/internal/commons"
10+
"github.com/steveyegge/wasteland/internal/style"
11+
)
12+
13+
func newStatusCmd(stdout, stderr io.Writer) *cobra.Command {
14+
return &cobra.Command{
15+
Use: "status <wanted-id>",
16+
Short: "Show detailed status for a wanted item",
17+
Long: `Show the full lifecycle status of a wanted item.
18+
19+
Displays all fields including description, timestamps, and conditionally
20+
shows completion and stamp details based on the item's current state.
21+
22+
Examples:
23+
wl status w-abc123`,
24+
Args: cobra.ExactArgs(1),
25+
RunE: func(cmd *cobra.Command, args []string) error {
26+
return runStatus(cmd, stdout, stderr, args[0])
27+
},
28+
}
29+
}
30+
31+
func runStatus(cmd *cobra.Command, stdout, _ io.Writer, wantedID string) error {
32+
wlCfg, err := resolveWasteland(cmd)
33+
if err != nil {
34+
return fmt.Errorf("loading wasteland config: %w", err)
35+
}
36+
37+
store := commons.NewWLCommons(wlCfg.LocalDir)
38+
39+
result, err := getStatus(store, wantedID)
40+
if err != nil {
41+
return err
42+
}
43+
44+
renderStatus(stdout, result)
45+
return nil
46+
}
47+
48+
// StatusResult holds everything needed to render a status view.
49+
type StatusResult struct {
50+
Item *commons.WantedItem
51+
Completion *commons.CompletionRecord
52+
Stamp *commons.Stamp
53+
}
54+
55+
// getStatus fetches all data needed for a status display.
56+
func getStatus(store commons.WLCommonsStore, wantedID string) (*StatusResult, error) {
57+
item, err := store.QueryWantedDetail(wantedID)
58+
if err != nil {
59+
return nil, fmt.Errorf("querying wanted item: %w", err)
60+
}
61+
62+
result := &StatusResult{Item: item}
63+
64+
if item.Status == "in_review" || item.Status == "completed" {
65+
completion, err := store.QueryCompletion(wantedID)
66+
if err != nil {
67+
return nil, fmt.Errorf("querying completion: %w", err)
68+
}
69+
result.Completion = completion
70+
71+
if completion.StampID != "" {
72+
stamp, err := store.QueryStamp(completion.StampID)
73+
if err != nil {
74+
return nil, fmt.Errorf("querying stamp: %w", err)
75+
}
76+
result.Stamp = stamp
77+
}
78+
}
79+
80+
return result, nil
81+
}
82+
83+
// renderStatus writes the formatted status output.
84+
func renderStatus(w io.Writer, r *StatusResult) {
85+
item := r.Item
86+
87+
// Header
88+
fmt.Fprintf(w, "%s\n", style.Bold.Render(fmt.Sprintf("%s: %s", item.ID, item.Title)))
89+
fmt.Fprintln(w)
90+
91+
// Status with color
92+
fmt.Fprintf(w, " Status: %s\n", colorizeStatus(item.Status))
93+
94+
// Type/Priority line
95+
typePri := " "
96+
if item.Type != "" {
97+
typePri += fmt.Sprintf("Type: %-14s", item.Type)
98+
}
99+
typePri += fmt.Sprintf("Priority: P%d", item.Priority)
100+
fmt.Fprintln(w, typePri)
101+
102+
// Project/Effort line
103+
projEffort := " "
104+
if item.Project != "" {
105+
projEffort += fmt.Sprintf("Project: %-14s", item.Project)
106+
}
107+
projEffort += fmt.Sprintf("Effort: %s", item.EffortLevel)
108+
fmt.Fprintln(w, projEffort)
109+
110+
// Posted by
111+
if item.PostedBy != "" {
112+
fmt.Fprintf(w, " Posted by: %s\n", item.PostedBy)
113+
}
114+
115+
// Tags
116+
if len(item.Tags) > 0 {
117+
fmt.Fprintf(w, " Tags: %s\n", strings.Join(item.Tags, ", "))
118+
}
119+
120+
// Timestamps
121+
if item.CreatedAt != "" {
122+
fmt.Fprintf(w, " Created: %s\n", item.CreatedAt)
123+
}
124+
if item.UpdatedAt != "" {
125+
fmt.Fprintf(w, " Updated: %s\n", item.UpdatedAt)
126+
}
127+
128+
// Description
129+
if item.Description != "" {
130+
fmt.Fprintln(w)
131+
fmt.Fprintln(w, " Description:")
132+
fmt.Fprintf(w, " %s\n", item.Description)
133+
}
134+
135+
// Claimed by
136+
if item.ClaimedBy != "" {
137+
fmt.Fprintln(w)
138+
fmt.Fprintf(w, " Claimed by: %s\n", item.ClaimedBy)
139+
}
140+
141+
// Completion
142+
if r.Completion != nil {
143+
fmt.Fprintln(w)
144+
fmt.Fprintf(w, " Completion: %s\n", r.Completion.ID)
145+
if r.Completion.Evidence != "" {
146+
fmt.Fprintf(w, " Evidence: %s\n", r.Completion.Evidence)
147+
}
148+
fmt.Fprintf(w, " Completed by: %s\n", r.Completion.CompletedBy)
149+
}
150+
151+
// Stamp
152+
if r.Stamp != nil {
153+
fmt.Fprintln(w)
154+
fmt.Fprintf(w, " Stamp: %s\n", r.Stamp.ID)
155+
fmt.Fprintf(w, " Quality: %d Reliability: %d Severity: %s\n",
156+
r.Stamp.Quality, r.Stamp.Reliability, r.Stamp.Severity)
157+
if len(r.Stamp.SkillTags) > 0 {
158+
fmt.Fprintf(w, " Skills: %s\n", strings.Join(r.Stamp.SkillTags, ", "))
159+
}
160+
if r.Stamp.Author != "" {
161+
fmt.Fprintf(w, " Accepted by: %s\n", r.Stamp.Author)
162+
}
163+
if r.Stamp.Message != "" {
164+
fmt.Fprintf(w, " Message: %s\n", r.Stamp.Message)
165+
}
166+
}
167+
}
168+
169+
func colorizeStatus(status string) string {
170+
switch status {
171+
case "completed":
172+
return style.Success.Render(status)
173+
case "in_review", "claimed":
174+
return style.Warning.Render(status)
175+
case "withdrawn":
176+
return style.Dim.Render(status)
177+
default:
178+
return status
179+
}
180+
}

0 commit comments

Comments
 (0)