Skip to content

Add entire trail link subcommand#919

Draft
matthiaswenz wants to merge 1 commit intomainfrom
cli-trail-branches-optional
Draft

Add entire trail link subcommand#919
matthiaswenz wants to merge 1 commit intomainfrom
cli-trail-branches-optional

Conversation

@matthiaswenz
Copy link
Copy Markdown
Collaborator

@matthiaswenz matthiaswenz commented Apr 10, 2026

Summary

  • Adds entire trail link [trail-id] [--branch <name>] subcommand to link a branchless trail to a git branch (companion to entirehq/entire.io#1306 making branches optional on trails)
  • Defaults to the currently checked-out branch; --branch overrides. Interactive picker when no trail ID is provided (filters to branchless trails only)
  • Handles HTTP 409 Conflict with a clear message explaining each branch can only be linked to one trail
  • Updates trail list and trail show to display (none) for branchless trails instead of an empty string

Test plan

  • entire trail link <id> links a branchless trail to the current branch
  • entire trail link <id> --branch feat/foo links to a named branch
  • entire trail link (no args) shows interactive picker of branchless trails
  • Linking to a branch already linked to another trail shows 409 conflict error
  • entire trail list shows (none) for branchless trails
  • entire trail (show) displays Branch: (none) for branchless trails

🤖 Generated with Claude Code


Note

Medium Risk
Adds a new CLI flow that mutates trail state via PATCH and introduces interactive selection and conflict handling; main risk is incorrect branch selection or unexpected API error handling impacting users’ trails.

Overview
Adds a new entire trail link [trail-id] [--branch <name>] subcommand to attach a previously branchless trail to a git branch (defaulting to the current branch, with an interactive picker when no ID is provided). The command updates the trail via PATCH and surfaces HTTP 409 Conflict as a clear “branch already linked” error.

Updates trail detail and list output to render branchless trails as Branch: (none) / (none) in the table instead of showing an empty value.

Reviewed by Cursor Bugbot for commit cf07d31. Configure here.

…ranches

Trails can now be created without a branch (entirehq/entire.io#1306). This adds
`entire trail link [trail-id] [--branch <name>]` to link a branchless trail to
the current or a named branch, with interactive picker and HTTP 409 handling.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 7f24ce489b27
Copilot AI review requested due to automatic review settings April 10, 2026 17:32
Copy link
Copy Markdown
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 entire trail link subcommand to associate previously branchless trails with a git branch, supporting the “branches optional on trails” workflow.

Changes:

  • Registers entire trail link [trail-id] [--branch <name>], defaulting to the current branch and offering an interactive picker when no ID is provided.
  • Updates trail show and trail list output to display (none) for branchless trails.
  • Adds explicit handling for HTTP 409 Conflict when a branch is already linked to another trail.

Comment on lines +614 to +617
// PATCH the trail to set the branch
updateReq := api.TrailUpdateRequest{
Branch: &branch,
}
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

The command help/PR intent says this subcommand links branchless trails, but when a trail ID is provided it unconditionally PATCHes the branch and can effectively re-link an already-linked trail to a new branch. Consider fetching the trail first (GET the trail detail endpoint) and refusing when Trail.Branch is already set (or update the command description/introduce an explicit --force to make re-linking intentional).

Copilot uses AI. Check for mistakes.
Comment on lines +668 to +671
if len(branchless) == 0 {
fmt.Fprintln(w, "No trails without a branch found.")
fmt.Fprintln(w, "Use 'entire trail create' to create a new trail, or pass a trail ID directly.")
return "", NewSilentError(errors.New("no branchless trails"))
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

This path prints an error condition to stdout and then returns SilentError. Other commands print user-facing error messages to cmd.ErrOrStderr() before returning SilentError (e.g., cmd/entire/cli/migrate.go:45-46). Consider writing these messages to stderr (cmd.ErrOrStderr()) so scripts/pipes can distinguish normal output from errors.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit cf07d31. Configure here.

),
)
if err := form.Run(); err != nil {
return "", handleFormCancellation(w, "Trail link", err)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Form cancellation proceeds with empty trail ID

Medium Severity

When the user cancels the interactive trail picker (Ctrl+C), handleFormCancellation returns nil (by design, for clean cobra exits). But pickBranchlessTrail returns ("", nil), and the caller runTrailLink only checks err != nil, so it continues with an empty trailID. This causes a PATCH request to an invalid URL (trailing / with no ID), resulting in a confusing API error after the "Trail link cancelled." message. Every other call site uses handleFormCancellation as a direct return from RunE, where returning nil correctly signals a clean exit.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit cf07d31. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants