Fix epic hierarchy display in bd show command
Amp-Thread-ID: https://ampcode.com/threads/T-43f6c7bf-9d4f-4475-8266-69e0a35a5909 Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
@@ -13,6 +13,22 @@ import (
|
|||||||
"github.com/steveyegge/beads/internal/types"
|
"github.com/steveyegge/beads/internal/types"
|
||||||
"github.com/steveyegge/beads/internal/utils"
|
"github.com/steveyegge/beads/internal/utils"
|
||||||
)
|
)
|
||||||
|
// formatDependencyType converts a dependency type to a human-readable label
|
||||||
|
func formatDependencyType(depType types.DependencyType) string {
|
||||||
|
switch depType {
|
||||||
|
case types.DepBlocks:
|
||||||
|
return "blocks"
|
||||||
|
case types.DepRelated:
|
||||||
|
return "related"
|
||||||
|
case types.DepParentChild:
|
||||||
|
return "parent-child"
|
||||||
|
case types.DepDiscoveredFrom:
|
||||||
|
return "discovered-from"
|
||||||
|
default:
|
||||||
|
return string(depType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var showCmd = &cobra.Command{
|
var showCmd = &cobra.Command{
|
||||||
Use: "show [id...]",
|
Use: "show [id...]",
|
||||||
Short: "Show issue details",
|
Short: "Show issue details",
|
||||||
@@ -60,9 +76,9 @@ var showCmd = &cobra.Command{
|
|||||||
if jsonOutput {
|
if jsonOutput {
|
||||||
type IssueDetails struct {
|
type IssueDetails struct {
|
||||||
types.Issue
|
types.Issue
|
||||||
Labels []string `json:"labels,omitempty"`
|
Labels []string `json:"labels,omitempty"`
|
||||||
Dependencies []*types.Issue `json:"dependencies,omitempty"`
|
Dependencies []*types.IssueWithDependencyMetadata `json:"dependencies,omitempty"`
|
||||||
Dependents []*types.Issue `json:"dependents,omitempty"`
|
Dependents []*types.IssueWithDependencyMetadata `json:"dependents,omitempty"`
|
||||||
}
|
}
|
||||||
var details IssueDetails
|
var details IssueDetails
|
||||||
if err := json.Unmarshal(resp.Data, &details); err == nil {
|
if err := json.Unmarshal(resp.Data, &details); err == nil {
|
||||||
@@ -80,9 +96,9 @@ var showCmd = &cobra.Command{
|
|||||||
// Parse response and use existing formatting code
|
// Parse response and use existing formatting code
|
||||||
type IssueDetails struct {
|
type IssueDetails struct {
|
||||||
types.Issue
|
types.Issue
|
||||||
Labels []string `json:"labels,omitempty"`
|
Labels []string `json:"labels,omitempty"`
|
||||||
Dependencies []*types.Issue `json:"dependencies,omitempty"`
|
Dependencies []*types.IssueWithDependencyMetadata `json:"dependencies,omitempty"`
|
||||||
Dependents []*types.Issue `json:"dependents,omitempty"`
|
Dependents []*types.IssueWithDependencyMetadata `json:"dependents,omitempty"`
|
||||||
}
|
}
|
||||||
var details IssueDetails
|
var details IssueDetails
|
||||||
if err := json.Unmarshal(resp.Data, &details); err != nil {
|
if err := json.Unmarshal(resp.Data, &details); err != nil {
|
||||||
@@ -152,15 +168,19 @@ var showCmd = &cobra.Command{
|
|||||||
fmt.Printf("\nLabels: %v\n", details.Labels)
|
fmt.Printf("\nLabels: %v\n", details.Labels)
|
||||||
}
|
}
|
||||||
if len(details.Dependencies) > 0 {
|
if len(details.Dependencies) > 0 {
|
||||||
fmt.Printf("\nDepends on (%d):\n", len(details.Dependencies))
|
fmt.Printf("\nDependencies (%d):\n", len(details.Dependencies))
|
||||||
for _, dep := range details.Dependencies {
|
for _, dep := range details.Dependencies {
|
||||||
fmt.Printf(" → %s: %s [P%d]\n", dep.ID, dep.Title, dep.Priority)
|
fmt.Printf(" [%s] %s: %s [P%d]\n",
|
||||||
|
formatDependencyType(dep.DependencyType),
|
||||||
|
dep.ID, dep.Title, dep.Priority)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(details.Dependents) > 0 {
|
if len(details.Dependents) > 0 {
|
||||||
fmt.Printf("\nBlocks (%d):\n", len(details.Dependents))
|
fmt.Printf("\nDependents (%d):\n", len(details.Dependents))
|
||||||
for _, dep := range details.Dependents {
|
for _, dep := range details.Dependents {
|
||||||
fmt.Printf(" ← %s: %s [P%d]\n", dep.ID, dep.Title, dep.Priority)
|
fmt.Printf(" [%s] %s: %s [P%d]\n",
|
||||||
|
formatDependencyType(dep.DependencyType),
|
||||||
|
dep.ID, dep.Title, dep.Priority)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
@@ -280,20 +300,49 @@ var showCmd = &cobra.Command{
|
|||||||
if len(labels) > 0 {
|
if len(labels) > 0 {
|
||||||
fmt.Printf("\nLabels: %v\n", labels)
|
fmt.Printf("\nLabels: %v\n", labels)
|
||||||
}
|
}
|
||||||
// Show dependencies
|
// Show dependencies with metadata (including type)
|
||||||
deps, _ := store.GetDependencies(ctx, issue.ID)
|
var depsWithMeta []*types.IssueWithDependencyMetadata
|
||||||
if len(deps) > 0 {
|
if sqliteStore, ok := store.(*sqlite.SQLiteStorage); ok {
|
||||||
fmt.Printf("\nDepends on (%d):\n", len(deps))
|
depsWithMeta, _ = sqliteStore.GetDependenciesWithMetadata(ctx, issue.ID)
|
||||||
|
} else {
|
||||||
|
// Fallback for non-SQLite storage
|
||||||
|
deps, _ := store.GetDependencies(ctx, issue.ID)
|
||||||
for _, dep := range deps {
|
for _, dep := range deps {
|
||||||
fmt.Printf(" → %s: %s [P%d]\n", dep.ID, dep.Title, dep.Priority)
|
depsWithMeta = append(depsWithMeta, &types.IssueWithDependencyMetadata{
|
||||||
|
Issue: *dep,
|
||||||
|
DependencyType: types.DepBlocks, // default
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Show dependents
|
if len(depsWithMeta) > 0 {
|
||||||
dependents, _ := store.GetDependents(ctx, issue.ID)
|
fmt.Printf("\nDependencies (%d):\n", len(depsWithMeta))
|
||||||
if len(dependents) > 0 {
|
for _, dep := range depsWithMeta {
|
||||||
fmt.Printf("\nBlocks (%d):\n", len(dependents))
|
fmt.Printf(" [%s] %s: %s [P%d]\n",
|
||||||
for _, dep := range dependents {
|
formatDependencyType(dep.DependencyType),
|
||||||
fmt.Printf(" ← %s: %s [P%d]\n", dep.ID, dep.Title, dep.Priority)
|
dep.ID, dep.Title, dep.Priority)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show dependents with metadata (including type)
|
||||||
|
var dependentsWithMeta []*types.IssueWithDependencyMetadata
|
||||||
|
if sqliteStore, ok := store.(*sqlite.SQLiteStorage); ok {
|
||||||
|
dependentsWithMeta, _ = sqliteStore.GetDependentsWithMetadata(ctx, issue.ID)
|
||||||
|
} else {
|
||||||
|
// Fallback for non-SQLite storage
|
||||||
|
dependents, _ := store.GetDependents(ctx, issue.ID)
|
||||||
|
for _, dependent := range dependents {
|
||||||
|
dependentsWithMeta = append(dependentsWithMeta, &types.IssueWithDependencyMetadata{
|
||||||
|
Issue: *dependent,
|
||||||
|
DependencyType: types.DepBlocks, // default
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(dependentsWithMeta) > 0 {
|
||||||
|
fmt.Printf("\nDependents (%d):\n", len(dependentsWithMeta))
|
||||||
|
for _, dep := range dependentsWithMeta {
|
||||||
|
fmt.Printf(" [%s] %s: %s [P%d]\n",
|
||||||
|
formatDependencyType(dep.DependencyType),
|
||||||
|
dep.ID, dep.Title, dep.Priority)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Show comments
|
// Show comments
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/steveyegge/beads/internal/storage/sqlite"
|
||||||
"github.com/steveyegge/beads/internal/types"
|
"github.com/steveyegge/beads/internal/types"
|
||||||
"github.com/steveyegge/beads/internal/utils"
|
"github.com/steveyegge/beads/internal/utils"
|
||||||
)
|
)
|
||||||
@@ -392,17 +393,39 @@ func (s *Server) handleShow(req *Request) Response {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate labels, dependencies, and dependents
|
// Populate labels, dependencies (with metadata), and dependents (with metadata)
|
||||||
labels, _ := store.GetLabels(ctx, issue.ID)
|
labels, _ := store.GetLabels(ctx, issue.ID)
|
||||||
deps, _ := store.GetDependencies(ctx, issue.ID)
|
|
||||||
dependents, _ := store.GetDependents(ctx, issue.ID)
|
// Get dependencies and dependents with metadata (including dependency type)
|
||||||
|
var deps []*types.IssueWithDependencyMetadata
|
||||||
|
var dependents []*types.IssueWithDependencyMetadata
|
||||||
|
if sqliteStore, ok := store.(*sqlite.SQLiteStorage); ok {
|
||||||
|
deps, _ = sqliteStore.GetDependenciesWithMetadata(ctx, issue.ID)
|
||||||
|
dependents, _ = sqliteStore.GetDependentsWithMetadata(ctx, issue.ID)
|
||||||
|
} else {
|
||||||
|
// Fallback for non-SQLite storage (won't have dependency type metadata)
|
||||||
|
regularDeps, _ := store.GetDependencies(ctx, issue.ID)
|
||||||
|
for _, d := range regularDeps {
|
||||||
|
deps = append(deps, &types.IssueWithDependencyMetadata{
|
||||||
|
Issue: *d,
|
||||||
|
DependencyType: types.DepBlocks, // default
|
||||||
|
})
|
||||||
|
}
|
||||||
|
regularDependents, _ := store.GetDependents(ctx, issue.ID)
|
||||||
|
for _, d := range regularDependents {
|
||||||
|
dependents = append(dependents, &types.IssueWithDependencyMetadata{
|
||||||
|
Issue: *d,
|
||||||
|
DependencyType: types.DepBlocks, // default
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create detailed response with related data
|
// Create detailed response with related data
|
||||||
type IssueDetails struct {
|
type IssueDetails struct {
|
||||||
*types.Issue
|
*types.Issue
|
||||||
Labels []string `json:"labels,omitempty"`
|
Labels []string `json:"labels,omitempty"`
|
||||||
Dependencies []*types.Issue `json:"dependencies,omitempty"`
|
Dependencies []*types.IssueWithDependencyMetadata `json:"dependencies,omitempty"`
|
||||||
Dependents []*types.Issue `json:"dependents,omitempty"`
|
Dependents []*types.IssueWithDependencyMetadata `json:"dependents,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
details := &IssueDetails{
|
details := &IssueDetails{
|
||||||
|
|||||||
Reference in New Issue
Block a user