summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-26 22:15:39 +0200
committerPaul Buetow <paul@buetow.org>2026-03-26 22:15:39 +0200
commitf6436fe4cfdd989d95607931314a7c47147ee1e2 (patch)
tree835ccadab7ae9bfc65832848e6394be064e79070
parentc8108deb77f1872e6a55925d5abf9c0ae12813c6 (diff)
ask: add fish UUID completions
-rw-r--r--assets/ask.fish71
-rw-r--r--docs/fish-completion.md3
-rw-r--r--internal/askcli/completion.go134
-rw-r--r--internal/askcli/completion_test.go6
4 files changed, 204 insertions, 10 deletions
diff --git a/assets/ask.fish b/assets/ask.fish
index e4d4654..6c5f904 100644
--- a/assets/ask.fish
+++ b/assets/ask.fish
@@ -37,6 +37,64 @@ function __ask_in_dep_context
test $seen_dep -eq 1
end
+function __ask_in_uuid_context
+ set -l tokens (commandline -opc)
+ set -l positional
+ for token in $tokens[2..-1]
+ if string match -qr '^-' -- $token
+ continue
+ end
+ set -a positional $token
+ end
+ if test (count $positional) -eq 0
+ return 1
+ end
+ if test (count $positional) -gt 2
+ return 1
+ end
+ switch $positional[1]
+ case info annotate start stop done priority tag modify denotate delete
+ return 0
+ case '*'
+ return 1
+ end
+ return 1
+end
+
+function __ask_in_dep_uuid_context
+ set -l tokens (commandline -opc)
+ set -l positional
+ for token in $tokens[2..-1]
+ if string match -qr '^-' -- $token
+ continue
+ end
+ set -a positional $token
+ end
+ if test (count $positional) -lt 2
+ return 1
+ end
+ if test $positional[1] != dep
+ return 1
+ end
+ switch $positional[2]
+ case add rm
+ if test (count $positional) -le 4
+ return 0
+ end
+ case list
+ if test (count $positional) -le 3
+ return 0
+ end
+ case '*'
+ return 1
+ end
+ return 1
+end
+
+function __ask_task_uuids
+ command ask all --json 2>/dev/null | string match -r -a -g '"uuid":"([^"]+)"'
+end
+
complete -c ask -f
complete -c ask -s j -l json -d 'Emit JSON output'
complete -c ask -n '__ask_needs_root_completion' -a 'add' -d 'Create a new task'
@@ -59,3 +117,16 @@ complete -c ask -n '__ask_needs_root_completion' -a 'help' -d 'Show help'
complete -c ask -n '__ask_in_dep_context' -a 'add' -d 'Add a dependency'
complete -c ask -n '__ask_in_dep_context' -a 'rm' -d 'Remove a dependency'
complete -c ask -n '__ask_in_dep_context' -a 'list' -d 'List dependencies'
+complete -c ask -n '__ask_in_uuid_context' -a '(__ask_task_uuids)' -d 'Show task details'
+complete -c ask -n '__ask_in_uuid_context' -a '(__ask_task_uuids)' -d 'Add an annotation'
+complete -c ask -n '__ask_in_uuid_context' -a '(__ask_task_uuids)' -d 'Start a task'
+complete -c ask -n '__ask_in_uuid_context' -a '(__ask_task_uuids)' -d 'Stop a task'
+complete -c ask -n '__ask_in_uuid_context' -a '(__ask_task_uuids)' -d 'Mark a task complete'
+complete -c ask -n '__ask_in_uuid_context' -a '(__ask_task_uuids)' -d 'Set priority'
+complete -c ask -n '__ask_in_uuid_context' -a '(__ask_task_uuids)' -d 'Add or remove a tag'
+complete -c ask -n '__ask_in_uuid_context' -a '(__ask_task_uuids)' -d 'Modify task fields'
+complete -c ask -n '__ask_in_uuid_context' -a '(__ask_task_uuids)' -d 'Remove an annotation'
+complete -c ask -n '__ask_in_uuid_context' -a '(__ask_task_uuids)' -d 'Delete a task'
+complete -c ask -n '__ask_in_dep_uuid_context' -a '(__ask_task_uuids)' -d 'Add a dependency'
+complete -c ask -n '__ask_in_dep_uuid_context' -a '(__ask_task_uuids)' -d 'Remove a dependency'
+complete -c ask -n '__ask_in_dep_uuid_context' -a '(__ask_task_uuids)' -d 'List dependencies'
diff --git a/docs/fish-completion.md b/docs/fish-completion.md
index 116a0a6..b37b79d 100644
--- a/docs/fish-completion.md
+++ b/docs/fish-completion.md
@@ -3,7 +3,8 @@
Hexai ships a Fish completion file for the `ask` task-management CLI at [`assets/ask.fish`](../assets/ask.fish).
It completes the top-level `ask` subcommands and the nested `ask dep` operations.
-The script also preserves the global `--json` flag.
+It also completes task UUIDs for UUID-taking commands by reading the current project from `ask all --json`.
+The script preserves the global `--json` flag.
Install it into Fish's completion directory:
diff --git a/internal/askcli/completion.go b/internal/askcli/completion.go
index f787849..28ad306 100644
--- a/internal/askcli/completion.go
+++ b/internal/askcli/completion.go
@@ -35,11 +35,61 @@ var askDepCompletionItems = []fishCompletionItem{
{name: "list", description: "List dependencies"},
}
+var askUUIDCompletionItems = []fishCompletionItem{
+ {name: "info", description: "Show task details"},
+ {name: "annotate", description: "Add an annotation"},
+ {name: "start", description: "Start a task"},
+ {name: "stop", description: "Stop a task"},
+ {name: "done", description: "Mark a task complete"},
+ {name: "priority", description: "Set priority"},
+ {name: "tag", description: "Add or remove a tag"},
+ {name: "modify", description: "Modify task fields"},
+ {name: "denotate", description: "Remove an annotation"},
+ {name: "delete", description: "Delete a task"},
+}
+
+var askDepUUIDCompletionItems = []fishCompletionItem{
+ {name: "add", description: "Add a dependency"},
+ {name: "rm", description: "Remove a dependency"},
+ {name: "list", description: "List dependencies"},
+}
+
func FishCompletion() string {
var b strings.Builder
+ writeFishPreamble(&b)
+ writeFishContextFunctions(&b)
+ writeFishTaskUUIDFunction(&b)
+ b.WriteString("complete -c ask -f\n")
+ b.WriteString("complete -c ask -s j -l json -d 'Emit JSON output'\n")
+ for _, item := range askRootCompletionItems {
+ writeFishCompletionLine(&b, "__ask_needs_root_completion", item)
+ }
+ for _, item := range askDepCompletionItems {
+ writeFishCompletionLine(&b, "__ask_in_dep_context", item)
+ }
+ for _, item := range askUUIDCompletionItems {
+ writeFishUUIDCompletionLine(&b, "__ask_in_uuid_context", item.description)
+ }
+ for _, item := range askDepUUIDCompletionItems {
+ writeFishUUIDCompletionLine(&b, "__ask_in_dep_uuid_context", item.description)
+ }
+ return b.String()
+}
+
+func writeFishPreamble(b *strings.Builder) {
b.WriteString("# Fish completion for ask.\n")
b.WriteString("# Install as ~/.config/fish/completions/ask.fish or")
b.WriteString(" $XDG_CONFIG_HOME/fish/completions/ask.fish.\n\n")
+}
+
+func writeFishContextFunctions(b *strings.Builder) {
+ writeFishNeedsRootCompletionFunction(b)
+ writeFishDepContextFunction(b)
+ writeFishUUIDContextFunction(b)
+ writeFishDepUUIDContextFunction(b)
+}
+
+func writeFishNeedsRootCompletionFunction(b *strings.Builder) {
b.WriteString("function __ask_needs_root_completion\n")
b.WriteString(" set -l tokens (commandline -opc)\n")
b.WriteString(" if test (count $tokens) -le 1\n")
@@ -52,6 +102,9 @@ func FishCompletion() string {
b.WriteString(" end\n")
b.WriteString(" return 0\n")
b.WriteString("end\n\n")
+}
+
+func writeFishDepContextFunction(b *strings.Builder) {
b.WriteString("function __ask_in_dep_context\n")
b.WriteString(" set -l tokens (commandline -opc)\n")
b.WriteString(" if test (count $tokens) -lt 2\n")
@@ -74,15 +127,70 @@ func FishCompletion() string {
b.WriteString(" end\n")
b.WriteString(" test $seen_dep -eq 1\n")
b.WriteString("end\n\n")
- b.WriteString("complete -c ask -f\n")
- b.WriteString("complete -c ask -s j -l json -d 'Emit JSON output'\n")
- for _, item := range askRootCompletionItems {
- writeFishCompletionLine(&b, "__ask_needs_root_completion", item)
- }
- for _, item := range askDepCompletionItems {
- writeFishCompletionLine(&b, "__ask_in_dep_context", item)
- }
- return b.String()
+}
+
+func writeFishUUIDContextFunction(b *strings.Builder) {
+ b.WriteString("function __ask_in_uuid_context\n")
+ b.WriteString(" set -l tokens (commandline -opc)\n")
+ b.WriteString(" set -l positional\n")
+ b.WriteString(" for token in $tokens[2..-1]\n")
+ b.WriteString(" if string match -qr '^-' -- $token\n")
+ b.WriteString(" continue\n")
+ b.WriteString(" end\n")
+ b.WriteString(" set -a positional $token\n")
+ b.WriteString(" end\n")
+ b.WriteString(" if test (count $positional) -eq 0\n")
+ b.WriteString(" return 1\n")
+ b.WriteString(" end\n")
+ b.WriteString(" if test (count $positional) -gt 2\n")
+ b.WriteString(" return 1\n")
+ b.WriteString(" end\n")
+ b.WriteString(" switch $positional[1]\n")
+ b.WriteString(" case info annotate start stop done priority tag modify denotate delete\n")
+ b.WriteString(" return 0\n")
+ b.WriteString(" case '*'\n")
+ b.WriteString(" return 1\n")
+ b.WriteString(" end\n")
+ b.WriteString(" return 1\n")
+ b.WriteString("end\n\n")
+}
+
+func writeFishDepUUIDContextFunction(b *strings.Builder) {
+ b.WriteString("function __ask_in_dep_uuid_context\n")
+ b.WriteString(" set -l tokens (commandline -opc)\n")
+ b.WriteString(" set -l positional\n")
+ b.WriteString(" for token in $tokens[2..-1]\n")
+ b.WriteString(" if string match -qr '^-' -- $token\n")
+ b.WriteString(" continue\n")
+ b.WriteString(" end\n")
+ b.WriteString(" set -a positional $token\n")
+ b.WriteString(" end\n")
+ b.WriteString(" if test (count $positional) -lt 2\n")
+ b.WriteString(" return 1\n")
+ b.WriteString(" end\n")
+ b.WriteString(" if test $positional[1] != dep\n")
+ b.WriteString(" return 1\n")
+ b.WriteString(" end\n")
+ b.WriteString(" switch $positional[2]\n")
+ b.WriteString(" case add rm\n")
+ b.WriteString(" if test (count $positional) -le 4\n")
+ b.WriteString(" return 0\n")
+ b.WriteString(" end\n")
+ b.WriteString(" case list\n")
+ b.WriteString(" if test (count $positional) -le 3\n")
+ b.WriteString(" return 0\n")
+ b.WriteString(" end\n")
+ b.WriteString(" case '*'\n")
+ b.WriteString(" return 1\n")
+ b.WriteString(" end\n")
+ b.WriteString(" return 1\n")
+ b.WriteString("end\n\n")
+}
+
+func writeFishTaskUUIDFunction(b *strings.Builder) {
+ b.WriteString("function __ask_task_uuids\n")
+ b.WriteString(" command ask all --json 2>/dev/null | string match -r -a -g '\"uuid\":\"([^\"]+)\"'\n")
+ b.WriteString("end\n\n")
}
func writeFishCompletionLine(b *strings.Builder, condition string, item fishCompletionItem) {
@@ -94,3 +202,11 @@ func writeFishCompletionLine(b *strings.Builder, condition string, item fishComp
b.WriteString(strings.ReplaceAll(item.description, "'", "\\'"))
b.WriteString("'\n")
}
+
+func writeFishUUIDCompletionLine(b *strings.Builder, condition, description string) {
+ b.WriteString("complete -c ask -n '")
+ b.WriteString(condition)
+ b.WriteString("' -a '(__ask_task_uuids)' -d '")
+ b.WriteString(strings.ReplaceAll(description, "'", "\\'"))
+ b.WriteString("'\n")
+}
diff --git a/internal/askcli/completion_test.go b/internal/askcli/completion_test.go
index e717575..439e483 100644
--- a/internal/askcli/completion_test.go
+++ b/internal/askcli/completion_test.go
@@ -35,6 +35,12 @@ func TestFishCompletion_IncludesCommandsAndExcludesExport(t *testing.T) {
"complete -c ask -n '__ask_in_dep_context' -a 'add' -d 'Add a dependency'",
"complete -c ask -n '__ask_in_dep_context' -a 'rm' -d 'Remove a dependency'",
"complete -c ask -n '__ask_in_dep_context' -a 'list' -d 'List dependencies'",
+ "function __ask_task_uuids",
+ "command ask all --json 2>/dev/null",
+ "complete -c ask -n '__ask_in_uuid_context' -a '(__ask_task_uuids)' -d 'Show task details'",
+ "complete -c ask -n '__ask_in_dep_uuid_context' -a '(__ask_task_uuids)' -d 'Add a dependency'",
+ "complete -c ask -n '__ask_in_dep_uuid_context' -a '(__ask_task_uuids)' -d 'Remove a dependency'",
+ "complete -c ask -n '__ask_in_dep_uuid_context' -a '(__ask_task_uuids)' -d 'List dependencies'",
} {
if !strings.Contains(script, line) {
t.Fatalf("script missing dep completion line %q", line)