Skip to content

feat: add buzz archived command to list archived goals#226

Open
Copilot wants to merge 4 commits intomainfrom
copilot/add-archived-goals-listing
Open

feat: add buzz archived command to list archived goals#226
Copilot wants to merge 4 commits intomainfrom
copilot/add-archived-goals-listing

Conversation

Copy link
Contributor

Copilot AI commented Feb 23, 2026

  • Explore codebase to understand patterns
  • Add FetchArchivedGoals() function in beeminder.go
  • Extract shared fetchGoalsList() helper — FetchGoals and FetchArchivedGoals both delegate to it; uses url.PathEscape for username and url.Values for auth_token
  • Add 3 tests for FetchArchivedGoals() in beeminder_test.go — all with full request assertions (method, path, auth_token)
  • Add handleArchivedCommand() in main.go
  • Extract shared printGoalsTable() helper to remove duplication
  • Add case "archived": in CLI switch statement in main.go
  • Fix doc comment on handleArchivedCommand to include Units column
  • Update printHelp() and "Available commands" error message in main.go
  • Update README.md with documentation for the new command
  • All tests pass; no CodeQL alerts
Original prompt

This section details on the original issue you should resolve

<issue_title>Add way to list archived goals</issue_title>
<issue_description>## Overview
Add functionality to list archived goals in the Buzz CLI application.

Current State

  • The Beeminder API already provides an endpoint for archived goals: GET /users/{username}/goals/archived.json
  • The codebase currently only fetches and displays active goals using FetchGoals() in beeminder.go
  • The handleListCommand() in main.go handles listing active goals

Implementation Details

1. Add API Function for Archived Goals

File: beeminder.go

Add a new function similar to FetchGoals():

func FetchArchivedGoals(config *Config) ([]Goal, error)

This function should:

  • Call the Beeminder API endpoint: /api/v1/users/{username}/goals/archived.json
  • Use the same authentication pattern as FetchGoals()
  • Return a slice of Goal objects
  • Include proper error handling and logging

2. Add CLI Command

File: main.go

Add a new command-line flag or subcommand to list archived goals. Options:

  • Option A: Add a flag to existing list command: buzz list --archived
  • Option B: Create separate command: buzz list-archived or buzz archived

Recommended: Option A for consistency with potential future filters.

3. Display Formatting

Use the existing display patterns from handleListCommand() and handleFilteredCommand():

  • Show slug, title, and other relevant metadata
  • Consider showing archive date if available
  • Maintain consistent formatting with active goal lists

4. Filter Support (Optional Enhancement)

Consider allowing filters on archived goals:

  • Search by slug or title
  • Filter by goal type
  • Sort options (by slug, archive date, etc.)

Technical Considerations

API Response

The archived goals endpoint returns the same Goal structure as active goals, making integration straightforward.

Error Handling

  • Handle cases where user has no archived goals
  • Provide clear error messages for API failures
  • Follow existing error handling patterns in the codebase

Testing

Add tests following the existing patterns:

  • Unit tests for FetchArchivedGoals() (similar to tests in beeminder_test.go)
  • Integration tests for the CLI command
  • Mock server tests for API interactions

Code Patterns

Follow existing conventions:

  • Use LogRequest() and LogResponse() for API calls
  • Follow the error wrapping pattern: fmt.Errorf("failed to...: %w", err)
  • Maintain consistency with existing command handlers

Suggested Implementation Order

  1. Add FetchArchivedGoals() function in beeminder.go
  2. Add corresponding tests in beeminder_test.go
  3. Add CLI command/flag in main.go
  4. Add command handler function
  5. Update documentation (README.md, help text)
  6. Test end-to-end functionality

Related Files

  • beeminder.go - API functions
  • main.go - CLI commands and handlers
  • beeminder_test.go - API function tests
  • model.go - Data structures (may need updates if archived goals need special handling)

Additional Notes

  • The Goal struct already has fields like won, lost, and frozen that may indicate archive status
  • Consider whether archived goals should be excluded from other commands (e.g., buzz next)
  • Archived goals likely won't have meaningful losedate or urgency data</issue_description>

Comments on the Issue (you are @copilot in this section)

@narthur @coderabbitai Please analyze this issue and update the description with implementation details, suggested approach, and any relevant technical considerations.

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Co-authored-by: narthur <4655422+narthur@users.noreply.github.com>
Copilot AI changed the title [WIP] Add functionality to list archived goals in Buzz CLI feat: add buzz archived command to list archived goals Feb 23, 2026
Copilot AI requested a review from narthur February 23, 2026 15:27
@narthur narthur marked this pull request as ready for review February 23, 2026 16:30
Copilot AI review requested due to automatic review settings February 23, 2026 16:30
@github-actions
Copy link

github-actions bot commented Feb 23, 2026

🚀 Pre-release Build Ready

Test builds are ready! Install directly using the bin command:

# Install the pre-release
bin install https://github.com/PinePeakDigital/buzz/releases/tag/pr-226-latest buzz-pr-226
# Run the pre-release
buzz-pr-226
# Uninstall the pre-release
bin remove buzz-pr-226

Direct Download Links

Or download binaries directly from the pre-release page:

💡 No GitHub login required for downloads!

🗑️ This pre-release will be automatically deleted when the PR is closed.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new CLI subcommand to surface Beeminder archived goals, backed by a new API fetch helper and updated docs/tests.

