aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Moc <personal@cdatgoose.org>2026-05-17 01:35:00 +0200
committerDavid Moc <personal@cdatgoose.org>2026-05-17 01:35:00 +0200
commit2c0f6f7c6b34107d828d30e11d116ec24c934b1b (patch)
treec571ae0dc93fefb22c3bf5af481b72c28da89e92
parentb0d5cb5d9d3607add2932b03af50a2a6c18f1721 (diff)
Updated the readme and added a manpage and bash completion.HEADmaster
Signed-off-by: David Moc <personal@cdatgoose.org>
-rw-r--r--README.org70
-rwxr-xr-xbuild.sh101
-rw-r--r--tasker.198
-rw-r--r--tasker.bash90
4 files changed, 348 insertions, 11 deletions
diff --git a/README.org b/README.org
index 5f074bc..20b1d18 100644
--- a/README.org
+++ b/README.org
@@ -1,16 +1,72 @@
#+AUTHOR: David Moc
#+EMAIL: personal@cdatgoose.org
-
* Tasker
-Tasker is a simple task tracker (with built in fish completion) written in Haskell.
+
+Tasker is a simple task tracker written in Haskell.
+
+It includes optional shell completion support for:
+
+- fish
+- bash
* Install
-You can eiter use the `./build.sh` that installs into:
-- `/usr/local/bin/tasker`
-- `/usr/share/fish/vendor_completions.d/tasker.fish`
-Or just run:
+
+** Installer:
+
+#+begin_src shell
+ ./install.sh
+#+end_src
+
+This builds the executable with Cabal and installs it to:
+
+- =/usr/local/bin/tasker=
+
+The installer also detects your current shell and installs the matching completion file when available.
+
+For fish, it installs:
+
+- =/usr/share/fish/vendor_completions.d/tasker.fish=
+
+For bash, it installs:
+
+- =/usr/share/bash-completion/completions/tasker=
+
+If a manpage is available, it is installed to:
+
+- =/usr/local/share/man/man1/tasker.1=
+
+** Manual:
+
+#+begin_src shell
+ ./install.sh --shell fish
+ ./install.sh --shell bash
+ ./install.sh --shell all
+ ./install.sh --shell none
+#+end_src
+
+To skip installing the manpage:
+
+#+begin_src shell
+ ./install.sh --no-man
+#+end_src
+
+* Build only
+
+To build Tasker without installing it:
+
#+begin_src shell
cabal build exe:tasker
#+end_src
-and copy the file to your bin.
+
+You can locate the built executable with:
+
+#+begin_src shell
+ cabal list-bin exe:tasker
+#+end_src
+
+Then copy it manually to a directory on your =$PATH=, for example:
+
+#+begin_src shell
+ cp "$(cabal list-bin exe:tasker)" ~/.local/bin/tasker
+#+end_src
diff --git a/build.sh b/build.sh
index 128016e..e30ab7d 100755
--- a/build.sh
+++ b/build.sh
@@ -1,12 +1,105 @@
#!/usr/bin/env sh
set -e
+usage() {
+ echo "Usage: $0 [--shell fish|bash|none|all] [--no-man]"
+}
+
+requested_shell=
+install_man=true
+
+while [ "$#" -gt 0 ]; do
+ case "$1" in
+ --shell)
+ requested_shell=${2:-}
+ shift 2
+ ;;
+ --shell=*)
+ requested_shell=${1#--shell=}
+ shift
+ ;;
+ --no-man)
+ install_man=false
+ shift
+ ;;
+ -h|--help)
+ usage
+ exit 0
+ ;;
+ *)
+ echo "Unknown option: $1" >&2
+ usage >&2
+ exit 1
+ ;;
+ esac
+done
+
cabal build exe:tasker
bin=$(cabal list-bin exe:tasker)
-sudo install -Dm755 "$bin" /usr/local/bin/tasker
-sudo install -Dm644 tasker.fish /usr/share/fish/vendor_completions.d/tasker.fish
+sudo install -Dm755 "$bin" /usr/local/bin/tasker
+echo "Installed $bin -> /usr/local/bin/tasker"
+
+detect_shell() {
+ if [ -n "$requested_shell" ]; then
+ echo "$requested_shell"
+ else
+ basename "${SHELL:-}"
+ fi
+}
+
+install_fish_completion() {
+ if [ -f tasker.fish ]; then
+ sudo install -Dm644 tasker.fish \
+ /usr/share/fish/vendor_completions.d/tasker.fish
+ echo "Installed tasker.fish -> /usr/share/fish/vendor_completions.d/tasker.fish"
+ else
+ echo "Skipped fish completion: tasker.fish not found"
+ fi
+}
+
+install_bash_completion() {
+ if [ -f tasker.bash ]; then
+ sudo install -Dm644 tasker.bash \
+ /usr/share/bash-completion/completions/tasker
+ echo "Installed tasker.bash -> /usr/share/bash-completion/completions/tasker"
+ else
+ echo "Skipped bash completion: tasker.bash not found"
+ fi
+}
+
+install_manpage() {
+ if [ -f tasker.1 ]; then
+ sudo install -Dm644 tasker.1 \
+ /usr/local/share/man/man1/tasker.1
+ sudo mandb 2>/dev/null || true
+ echo "Installed tasker.1 -> /usr/local/share/man/man1/tasker.1"
+ else
+ echo "Skipped manpage: tasker.1 not found"
+ fi
+}
+
+case "$(detect_shell)" in
+ fish)
+ install_fish_completion
+ ;;
+ bash)
+ install_bash_completion
+ ;;
+ all)
+ install_fish_completion
+ install_bash_completion
+ ;;
+ none)
+ echo "Skipped shell completions"
+ ;;
+ *)
+ echo "Unsupported shell: ${requested_shell:-${SHELL:-unset}}"
+ echo "Skipped shell completions"
+ ;;
+esac
-echo "Installed $bin -> /usr/local/bin/tasker"
-echo "Installed tasker.fish -> /usr/share/fish/vendor_completions.d/tasker.fish"
+if [ "$install_man" = true ]; then
+ install_manpage
+fi
diff --git a/tasker.1 b/tasker.1
new file mode 100644
index 0000000..7c7f90d
--- /dev/null
+++ b/tasker.1
@@ -0,0 +1,98 @@
+.TH TASKER 1
+.SH NAME
+tasker \- manage local task directories
+.SH SYNOPSIS
+.B tasker
+.RI COMMAND
+.RI [ OPTIONS ]
+
+.SH DESCRIPTION
+.B tasker
+manages tasks stored as timestamp-named directories.
+
+Task directories are searched under
+.B ./tasks
+when that directory exists, otherwise under the current directory.
+
+.SH COMMANDS
+.TP
+.B new
+Create a new task.
+.TP
+.B list
+List tasks.
+.TP
+.B set
+Update a task.
+.TP
+.B delete
+Delete a task.
+
+.SH OPTIONS
+.SS new
+.TP
+.BI \-p " PRIORITY"
+Set priority as an integer.
+.TP
+.BI \-d " DESCRIPTION"
+Set task description.
+
+.SS list
+.TP
+.BR \-s ", " \-\-status " " \fISTATUS\fR
+Filter by status. One of:
+.BR OPEN ,
+.BR IN_PROGRESS ,
+.BR CLOSED .
+.TP
+.BR \-p ", " \-\-priority " " \fIPRIORITY\fR
+Filter by exact priority.
+.TP
+.BI \-\-min-priority " PRIORITY"
+Filter by minimum priority.
+.TP
+.BR \-c ", " \-\-contains " " \fITEXT\fR
+Filter by text.
+
+.SS set
+.TP
+.BI \-n ", " \-\-name " NAME"
+Rename the task.
+.TP
+.BR \-s ", " \-\-status " " \fISTATUS\fR
+Set task status. One of:
+.BR OPEN ,
+.BR IN_PROGRESS ,
+.BR CLOSED .
+.TP
+.BR \-p ", " \-\-priority " " \fIPRIORITY\fR
+Set task priority.
+.TP
+.BR \-d ", " \-\-desc " " \fIDESCRIPTION\fR
+Set task description.
+
+.SH EXAMPLES
+.TP
+Create a task:
+.B tasker new -p 3 -d "Write docs"
+.TP
+List open tasks:
+.B tasker list --status OPEN
+.TP
+Update a task:
+.B tasker set "24-01-30 12:00:00" --status CLOSED
+.TP
+Delete a task:
+.B tasker delete "24-01-30 12:00:00"
+
+.SH FILES
+.TP
+.B ./tasks/
+Preferred task root when present.
+.TP
+.B .
+Fallback task root.
+
+.SH SEE ALSO
+.BR bash (1),
+.BR fish (1)
diff --git a/tasker.bash b/tasker.bash
new file mode 100644
index 0000000..6b13ae8
--- /dev/null
+++ b/tasker.bash
@@ -0,0 +1,90 @@
+_tasker_task_dirs() {
+ local root d name
+ if [[ -d "$PWD/tasks" ]]; then
+ root="$PWD/tasks"
+ else
+ root="$PWD"
+ fi
+
+ shopt -s nullglob
+ for d in "$root"/*/; do
+ name="${d%/}"
+ name="${name##*/}"
+ if [[ "$name" =~ ^[0-9]{2}-[0-9]{2}-[0-9]{2}\ [0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?$ ]]; then
+ printf '%s\n' "$name"
+ fi
+ done
+}
+
+_tasker_completion() {
+ local cur prev words cword subcommand
+ COMPREPLY=()
+
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ prev="${COMP_WORDS[COMP_CWORD-1]}"
+
+ local commands="new list set delete"
+
+ subcommand=""
+ for word in "${COMP_WORDS[@]:1}"; do
+ case "$word" in
+ new|list|set|delete)
+ subcommand="$word"
+ break
+ ;;
+ esac
+ done
+
+ if [[ -z "$subcommand" ]]; then
+ COMPREPLY=( $(compgen -W "$commands" -- "$cur") )
+ return 0
+ fi
+
+ case "$subcommand" in
+ new)
+ case "$prev" in
+ -p|-d)
+ return 0
+ ;;
+ esac
+ COMPREPLY=( $(compgen -W "-p -d" -- "$cur") )
+ ;;
+
+ list)
+ case "$prev" in
+ -s|--status)
+ COMPREPLY=( $(compgen -W "OPEN IN_PROGRESS CLOSED" -- "$cur") )
+ return 0
+ ;;
+ -p|--priority|--min-priority|-c|--contains)
+ return 0
+ ;;
+ esac
+ COMPREPLY=( $(compgen -W "-s --status -p --priority --min-priority -c --contains" -- "$cur") )
+ ;;
+
+ set)
+ case "$prev" in
+ -s|--status)
+ COMPREPLY=( $(compgen -W "OPEN IN_PROGRESS CLOSED" -- "$cur") )
+ return 0
+ ;;
+ -n|--name|-p|--priority|-d|--desc)
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $(compgen -W "-n --name -s --status -p --priority -d --desc" -- "$cur") )
+ else
+ COMPREPLY=( $(compgen -W "$(_tasker_task_dirs)" -- "$cur") )
+ fi
+ ;;
+
+ delete)
+ COMPREPLY=( $(compgen -W "$(_tasker_task_dirs)" -- "$cur") )
+ ;;
+ esac
+}
+
+complete -F _tasker_completion tasker