package orch import ( "fmt" "strings" "time" "ai-workflow-skill/packages/coord-core/protocol" "ai-workflow-skill/packages/coord-core/store" "github.com/spf13/cobra" ) type waitOptions struct { runID string eventTypesRaw string afterEventID int64 timeoutSeconds int } func newWaitCmd(root *rootOptions) *cobra.Command { opts := &waitOptions{} cmd := &cobra.Command{ Use: "wait", Short: "Block until matching run-scoped task events become available", Long: helpLong( "Use wait as the leader-side blocking primitive.", "Instead of polling with manual sleep loops, wait blocks until later matching task events exist for the run, such as ready, blocked, done, or failed.", "Use --after-event when resuming from a known cursor so you do not reprocess earlier events.", ), Example: ` orch --db .agents/coord.db wait --run blog_mvp_001 --for task_blocked,task_done --after-event 0 --timeout-seconds 900`, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() sqlDB, err := openOrchDB(ctx, root.dbPath) if err != nil { return err } defer sqlDB.Close() result, err := store.NewOrchStore(sqlDB).WaitForEvents(ctx, store.WaitInput{ RunID: opts.runID, EventTypes: splitCommaList(opts.eventTypesRaw), AfterEventID: opts.afterEventID, Timeout: time.Duration(opts.timeoutSeconds) * time.Second, }) if err != nil { return err } resp := protocol.Success{ OK: true, Command: "wait", Data: map[string]any{ "run_id": opts.runID, "woke": result.Woke, "next_event_id": result.NextEventID, "events": result.Events, }, } if root.json { return protocol.WriteJSON(cmd.OutOrStdout(), resp) } if !result.Woke { _, err = fmt.Fprintf(cmd.OutOrStdout(), "wait timed out after event %d\n", result.NextEventID) return err } for _, event := range result.Events { if _, err := fmt.Fprintf(cmd.OutOrStdout(), "%d\t%s\t%s\t%s\n", event.EventID, event.Type, event.TaskID, event.Summary); err != nil { return err } } return nil }, } cmd.Flags().StringVar(&opts.runID, "run", "", "Run ID") cmd.Flags().StringVar(&opts.eventTypesRaw, "for", "task_ready,task_blocked,task_done,task_failed", "Comma-separated event types to wait for") cmd.Flags().Int64Var(&opts.afterEventID, "after-event", 0, "Only wait for events after this event ID") cmd.Flags().IntVar(&opts.timeoutSeconds, "timeout-seconds", 0, "Maximum time to wait before timing out") _ = cmd.MarkFlagRequired("run") return cmd } func splitCommaList(value string) []string { if strings.TrimSpace(value) == "" { return nil } parts := strings.Split(value, ",") result := make([]string, 0, len(parts)) for _, part := range parts { part = strings.TrimSpace(part) if part == "" { continue } result = append(result, part) } return result }