Changes:

  • Added FetchArchivedGoals() to call Beeminder’s /goals/archived.json endpoint.
  • Added buzz archived command handler and refactored shared table printing into printGoalsTable.
  • Added mock-server tests for the archived goals fetch and documented the new command in the README.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
main.go Wires new archived subcommand, adds handler, and extracts shared goals table printing.
beeminder.go Introduces FetchArchivedGoals() API call mirroring FetchGoals().
beeminder_test.go Adds mock-server coverage for FetchArchivedGoals() success/empty/error paths.
README.md Documents buzz archived usage and example output.

Comment on lines +87 to +111
// FetchArchivedGoals fetches the user's archived goals from Beeminder API
func FetchArchivedGoals(config *Config) ([]Goal, error) {
baseURL := getBaseURL(config)
url := fmt.Sprintf("%s/api/v1/users/%s/goals/archived.json?auth_token=%s",
baseURL, config.Username, config.AuthToken)

LogRequest(config, "GET", url)
resp, err := http.Get(url)
if err != nil {
return nil, fmt.Errorf("failed to fetch archived goals: %w", err)
}
defer resp.Body.Close()
LogResponse(config, resp.StatusCode, url)

if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("API returned status %d", resp.StatusCode)
}

var goals []Goal
if err := json.NewDecoder(resp.Body).Decode(&goals); err != nil {
return nil, fmt.Errorf("failed to decode archived goals: %w", err)
}

return goals, nil
}
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FetchArchivedGoals duplicates FetchGoals almost line-for-line (URL construction, logging, http.Get, status check, JSON decode). This increases the chance the two drift (e.g., future improvements to error handling/timeouts only applied to one). Consider extracting a shared helper for GET+decode (parameterized by endpoint path) and have both FetchGoals and FetchArchivedGoals call it.

Copilot uses AI. Check for mistakes.
beeminder.go Outdated
Comment on lines +89 to +92
baseURL := getBaseURL(config)
url := fmt.Sprintf("%s/api/v1/users/%s/goals/archived.json?auth_token=%s",
baseURL, config.Username, config.AuthToken)

Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FetchArchivedGoals builds the request URL via fmt.Sprintf and interpolates username/auth_token without URL-escaping. If either contains characters like spaces, '+' or '&', the request can break or change query semantics. Prefer constructing the URL with net/url (PathEscape for the username path segment and url.Values for the auth_token query) similar to how goal slugs are escaped elsewhere in this file.

Copilot uses AI. Check for mistakes.
Comment on lines +2065 to +2070
func TestFetchArchivedGoalsEmptyList(t *testing.T) {
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte("[]"))
}))
defer mockServer.Close()
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestFetchArchivedGoalsEmptyList doesn't verify the request method/path/query (unlike the main mock-server test). That means it could still pass even if FetchArchivedGoals hits the wrong endpoint or omits auth_token. Add the same assertions (GET, /api/v1/users/testuser/goals/archived.json, auth_token=testtoken) to make this test actually validate the behavior it’s meant to cover.

Copilot uses AI. Check for mistakes.

// TestFetchArchivedGoalsAPIError tests FetchArchivedGoals when the API returns an error
func TestFetchArchivedGoalsAPIError(t *testing.T) {
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestFetchArchivedGoalsAPIError doesn't assert that the failing request was made to the archived-goals endpoint (method/path/auth_token). As written, it only checks that some error occurs, so a regression in URL construction could slip by. Mirror the request assertions used in TestFetchArchivedGoalsWithMockServer before returning 500.

Suggested change
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Verify it's a GET request
if r.Method != http.MethodGet {
t.Errorf("Expected GET request, got %s", r.Method)
}
// Verify the URL path targets the archived goals endpoint
if !strings.Contains(r.URL.Path, "/users/testuser/archived_goals.json") {
t.Errorf("Unexpected URL path for archived goals: %s", r.URL.Path)
}
// Verify auth_token is passed as a query parameter
if r.URL.Query().Get("auth_token") != "testtoken" {
t.Errorf("Expected auth_token query parameter to be %q, got %q", "testtoken", r.URL.Query().Get("auth_token"))
}

Copilot uses AI. Check for mistakes.
main.go Outdated
Comment on lines +561 to +562
// handleArchivedCommand outputs a summary list of all archived goals with slug, title, rate, and stakes
func handleArchivedCommand() {
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc comment for handleArchivedCommand says it outputs slug, title, rate, and stakes, but the shared table includes a Units column as well. Update the comment to match the actual output columns (or adjust the output to match the comment).

Copilot uses AI. Check for mistakes.
@narthur
Copy link
Collaborator

narthur commented Feb 24, 2026

@copilot apply changes based on the comments in this thread

Co-authored-by: narthur <4655422+narthur@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 24, 2026

@copilot apply changes based on the comments in this thread

Done in 1de7684. Applied all five review changes:

  • Extracted fetchGoalsList() shared helper — both FetchGoals and FetchArchivedGoals now delegate to it, eliminating the duplication.
  • URL escaping — the shared helper uses url.PathEscape for the username path segment and url.Values for the auth_token query parameter.
  • TestFetchArchivedGoalsEmptyList — added GET method, exact path, and auth_token assertions.
  • TestFetchArchivedGoalsAPIError — same request assertions added before the 500 response.
  • handleArchivedCommand doc comment — updated to include the Units column.

Copilot AI requested a review from narthur February 24, 2026 17:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add way to list archived goals

3 participants