diff options
| -rwxr-xr-x | cmd/ask/ask | bin | 3528465 -> 3536143 bytes | |||
| -rw-r--r-- | internal/askcli/command_info_add.go | 11 | ||||
| -rw-r--r-- | internal/askcli/command_info_add_test.go | 22 | ||||
| -rw-r--r-- | internal/askcli/taskexec.go | 4 | ||||
| -rw-r--r-- | internal/askcli/taskexec_test.go | 4 | ||||
| -rw-r--r-- | internal/version.go | 2 |
6 files changed, 37 insertions, 6 deletions
diff --git a/cmd/ask/ask b/cmd/ask/ask Binary files differindex 3dce495..9d4efad 100755 --- a/cmd/ask/ask +++ b/cmd/ask/ask diff --git a/internal/askcli/command_info_add.go b/internal/askcli/command_info_add.go index 0de9be5..2680163 100644 --- a/internal/askcli/command_info_add.go +++ b/internal/askcli/command_info_add.go @@ -70,15 +70,22 @@ func extractUUIDFromAddOutput(output string) string { return "" } +// parseAddArgs splits args into taskwarrior modifier tokens and the description. +// Modifier tokens are args that start with "priority:", "+", or "-" AND contain +// no spaces (tags and priority flags cannot have spaces). The first arg that is +// not a modifier begins the description; all remaining args are joined with spaces. +// If all args are modifiers the description is empty. func parseAddArgs(args []string) (modifiers []string, description string) { for i, arg := range args { - if strings.HasPrefix(arg, "priority:") || strings.HasPrefix(arg, "+") || strings.HasPrefix(arg, "-") { + isModifier := !strings.Contains(arg, " ") && + (strings.HasPrefix(arg, "priority:") || strings.HasPrefix(arg, "+") || strings.HasPrefix(arg, "-")) + if isModifier { modifiers = append(modifiers, arg) } else { description = strings.Join(args[i:], " ") return } } - description = strings.Join(args, " ") + // All args were modifiers; no description provided. return } diff --git a/internal/askcli/command_info_add_test.go b/internal/askcli/command_info_add_test.go index ce821ca..f4dc5c1 100644 --- a/internal/askcli/command_info_add_test.go +++ b/internal/askcli/command_info_add_test.go @@ -185,4 +185,26 @@ func TestParseAddArgs(t *testing.T) { if desc != "Old task" || len(mods) != 1 || mods[0] != "-deprecated" { t.Fatalf("parseAddArgs([\"-deprecated\", \"Old task\"]) = mods=%v, desc=%q", mods, desc) } + + // An arg starting with "+" but containing spaces is NOT a modifier — it is + // the start of the description. This prevents agents from quoting tag+desc + // together (e.g. "+code-quality Fix foo") and having them land in the wrong + // place. + mods, desc = parseAddArgs([]string{"+code-quality Fix foo bar"}) + if desc != "+code-quality Fix foo bar" || len(mods) != 0 { + t.Fatalf("space-containing +arg should be description, got mods=%v, desc=%q", mods, desc) + } + + // Same issue when mixed: a proper tag precedes a space-containing arg. + mods, desc = parseAddArgs([]string{"+cli", "+code-quality Fix foo bar"}) + if desc != "+code-quality Fix foo bar" || len(mods) != 1 || mods[0] != "+cli" { + t.Fatalf("mixed case: mods=%v, desc=%q", mods, desc) + } + + // All-modifier args (no description) should return empty description, not a + // duplicate of the modifiers. + mods, desc = parseAddArgs([]string{"+cli", "+agent"}) + if desc != "" || len(mods) != 2 { + t.Fatalf("all-modifier case: mods=%v, desc=%q, want empty desc", mods, desc) + } } diff --git a/internal/askcli/taskexec.go b/internal/askcli/taskexec.go index 9c9aa69..0553070 100644 --- a/internal/askcli/taskexec.go +++ b/internal/askcli/taskexec.go @@ -37,7 +37,9 @@ func (e Executor) taskArgs(repoRoot string, args []string) ([]string, error) { if err != nil { return nil, err } - return append([]string{"project:" + projectName, "+agent"}, args...), nil + // rc.confirmation=off suppresses interactive prompts so the CLI works + // non-interactively (stdin is never available when called from an agent). + return append([]string{"rc.confirmation=off", "project:" + projectName, "+agent"}, args...), nil } func (e Executor) Run(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.Writer) (int, error) { diff --git a/internal/askcli/taskexec_test.go b/internal/askcli/taskexec_test.go index 4450a28..5cbb7f1 100644 --- a/internal/askcli/taskexec_test.go +++ b/internal/askcli/taskexec_test.go @@ -17,7 +17,7 @@ func TestExecutorTaskArgs(t *testing.T) { if err != nil { t.Fatalf("taskArgs returned error: %v", err) } - want := []string{"project:hexai", "+agent", "list", "limit:1"} + want := []string{"rc.confirmation=off", "project:hexai", "+agent", "list", "limit:1"} if !reflect.DeepEqual(args, want) { t.Fatalf("task args = %v, want %v", args, want) } @@ -47,7 +47,7 @@ func TestExecutorRun_InjectsProjectFilterAndAgentTag(t *testing.T) { if gotName != "/usr/bin/task" { t.Fatalf("task binary = %q, want /usr/bin/task", gotName) } - wantArgs := []string{"project:hexai", "+agent", "list", "limit:1"} + wantArgs := []string{"rc.confirmation=off", "project:hexai", "+agent", "list", "limit:1"} if !reflect.DeepEqual(gotArgs, wantArgs) { t.Fatalf("task args = %v, want %v", gotArgs, wantArgs) } diff --git a/internal/version.go b/internal/version.go index 54e3b55..30b7ba7 100644 --- a/internal/version.go +++ b/internal/version.go @@ -1,4 +1,4 @@ // Package internal provides the Hexai semantic version identifier used by CLI and LSP binaries. package internal -const Version = "0.25.10" +const Version = "0.25.11" |
