diff --git a/cmd/bd/show.go b/cmd/bd/show.go index da066e65..a056ddfc 100644 --- a/cmd/bd/show.go +++ b/cmd/bd/show.go @@ -111,6 +111,7 @@ var showCmd = &cobra.Command{ Labels []string `json:"labels,omitempty"` Dependencies []*types.IssueWithDependencyMetadata `json:"dependencies,omitempty"` Dependents []*types.IssueWithDependencyMetadata `json:"dependents,omitempty"` + Comments []*types.Comment `json:"comments,omitempty"` } details := &IssueDetails{Issue: issue} details.Labels, _ = issueStore.GetLabels(ctx, issue.ID) @@ -118,6 +119,7 @@ var showCmd = &cobra.Command{ details.Dependencies, _ = sqliteStore.GetDependenciesWithMetadata(ctx, issue.ID) details.Dependents, _ = sqliteStore.GetDependentsWithMetadata(ctx, issue.ID) } + details.Comments, _ = issueStore.GetIssueComments(ctx, issue.ID) allDetails = append(allDetails, details) } else { if displayIdx > 0 { @@ -151,6 +153,7 @@ var showCmd = &cobra.Command{ Labels []string `json:"labels,omitempty"` Dependencies []*types.IssueWithDependencyMetadata `json:"dependencies,omitempty"` Dependents []*types.IssueWithDependencyMetadata `json:"dependents,omitempty"` + Comments []*types.Comment `json:"comments,omitempty"` } var details IssueDetails if err := json.Unmarshal(resp.Data, &details); err == nil { @@ -173,6 +176,7 @@ var showCmd = &cobra.Command{ Labels []string `json:"labels,omitempty"` Dependencies []*types.IssueWithDependencyMetadata `json:"dependencies,omitempty"` Dependents []*types.IssueWithDependencyMetadata `json:"dependents,omitempty"` + Comments []*types.Comment `json:"comments,omitempty"` } var details IssueDetails if err := json.Unmarshal(resp.Data, &details); err != nil { @@ -301,9 +305,20 @@ var showCmd = &cobra.Command{ fmt.Printf(" ◊ %s: %s [P%d - %s]\n", dep.ID, dep.Title, dep.Priority, dep.Status) } } - } - fmt.Println() + if len(details.Comments) > 0 { + fmt.Printf("\nComments (%d):\n", len(details.Comments)) + for _, comment := range details.Comments { + fmt.Printf(" [%s] %s\n", comment.Author, comment.CreatedAt.Format("2006-01-02 15:04")) + commentLines := strings.Split(comment.Text, "\n") + for _, line := range commentLines { + fmt.Printf(" %s\n", line) + } + } + } + } + + fmt.Println() } } diff --git a/commands/comments.md b/commands/comments.md index 0cffe659..3b0e186c 100644 --- a/commands/comments.md +++ b/commands/comments.md @@ -5,6 +5,8 @@ argument-hint: [issue-id] View or add comments to a beads issue. +Comments are separate from issue properties (title, description, etc.) because they serve a different purpose: they're a **discussion thread** rather than **singular editable fields**. Use `bd comments` for threaded conversations and `bd edit` for core issue metadata. + ## View Comments To view all comments on an issue: diff --git a/commands/update.md b/commands/update.md index 535e1704..9e239ffb 100644 --- a/commands/update.md +++ b/commands/update.md @@ -16,6 +16,8 @@ If arguments are missing, ask the user for: Use the beads MCP `update` tool to apply the changes. Show the updated issue to confirm the change. +**Note:** Comments are managed separately with `bd comments add`. The `update` command is for singular, versioned properties (title, status, priority, etc.), while comments form a discussion thread that's appended to, not updated. + Common workflows: - Start work: Update status to `in_progress` - Mark blocked: Update status to `blocked` diff --git a/internal/rpc/server_issues_epics.go b/internal/rpc/server_issues_epics.go index d9d55f43..0e40a102 100644 --- a/internal/rpc/server_issues_epics.go +++ b/internal/rpc/server_issues_epics.go @@ -1222,12 +1222,16 @@ func (s *Server) handleShow(req *Request) Response { } } + // Fetch comments + comments, _ := store.GetIssueComments(ctx, issue.ID) + // Create detailed response with related data type IssueDetails struct { *types.Issue Labels []string `json:"labels,omitempty"` Dependencies []*types.IssueWithDependencyMetadata `json:"dependencies,omitempty"` Dependents []*types.IssueWithDependencyMetadata `json:"dependents,omitempty"` + Comments []*types.Comment `json:"comments,omitempty"` } details := &IssueDetails{ @@ -1235,6 +1239,7 @@ func (s *Server) handleShow(req *Request) Response { Labels: labels, Dependencies: deps, Dependents: dependents, + Comments: comments, } data, _ := json.Marshal(details) diff --git a/internal/storage/sqlite/migrations/028_tombstone_closed_at.go b/internal/storage/sqlite/migrations/028_tombstone_closed_at.go index 2991bb8b..82d9353d 100644 --- a/internal/storage/sqlite/migrations/028_tombstone_closed_at.go +++ b/internal/storage/sqlite/migrations/028_tombstone_closed_at.go @@ -81,9 +81,19 @@ func MigrateTombstoneClosedAt(db *sql.DB) error { } // Step 2: Copy data from old table to new table + // List all columns explicitly to handle cases where old table has fewer columns + // Note: created_by is added in migration 029, so don't reference it here _, err = db.Exec(` INSERT INTO issues_new - SELECT * FROM issues + SELECT + id, content_hash, title, description, design, acceptance_criteria, notes, + status, priority, issue_type, assignee, estimated_minutes, + created_at, updated_at, closed_at, external_ref, + source_repo, compaction_level, compacted_at, compacted_at_commit, original_size, + deleted_at, deleted_by, delete_reason, original_type, + sender, ephemeral, close_reason, pinned, is_template, + await_type, await_id, timeout_ns, waiters + FROM issues `) if err != nil { return fmt.Errorf("failed to copy issues data: %w", err)