bd sync: 2025-12-23 23:38:57
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
@@ -10,6 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/steveyegge/beads/internal/storage"
|
||||
"github.com/steveyegge/beads/internal/types"
|
||||
)
|
||||
|
||||
// ServerVersion is the version of this RPC server
|
||||
@@ -80,6 +82,8 @@ const (
|
||||
type MutationEvent struct {
|
||||
Type string // One of the Mutation* constants
|
||||
IssueID string // e.g., "bd-42"
|
||||
Title string // Issue title for display context (may be empty for some operations)
|
||||
Assignee string // Issue assignee for display context (may be empty)
|
||||
Timestamp time.Time
|
||||
// Optional metadata for richer events (used by status, bonded, etc.)
|
||||
OldStatus string `json:"old_status,omitempty"` // Previous status (for status events)
|
||||
@@ -138,10 +142,13 @@ func NewServer(socketPath string, store storage.Storage, workspacePath string, d
|
||||
// emitMutation sends a mutation event to the daemon's event-driven loop.
|
||||
// Non-blocking: drops event if channel is full (sync will happen eventually).
|
||||
// Also stores in recent mutations buffer for polling.
|
||||
func (s *Server) emitMutation(eventType, issueID string) {
|
||||
// Title and assignee provide context for activity feeds; pass empty strings if unknown.
|
||||
func (s *Server) emitMutation(eventType, issueID, title, assignee string) {
|
||||
s.emitRichMutation(MutationEvent{
|
||||
Type: eventType,
|
||||
IssueID: issueID,
|
||||
Type: eventType,
|
||||
IssueID: issueID,
|
||||
Title: title,
|
||||
Assignee: assignee,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -227,3 +234,120 @@ func (s *Server) handleGetMutations(req *Request) Response {
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// handleGetMoleculeProgress handles the get_molecule_progress RPC operation
|
||||
// Returns detailed progress for a molecule (parent issue with child steps)
|
||||
func (s *Server) handleGetMoleculeProgress(req *Request) Response {
|
||||
var args GetMoleculeProgressArgs
|
||||
if err := json.Unmarshal(req.Args, &args); err != nil {
|
||||
return Response{
|
||||
Success: false,
|
||||
Error: fmt.Sprintf("invalid arguments: %v", err),
|
||||
}
|
||||
}
|
||||
|
||||
store := s.storage
|
||||
if store == nil {
|
||||
return Response{
|
||||
Success: false,
|
||||
Error: "storage not available",
|
||||
}
|
||||
}
|
||||
|
||||
ctx := s.reqCtx(req)
|
||||
|
||||
// Get the molecule (parent issue)
|
||||
molecule, err := store.GetIssue(ctx, args.MoleculeID)
|
||||
if err != nil {
|
||||
return Response{
|
||||
Success: false,
|
||||
Error: fmt.Sprintf("failed to get molecule: %v", err),
|
||||
}
|
||||
}
|
||||
if molecule == nil {
|
||||
return Response{
|
||||
Success: false,
|
||||
Error: fmt.Sprintf("molecule not found: %s", args.MoleculeID),
|
||||
}
|
||||
}
|
||||
|
||||
// Get children (issues that have parent-child dependency on this molecule)
|
||||
var children []*types.IssueWithDependencyMetadata
|
||||
if sqliteStore, ok := store.(interface {
|
||||
GetDependentsWithMetadata(ctx context.Context, issueID string) ([]*types.IssueWithDependencyMetadata, error)
|
||||
}); ok {
|
||||
allDependents, err := sqliteStore.GetDependentsWithMetadata(ctx, args.MoleculeID)
|
||||
if err != nil {
|
||||
return Response{
|
||||
Success: false,
|
||||
Error: fmt.Sprintf("failed to get molecule children: %v", err),
|
||||
}
|
||||
}
|
||||
// Filter for parent-child relationships only
|
||||
for _, dep := range allDependents {
|
||||
if dep.DependencyType == types.DepParentChild {
|
||||
children = append(children, dep)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get blocked issue IDs for status computation
|
||||
blockedIDs := make(map[string]bool)
|
||||
if sqliteStore, ok := store.(interface {
|
||||
GetBlockedIssueIDs(ctx context.Context) ([]string, error)
|
||||
}); ok {
|
||||
ids, err := sqliteStore.GetBlockedIssueIDs(ctx)
|
||||
if err == nil {
|
||||
for _, id := range ids {
|
||||
blockedIDs[id] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build steps from children
|
||||
steps := make([]MoleculeStep, 0, len(children))
|
||||
for _, child := range children {
|
||||
step := MoleculeStep{
|
||||
ID: child.ID,
|
||||
Title: child.Title,
|
||||
}
|
||||
|
||||
// Compute step status
|
||||
switch child.Status {
|
||||
case types.StatusClosed:
|
||||
step.Status = "done"
|
||||
case types.StatusInProgress:
|
||||
step.Status = "current"
|
||||
default: // open, blocked, etc.
|
||||
if blockedIDs[child.ID] {
|
||||
step.Status = "blocked"
|
||||
} else {
|
||||
step.Status = "ready"
|
||||
}
|
||||
}
|
||||
|
||||
// Set timestamps
|
||||
startTime := child.CreatedAt.Format(time.RFC3339)
|
||||
step.StartTime = &startTime
|
||||
|
||||
if child.ClosedAt != nil {
|
||||
closeTime := child.ClosedAt.Format(time.RFC3339)
|
||||
step.CloseTime = &closeTime
|
||||
}
|
||||
|
||||
steps = append(steps, step)
|
||||
}
|
||||
|
||||
progress := MoleculeProgress{
|
||||
MoleculeID: molecule.ID,
|
||||
Title: molecule.Title,
|
||||
Assignee: molecule.Assignee,
|
||||
Steps: steps,
|
||||
}
|
||||
|
||||
data, _ := json.Marshal(progress)
|
||||
return Response{
|
||||
Success: true,
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user