summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-27 06:44:25 +0200
committerPaul Buetow <paul@buetow.org>2026-03-27 06:44:25 +0200
commit21c1d5c9cc4974ba490b329acae735ff6bde9973 (patch)
tree6eceefd9094eebb9c03eb2fa42d406c9cb2aea9f
parent1f4c09e0f5a2a38c0b133264ec24e8ae5e5a3c22 (diff)
fix ask add alias output for 1a1731dc-1f11-42bd-be12-4c1af7f7e673
-rw-r--r--docs/usage.md2
-rw-r--r--integrationtests/ask_test.go42
-rw-r--r--internal/askcli/command_info_add.go7
-rw-r--r--internal/askcli/command_info_add_test.go41
-rw-r--r--internal/askcli/dispatch.go2
5 files changed, 77 insertions, 17 deletions
diff --git a/docs/usage.md b/docs/usage.md
index 3447cd3..c43f406 100644
--- a/docs/usage.md
+++ b/docs/usage.md
@@ -135,7 +135,7 @@ cat SOMEFILE.txt | hexai --tps-simulation 20
| Subcommand | Description |
|---|---|
-| `ask add "description"` | Create a new task |
+| `ask add "description"` | Create a new task and print its alias ID |
| `ask add priority:H "description"` | Create task with priority |
| `ask add +tag "description"` | Create task with tag |
| `ask list` | List pending tasks only (alias-ID table) |
diff --git a/integrationtests/ask_test.go b/integrationtests/ask_test.go
index 337a486..0e56bf3 100644
--- a/integrationtests/ask_test.go
+++ b/integrationtests/ask_test.go
@@ -117,17 +117,24 @@ func runTaskWithStdin(ctx context.Context, args []string, stdin string) (stdout,
}
// createTask creates a new task via ask add and returns its UUID.
-// ask add outputs the UUID directly (via rc.verbose=new-uuid), so no follow-up lookup is needed.
+// ask add prints the human-facing alias ID, so we resolve the created UUID via ask info.
func createTask(ctx context.Context, desc string) (string, error) {
stdout, stderr, code := runAsk(ctx, []string{"add", "+integrationtest", desc})
if code != 0 {
return "", fmt.Errorf("create task failed (code %d): stdout=%s stderr=%s", code, stdout.String(), stderr.String())
}
- uuid := strings.TrimSpace(stdout.String())
- if uuid == "" {
- return "", fmt.Errorf("could not extract UUID from ask add output: %s", stdout.String())
+ id := strings.TrimSpace(stdout.String())
+ if id == "" {
+ return "", fmt.Errorf("could not extract task ID from ask add output: %s", stdout.String())
}
- return uuid, nil
+ info, ok := getTaskInfoFast(ctx, id)
+ if !ok {
+ return "", fmt.Errorf("could not resolve task ID %q after ask add", id)
+ }
+ if info.UUID == "" {
+ return "", fmt.Errorf("ask info %q did not return a UUID", id)
+ }
+ return info.UUID, nil
}
func deleteTask(ctx context.Context, uuid string) {
@@ -291,8 +298,8 @@ func TestAdd(t *testing.T) {
}
}
-// TestAddReturnsUUID verifies that ask add outputs a UUID, never a numeric task ID.
-func TestAddReturnsUUID(t *testing.T) {
+// TestAddReturnsAlias verifies that ask add outputs the human-facing alias ID.
+func TestAddReturnsAlias(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
@@ -300,11 +307,24 @@ func TestAddReturnsUUID(t *testing.T) {
if code != 0 {
t.Fatalf("ask add failed with code %d", code)
}
- uuid := strings.TrimSpace(stdout.String())
- defer deleteTask(ctx, uuid)
+ id := strings.TrimSpace(stdout.String())
+ info, ok := getTaskInfoFast(ctx, id)
+ if !ok {
+ t.Fatalf("ask info %q failed after add", id)
+ }
+ defer deleteTask(ctx, info.UUID)
- if !uuidFormatRx.MatchString(uuid) {
- t.Errorf("ask add output %q is not a valid UUID", uuid)
+ if id == "" {
+ t.Fatal("ask add returned an empty task ID")
+ }
+ if uuidFormatRx.MatchString(id) {
+ t.Fatalf("ask add output %q leaked a UUID, want alias ID", id)
+ }
+ if info.ID != id {
+ t.Fatalf("ask info ID = %q, want %q", info.ID, id)
+ }
+ if !uuidFormatRx.MatchString(info.UUID) {
+ t.Fatalf("ask info UUID = %q, want valid UUID", info.UUID)
}
}
diff --git a/internal/askcli/command_info_add.go b/internal/askcli/command_info_add.go
index 5332e71..4fa0941 100644
--- a/internal/askcli/command_info_add.go
+++ b/internal/askcli/command_info_add.go
@@ -104,7 +104,12 @@ func (d Dispatcher) handleAdd(ctx context.Context, args []string, stdout, stderr
io.WriteString(stderr, "error: could not parse UUID from task creation output\n")
return 1, nil
}
- io.WriteString(stdout, uuid+"\n")
+ aliases, err := ensureTaskAliasesForUUIDs([]string{uuid})
+ if err != nil {
+ fmt.Fprintf(stderr, "error: failed to assign task alias: %v\n", err)
+ return 1, nil
+ }
+ io.WriteString(stdout, displayTaskAlias(uuid, aliases)+"\n")
return 0, nil
}
diff --git a/internal/askcli/command_info_add_test.go b/internal/askcli/command_info_add_test.go
index bd95de4..11bc451 100644
--- a/internal/askcli/command_info_add_test.go
+++ b/internal/askcli/command_info_add_test.go
@@ -211,7 +211,14 @@ func TestHandleInfo_MissingUUID_MultipleStartedTasks(t *testing.T) {
}
func TestHandleAdd_Success(t *testing.T) {
- // With rc.verbose=new-uuid, task add outputs "Created task <uuid>." directly.
+ now := useIsolatedTaskAliasCache(t)
+ writeTaskAliasCacheForTest(t, taskAliasCache{
+ NextID: 1,
+ Entries: []taskAliasCacheEntry{
+ {UUID: "existing-uuid", Alias: "0", CreatedAt: now},
+ },
+ })
+
d := NewDispatcher(&spyRunner{runFn: func(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.Writer) (int, error) {
io.WriteString(stdout, "Created task abc-123-def.")
return 0, nil
@@ -221,8 +228,36 @@ func TestHandleAdd_Success(t *testing.T) {
if code != 0 {
t.Fatalf("add code = %d, want 0", code)
}
- if !strings.Contains(stdout.String(), "abc-123-def") {
- t.Fatalf("output missing UUID: %s", stdout.String())
+ if got := strings.TrimSpace(stdout.String()); got != "1" {
+ t.Fatalf("stdout = %q, want alias 1", stdout.String())
+ }
+ cache := readTaskAliasCacheSnapshot(t)
+ entry := findTaskAliasEntry(t, cache, "abc-123-def")
+ if entry.Alias != "1" {
+ t.Fatalf("created task alias = %q, want 1", entry.Alias)
+ }
+}
+
+func TestHandleAdd_AliasAssignmentFailure(t *testing.T) {
+ oldRoot := taskAliasCacheRoot
+ taskAliasCacheRoot = func() (string, error) { return "", io.ErrUnexpectedEOF }
+ defer func() { taskAliasCacheRoot = oldRoot }()
+
+ d := NewDispatcher(&spyRunner{runFn: func(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.Writer) (int, error) {
+ io.WriteString(stdout, "Created task abc-123-def.")
+ return 0, nil
+ }})
+
+ var stdout, stderr bytes.Buffer
+ code, _ := d.Dispatch(context.Background(), []string{"add", "New task description"}, nil, &stdout, &stderr)
+ if code != 1 {
+ t.Fatalf("add code = %d, want 1", code)
+ }
+ if stdout.Len() != 0 {
+ t.Fatalf("stdout = %q, want empty output on alias assignment failure", stdout.String())
+ }
+ if !strings.Contains(stderr.String(), "failed to assign task alias") {
+ t.Fatalf("stderr = %q, want alias assignment failure", stderr.String())
}
}
diff --git a/internal/askcli/dispatch.go b/internal/askcli/dispatch.go
index 7cb217b..12407e3 100644
--- a/internal/askcli/dispatch.go
+++ b/internal/askcli/dispatch.go
@@ -88,7 +88,7 @@ func (d Dispatcher) Dispatch(ctx context.Context, args []string, stdin io.Reader
func (d Dispatcher) help(w io.Writer) (int, error) {
io.WriteString(w, "ask - task management CLI\n")
io.WriteString(w, "\nSubcommands:\n")
- io.WriteString(w, " ask add \"description\" Create a new task\n")
+ io.WriteString(w, " ask add \"description\" Create a new task and print its ID\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")