feat: add --parent flag to bd list command (bd-yqhh)
Filters issues by parent issue ID using parent-child dependencies. Example: bd list --parent=bd-xyz --status=open Changes: - Add ParentID field to IssueFilter type - Add --parent flag to list command - Forward parent filter through RPC - Implement filtering in SQLite and memory storage 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -148,6 +148,9 @@ var listCmd = &cobra.Command{
|
|||||||
// Template filtering (beads-1ra)
|
// Template filtering (beads-1ra)
|
||||||
includeTemplates, _ := cmd.Flags().GetBool("include-templates")
|
includeTemplates, _ := cmd.Flags().GetBool("include-templates")
|
||||||
|
|
||||||
|
// Parent filtering (bd-yqhh)
|
||||||
|
parentID, _ := cmd.Flags().GetString("parent")
|
||||||
|
|
||||||
// Use global jsonOutput set by PersistentPreRun
|
// Use global jsonOutput set by PersistentPreRun
|
||||||
|
|
||||||
// Normalize labels: trim, dedupe, remove empty
|
// Normalize labels: trim, dedupe, remove empty
|
||||||
@@ -311,6 +314,11 @@ var listCmd = &cobra.Command{
|
|||||||
filter.IsTemplate = &isTemplate
|
filter.IsTemplate = &isTemplate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parent filtering (bd-yqhh): filter children by parent issue
|
||||||
|
if parentID != "" {
|
||||||
|
filter.ParentID = &parentID
|
||||||
|
}
|
||||||
|
|
||||||
// Check database freshness before reading (bd-2q6d, bd-c4rq)
|
// Check database freshness before reading (bd-2q6d, bd-c4rq)
|
||||||
// Skip check when using daemon (daemon auto-imports on staleness)
|
// Skip check when using daemon (daemon auto-imports on staleness)
|
||||||
ctx := rootCtx
|
ctx := rootCtx
|
||||||
@@ -392,6 +400,9 @@ var listCmd = &cobra.Command{
|
|||||||
// Template filtering (beads-1ra)
|
// Template filtering (beads-1ra)
|
||||||
listArgs.IncludeTemplates = includeTemplates
|
listArgs.IncludeTemplates = includeTemplates
|
||||||
|
|
||||||
|
// Parent filtering (bd-yqhh)
|
||||||
|
listArgs.ParentID = parentID
|
||||||
|
|
||||||
resp, err := daemonClient.List(listArgs)
|
resp, err := daemonClient.List(listArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||||
@@ -666,6 +677,9 @@ func init() {
|
|||||||
// Template filtering (beads-1ra): exclude templates by default
|
// Template filtering (beads-1ra): exclude templates by default
|
||||||
listCmd.Flags().Bool("include-templates", false, "Include template molecules in output")
|
listCmd.Flags().Bool("include-templates", false, "Include template molecules in output")
|
||||||
|
|
||||||
|
// Parent filtering (bd-yqhh): filter children by parent issue
|
||||||
|
listCmd.Flags().String("parent", "", "Filter by parent issue ID (shows children of specified issue)")
|
||||||
|
|
||||||
// Note: --json flag is defined as a persistent flag in main.go, not here
|
// Note: --json flag is defined as a persistent flag in main.go, not here
|
||||||
rootCmd.AddCommand(listCmd)
|
rootCmd.AddCommand(listCmd)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,6 +162,9 @@ type ListArgs struct {
|
|||||||
|
|
||||||
// Template filtering (beads-1ra)
|
// Template filtering (beads-1ra)
|
||||||
IncludeTemplates bool `json:"include_templates,omitempty"`
|
IncludeTemplates bool `json:"include_templates,omitempty"`
|
||||||
|
|
||||||
|
// Parent filtering (bd-yqhh)
|
||||||
|
ParentID string `json:"parent_id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CountArgs represents arguments for the count operation
|
// CountArgs represents arguments for the count operation
|
||||||
|
|||||||
@@ -751,6 +751,11 @@ func (s *Server) handleList(req *Request) Response {
|
|||||||
filter.IsTemplate = &isTemplate
|
filter.IsTemplate = &isTemplate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parent filtering (bd-yqhh)
|
||||||
|
if listArgs.ParentID != "" {
|
||||||
|
filter.ParentID = &listArgs.ParentID
|
||||||
|
}
|
||||||
|
|
||||||
// Guard against excessive ID lists to avoid SQLite parameter limits
|
// Guard against excessive ID lists to avoid SQLite parameter limits
|
||||||
const maxIDs = 1000
|
const maxIDs = 1000
|
||||||
if len(filter.IDs) > maxIDs {
|
if len(filter.IDs) > maxIDs {
|
||||||
|
|||||||
@@ -571,6 +571,20 @@ func (m *MemoryStorage) SearchIssues(ctx context.Context, query string, filter t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parent filtering (bd-yqhh): filter children by parent issue
|
||||||
|
if filter.ParentID != nil {
|
||||||
|
isChild := false
|
||||||
|
for _, dep := range m.dependencies[issue.ID] {
|
||||||
|
if dep.Type == types.DepParentChild && dep.DependsOnID == *filter.ParentID {
|
||||||
|
isChild = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !isChild {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Copy issue and attach metadata
|
// Copy issue and attach metadata
|
||||||
issueCopy := *issue
|
issueCopy := *issue
|
||||||
if deps, ok := m.dependencies[issue.ID]; ok {
|
if deps, ok := m.dependencies[issue.ID]; ok {
|
||||||
|
|||||||
@@ -1623,6 +1623,12 @@ func (s *SQLiteStorage) SearchIssues(ctx context.Context, query string, filter t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parent filtering (bd-yqhh): filter children by parent issue
|
||||||
|
if filter.ParentID != nil {
|
||||||
|
whereClauses = append(whereClauses, "id IN (SELECT issue_id FROM dependencies WHERE type = 'parent-child' AND depends_on_id = ?)")
|
||||||
|
args = append(args, *filter.ParentID)
|
||||||
|
}
|
||||||
|
|
||||||
whereSQL := ""
|
whereSQL := ""
|
||||||
if len(whereClauses) > 0 {
|
if len(whereClauses) > 0 {
|
||||||
whereSQL = "WHERE " + strings.Join(whereClauses, " AND ")
|
whereSQL = "WHERE " + strings.Join(whereClauses, " AND ")
|
||||||
|
|||||||
@@ -1098,6 +1098,12 @@ func (t *sqliteTxStorage) SearchIssues(ctx context.Context, query string, filter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parent filtering (bd-yqhh): filter children by parent issue
|
||||||
|
if filter.ParentID != nil {
|
||||||
|
whereClauses = append(whereClauses, "id IN (SELECT issue_id FROM dependencies WHERE type = 'parent-child' AND depends_on_id = ?)")
|
||||||
|
args = append(args, *filter.ParentID)
|
||||||
|
}
|
||||||
|
|
||||||
whereSQL := ""
|
whereSQL := ""
|
||||||
if len(whereClauses) > 0 {
|
if len(whereClauses) > 0 {
|
||||||
whereSQL = "WHERE " + strings.Join(whereClauses, " AND ")
|
whereSQL = "WHERE " + strings.Join(whereClauses, " AND ")
|
||||||
|
|||||||
@@ -562,6 +562,9 @@ type IssueFilter struct {
|
|||||||
|
|
||||||
// Template filtering (beads-1ra)
|
// Template filtering (beads-1ra)
|
||||||
IsTemplate *bool // Filter by template flag (nil = any, true = only templates, false = exclude templates)
|
IsTemplate *bool // Filter by template flag (nil = any, true = only templates, false = exclude templates)
|
||||||
|
|
||||||
|
// Parent filtering (bd-yqhh): filter children by parent issue ID
|
||||||
|
ParentID *string // Filter by parent issue (via parent-child dependency)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SortPolicy determines how ready work is ordered
|
// SortPolicy determines how ready work is ordered
|
||||||
|
|||||||
Reference in New Issue
Block a user