package askcli import ( "context" "fmt" "io" ) // Runner performs CLI work that would otherwise be handled by ask itself. // // The interface is implemented by the executor that ultimately proxies commands to Taskwarrior. type Runner interface { Run(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.Writer) (int, error) } // Dispatcher translates CLI arguments into concrete subcommands and presents the output. type Dispatcher struct { runner Runner jsonOutput bool } // NewDispatcher creates a Dispatcher backed by the provided Runner or a default executor when nil. func NewDispatcher(runner Runner) *Dispatcher { if runner == nil { e := NewExecutor("ask") runner = &e } return &Dispatcher{runner: runner} } func parseGlobalFlags(args []string) ([]string, bool) { var filtered []string var jsonOutput bool for _, arg := range args { if arg == "--json" { jsonOutput = true continue } filtered = append(filtered, arg) } return filtered, jsonOutput } // Dispatch parses CLI arguments, handles global flags, and routes the request to the matching subcommand. func (d *Dispatcher) Dispatch(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.Writer) (int, error) { args, jsonOutput := parseGlobalFlags(args) d.jsonOutput = jsonOutput scope, args := parseTaskScopePrefix(args) ctx = contextWithTaskScope(ctx, scope) if len(args) == 0 { args = []string{"list"} } subcommand := args[0] entry, ok := commandRegistry.get(subcommand) if !ok { return d.unknownCommand(stderr, subcommand) } return entry.handler(d, ctx, args, stdin, stdout, stderr) } func (d *Dispatcher) help(w io.Writer) (int, error) { _, _ = io.WriteString(w, "ask - task management CLI\n") _, _ = io.WriteString(w, "\nScope prefixes:\n") _, _ = io.WriteString(w, " ask na Run a subcommand against project tasks without +agent\n") _, _ = io.WriteString(w, " ask no-agent Alias for ask na\n") _, _ = io.WriteString(w, "\nSubcommands:\n") _, _ = io.WriteString(w, " ask add [mods...] [depends:,...] Create a new task and print created task \n") _, _ = io.WriteString(w, " ask list [filters] List active tasks (default)\n") _, _ = io.WriteString(w, " ask ready List READY tasks (not blocked)\n") _, _ = io.WriteString(w, " ask all [filters] List all tasks including completed/deleted\n") _, _ = io.WriteString(w, " ask info [id|uuid] Show task details or current started task\n") _, _ = io.WriteString(w, " ask annotate \"note\" Add annotation to task\n") _, _ = io.WriteString(w, " ask start Start working on task\n") _, _ = io.WriteString(w, " ask stop Stop work on a task\n") _, _ = io.WriteString(w, " ask done Mark task complete\n") _, _ = io.WriteString(w, " ask priority

Set priority (H/M/L)\n") _, _ = io.WriteString(w, " ask tag +/- Add or remove tag\n") _, _ = io.WriteString(w, " ask dep add Add dependency\n") _, _ = io.WriteString(w, " ask dep rm Remove dependency\n") _, _ = io.WriteString(w, " ask dep list List dependencies\n") _, _ = io.WriteString(w, " ask urgency List tasks sorted by urgency\n") _, _ = io.WriteString(w, " ask modify Modify task fields\n") _, _ = io.WriteString(w, " ask denotate \"text\" Remove annotation\n") _, _ = io.WriteString(w, " ask delete Delete a task\n") _, _ = io.WriteString(w, " ask fish Emit Fish shell completion script\n") return 0, nil } func (d *Dispatcher) unknownCommand(w io.Writer, subcommand string) (int, error) { fmt.Fprintf(w, "ask: unknown subcommand %q\n", subcommand) return 1, nil }