aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulio Capote <jcapote@gmail.com>2022-07-14 03:40:24 +0000
committerJulio Capote <jcapote@gmail.com>2022-07-14 03:40:24 +0000
commit891f946e86f8a0911f67983d52fccacc9f8ebbad (patch)
tree36b032deb891f4adf5c047b255eb0ca6da275fef
downloadrecit-891f946e86f8a0911f67983d52fccacc9f8ebbad.tar.gz
initial
-rw-r--r--LICENSE20
-rw-r--r--README.md41
l---------bin/recit1
-rw-r--r--completions/recit.bash14
-rw-r--r--completions/recit.zsh19
-rw-r--r--lib/loader12
-rw-r--r--lib/recfile_template19
-rwxr-xr-xlibexec/recit41
-rwxr-xr-xlibexec/recit-add-entry46
-rwxr-xr-xlibexec/recit-add-project9
-rwxr-xr-xlibexec/recit-commands42
-rwxr-xr-xlibexec/recit-completions14
-rwxr-xr-xlibexec/recit-edit16
-rwxr-xr-xlibexec/recit-edit-entry32
-rwxr-xr-xlibexec/recit-help104
-rwxr-xr-xlibexec/recit-init94
-rwxr-xr-xlibexec/recit-open-projects8
-rwxr-xr-xlibexec/recit-setup12
-rwxr-xr-xlibexec/recit-sh-shell4
-rwxr-xr-xlibexec/recit-today13
-rwxr-xr-xlibexec/recit-tomorrow11
-rwxr-xr-xlibexec/recit-yesterday11
-rw-r--r--share/recit/example4
23 files changed, 587 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..ef54b21
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2012-2021 Sam Stephenson, Nick Quaranto
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..825c21a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,41 @@
+# recit
+
+personal productivity system backed by [recfiles](https://en.wikipedia.org/wiki/Recfiles)
+
+# dependencies
+
+* [recutils](https://www.gnu.org/software/recutils/)
+* uuidgen
+* any perl
+
+# workflows
+
+You can use `recit` to track things like:
+
+* What needs to be done today?
+* What did you do yesterday?
+* What did you talk about in that 1:1 a few weeks ago?
+* Where did you leave off on that project you started last year?
+* What did you do last year?
+
+# usage
+
+`recit setup` gets you started
+
+## add some entries
+
+`recit add-entry` opens `$EDITOR` to let you add an entry at that moment in time, which is the same
+as `recit add-entry now` or `recit add-entry today`. If the notes are short, you can just
+pass it as a 3rd argument, like `recit add-entry now "this is the entry"`
+
+You can also add entries in the future by running `recit add-entry tomorrow` or any date like `recit add-entry 2022-07-22`.
+
+
+## view entries
+
+`recit today` or `recit tomorrow` (also `recit yesterday`)
+
+## edit entries
+
+you'll notice that `recit add-entry` returns a UUID, you can pass this UUID to `recit edit-entry`
+to edit the entry \ No newline at end of file
diff --git a/bin/recit b/bin/recit
new file mode 120000
index 0000000..b4e740f
--- /dev/null
+++ b/bin/recit
@@ -0,0 +1 @@
+../libexec/recit \ No newline at end of file
diff --git a/completions/recit.bash b/completions/recit.bash
new file mode 100644
index 0000000..76ab736
--- /dev/null
+++ b/completions/recit.bash
@@ -0,0 +1,14 @@
+_recit() {
+ COMPREPLY=()
+ local word="${COMP_WORDS[COMP_CWORD]}"
+
+ if [ "$COMP_CWORD" -eq 1 ]; then
+ COMPREPLY=( $(compgen -W "$(recit commands)" -- "$word") )
+ else
+ local command="${COMP_WORDS[1]}"
+ local completions="$(recit completions "$command")"
+ COMPREPLY=( $(compgen -W "$completions" -- "$word") )
+ fi
+}
+
+complete -F _recit recit
diff --git a/completions/recit.zsh b/completions/recit.zsh
new file mode 100644
index 0000000..db7a043
--- /dev/null
+++ b/completions/recit.zsh
@@ -0,0 +1,19 @@
+if [[ ! -o interactive ]]; then
+ return
+fi
+
+compctl -K _recit recit
+
+_recit() {
+ local word words completions
+ read -cA words
+ word="${words[2]}"
+
+ if [ "${#words}" -eq 2 ]; then
+ completions="$(recit commands)"
+ else
+ completions="$(recit completions "${word}")"
+ fi
+
+ reply=("${(ps:\n:)completions}")
+}
diff --git a/lib/loader b/lib/loader
new file mode 100644
index 0000000..9b01d2a
--- /dev/null
+++ b/lib/loader
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+function load_recit {
+ local recfile_location
+ if [[ -f "$HOME/.recit.location" ]]; then
+ recfile_location=$(cat "$HOME/.recit.location")
+ else
+ echo "$HOME/.recit.location not found"
+ exit 1
+ fi
+ echo "$recfile_location"
+} \ No newline at end of file
diff --git a/lib/recfile_template b/lib/recfile_template
new file mode 100644
index 0000000..674dc7b
--- /dev/null
+++ b/lib/recfile_template
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+function recfile_template {
+ echo "# recit schema version v0.0.1
+%rec: Project
+%key: Id
+
+Id: example-project
+Status: open
+Notes: blah blah
+
+%rec: Entry
+%key: Id
+%type: Time date
+%auto: Time
+%type: ProjectRef rec Project
+%sort: Time
+ "
+} \ No newline at end of file
diff --git a/libexec/recit b/libexec/recit
new file mode 100755
index 0000000..8573b1f
--- /dev/null
+++ b/libexec/recit
@@ -0,0 +1,41 @@
+#!/usr/bin/env bash
+set -e
+
+resolve_link() {
+ $(type -p greadlink readlink | head -1) "$1"
+}
+
+abs_dirname() {
+ local cwd="$(pwd)"
+ local path="$1"
+
+ while [ -n "$path" ]; do
+ cd "${path%/*}"
+ local name="${path##*/}"
+ path="$(resolve_link "$name" || true)"
+ done
+
+ pwd
+ cd "$cwd"
+}
+
+libexec_path="$(abs_dirname "$0")"
+export _RECIT_ROOT="$(abs_dirname "$libexec_path")"
+export PATH="${libexec_path}:$PATH"
+
+command="$1"
+case "$command" in
+"" | "-h" | "--help" )
+ exec recit-help
+ ;;
+* )
+ command_path="$(command -v "recit-$command" || true)"
+ if [ ! -x "$command_path" ]; then
+ echo "recit: no such command \`$command'" >&2
+ exit 1
+ fi
+
+ shift
+ exec "$command_path" "$@"
+ ;;
+esac
diff --git a/libexec/recit-add-entry b/libexec/recit-add-entry
new file mode 100755
index 0000000..ae0be2f
--- /dev/null
+++ b/libexec/recit-add-entry
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+# Usage: recit add-entry [time|"now"] "notes" (on 2nd argument opens $EDITOR)
+
+set -e
+
+# shellcheck source=/dev/null
+source lib/loader
+
+recfile=$(load_recit)
+
+uuid=$(uuidgen)
+
+notes=""
+query_time=""
+
+if [[ -z "$1" ]]; then
+ query_time="now"
+else
+ query_time="$1"
+fi
+
+if [[ -z "$2" ]]; then
+
+ if [[ -z ${EDITOR+x} ]]; then
+ echo "$EDITOR is not defined please pass a message"
+ exit 1
+ fi
+
+ tmpfile="$(mktemp)"
+ command $EDITOR "$tmpfile"
+ notes=$(cat "$tmpfile")
+ rm "$tmpfile"
+else
+ notes=$2
+fi
+
+if [[ "$query_time" == "now" || "$query_time" == "today" ]]; then
+ fmt_date=$(date '+%Y-%m-%d %r')
+elif [[ "$query_time" == "tomorrow" ]]; then
+ fmt_date=$(perl -e "use POSIX qw(strftime); print strftime('%Y-%m-%d', localtime(time + 86400)), qq(\n);")
+else
+ fmt_date=$query_time
+fi
+
+recins --verbose -f Id -v "$uuid" -f Time -v "$fmt_date" -f Notes -v "$notes" -t Entry "${recfile}"
+echo "$uuid" \ No newline at end of file
diff --git a/libexec/recit-add-project b/libexec/recit-add-project
new file mode 100755
index 0000000..25627bc
--- /dev/null
+++ b/libexec/recit-add-project
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+# Usage: recit add-project "notes"
+
+# shellcheck source=/dev/null
+source lib/loader
+
+recfile=$(load_recit)
+
+recins --verbose -f Id -v "$1" -t Project "${recfile}" \ No newline at end of file
diff --git a/libexec/recit-commands b/libexec/recit-commands
new file mode 100755
index 0000000..edc18dd
--- /dev/null
+++ b/libexec/recit-commands
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+# Usage: recit commands
+# Summary: List all recit commands
+# Help: This command is mostly used for autocompletion in various shells, and for `recit help`.
+# Also, this command helps find commands that are named the same as potentially builtin shell commands (which, cd, etc)
+
+set -e
+
+# Provide recit completions
+if [ "$1" = "--complete" ]; then
+ echo --sh
+ echo --no-sh
+ exit
+fi
+
+if [ "$1" = "--sh" ]; then
+ sh=1
+ shift
+elif [ "$1" = "--no-sh" ]; then
+ nosh=1
+ shift
+fi
+
+shopt -s nullglob
+
+{ for path in ${PATH//:/$'\n'}; do
+ for command in "${path}/recit-"*; do
+ command="${command##${path}/recit-}"
+ if [ -n "$sh" ]; then
+ if [ ${command:0:3} = "sh-" ]; then
+ echo ${command##sh-}
+ fi
+ elif [ -n "$nosh" ]; then
+ if [ ${command:0:3} != "sh-" ]; then
+ echo ${command##sh-}
+ fi
+ else
+ echo ${command##sh-}
+ fi
+ done
+ done
+} | sort | uniq
diff --git a/libexec/recit-completions b/libexec/recit-completions
new file mode 100755
index 0000000..2384dc3
--- /dev/null
+++ b/libexec/recit-completions
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+set -e
+
+COMMAND="$1"
+if [ -z "$COMMAND" ]; then
+ echo "usage: recit completions COMMAND [arg1 arg2...]" >&2
+ exit 1
+fi
+
+COMMAND_PATH="$(command -v "recit-$COMMAND")"
+if grep -i "^# provide recit completions" "$COMMAND_PATH" >/dev/null; then
+ shift
+ exec "$COMMAND_PATH" --complete "$@"
+fi
diff --git a/libexec/recit-edit b/libexec/recit-edit
new file mode 100755
index 0000000..f105bdc
--- /dev/null
+++ b/libexec/recit-edit
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+# Usage: recit edit
+
+set -e
+
+# shellcheck source=/dev/null
+source lib/loader
+
+recfile=$(load_recit)
+
+if [[ -z ${EDITOR+x} ]]; then
+ echo "$EDITOR is not defined please pass a message"
+ exit 1
+fi
+
+command $EDITOR "$recfile" \ No newline at end of file
diff --git a/libexec/recit-edit-entry b/libexec/recit-edit-entry
new file mode 100755
index 0000000..0636bc6
--- /dev/null
+++ b/libexec/recit-edit-entry
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+# Usage: recit edit-entry uuid
+
+set -e
+
+# shellcheck source=/dev/null
+source lib/loader
+
+recfile=$(load_recit)
+
+uuid=$1
+
+notes=$(recsel -e "Id = '$uuid'" -t Entry -P Notes "${recfile}")
+time=$(recsel -e "Id = '$uuid'" -t Entry -P Time "${recfile}")
+
+if [[ -z $notes ]]; then
+ echo "record not found"
+ exit 1
+fi
+
+if [[ -z ${EDITOR+x} ]]; then
+ echo "$EDITOR is not defined please pass a message"
+ exit 1
+fi
+
+tmpfile="$(mktemp)"
+echo "$notes" > $tmpfile
+command $EDITOR "$tmpfile"
+notes=$(cat "$tmpfile")
+rm "$tmpfile"
+
+recins -e "Id = '$uuid'" -t Entry -f Id -v "$uuid" -f Notes -v "$notes" -f Time -v "$time" "${recfile}"
diff --git a/libexec/recit-help b/libexec/recit-help
new file mode 100755
index 0000000..1c6371b
--- /dev/null
+++ b/libexec/recit-help
@@ -0,0 +1,104 @@
+#!/usr/bin/env bash
+set -e
+
+print_summaries() {
+ local commands=()
+ local summaries=()
+ local longest_command=0
+ local command
+
+ for command in $(recit-commands); do
+ local file="$(command_path "$command")"
+ if [ ! -h "$file" ]; then
+ local summary="$(summary "$file")"
+ if [ -n "$summary" ]; then
+ commands["${#commands[@]}"]="$command"
+ summaries["${#summaries[@]}"]="$summary"
+
+ if [ "${#command}" -gt "$longest_command" ]; then
+ longest_command="${#command}"
+ fi
+ fi
+ fi
+ done
+
+ local index
+ local columns="$(tput cols)"
+ local summary_length=$(( $columns - $longest_command - 5 ))
+
+ for (( index=0; index < ${#commands[@]}; index++ )); do
+ printf " %-${longest_command}s %s\n" "${commands[$index]}" \
+ "$(truncate "$summary_length" "${summaries[$index]}")"
+ done
+}
+
+print_help() {
+ local file="$1"
+ local usage="$(usage "$file")"
+
+ if [ -n "$usage" ]; then
+ echo "$usage"
+
+ local summary="$(summary "$file")"
+ [ -n "$summary" ] && echo "Summary: $summary"
+
+ local help="$(help "$file")"
+ [ -n "$help" ] && echo && echo "$help"
+ else
+ echo "Sorry, this command isn't documented yet."
+ fi
+}
+
+command_path() {
+ command -v "recit-$command" || command -v "recit-sh-$command" || true
+}
+
+summary() {
+ sed -n "s/^# Summary: \(.*\)/\1/p" "$1"
+}
+
+usage() {
+ sed -n "s/^# \(Usage: .*\)/\1/p" "$1"
+}
+
+help() {
+ awk '/^[^#]/{p=0} /^# Help:/{p=1} p' "$1" | sed "s/^# Help: //;s/^# //;s/^#//"
+}
+
+truncate() {
+ local max_length="$1"
+ local string="$2"
+
+ if [ "${#string}" -gt "$max_length" ]; then
+ local length=$(( $max_length - 3 ))
+ echo "${string:0:$length}..."
+ else
+ echo "$string"
+ fi
+}
+
+# Provide recit completions
+if [ "$1" = "--complete" ]; then
+ exec "recit-commands"
+ exit
+fi
+
+command="$1"
+case "$command" in
+"") echo "Usage: recit <command> [<args>]
+
+Some useful recit commands are:
+$(print_summaries)
+
+See 'recit help <command>' for information on a specific command."
+;;
+*)
+ file="$(command_path "$command")"
+
+ if [ -n "$file" ]; then
+ print_help "$file"
+ else
+ echo "recit: no such command \`$command'" >&2
+ exit 1
+ fi
+esac
diff --git a/libexec/recit-init b/libexec/recit-init
new file mode 100755
index 0000000..a5df7d1
--- /dev/null
+++ b/libexec/recit-init
@@ -0,0 +1,94 @@
+#!/usr/bin/env bash
+set -e
+
+print=""
+if [ "$1" = "-" ]; then
+ print=1
+ shift
+fi
+
+shell="$1"
+if [ -z "$shell" ]; then
+ shell="$(basename "$SHELL")"
+fi
+
+resolve_link() {
+ $(type -p greadlink readlink | head -1) $1
+}
+
+abs_dirname() {
+ local cwd="$(pwd)"
+ local path="$1"
+
+ while [ -n "$path" ]; do
+ cd "${path%/*}"
+ local name="${path##*/}"
+ path="$(resolve_link "$name" || true)"
+ done
+
+ pwd
+ cd "$cwd"
+}
+
+root="$(abs_dirname "$0")/.."
+
+if [ -z "$print" ]; then
+ case "$shell" in
+ bash )
+ profile='~/.bash_profile'
+ ;;
+ zsh )
+ profile='~/.zshenv'
+ ;;
+ * )
+ profile='your profile'
+ ;;
+ esac
+
+ { echo "# Load recit automatically by adding"
+ echo "# the following to ${profile}:"
+ echo
+ echo "eval \"\$(${_RECIT_ROOT}/bin/recit init -)\""
+ echo
+ } >&2
+
+ exit 1
+fi
+
+echo "export PATH=\"\${PATH}:${_RECIT_ROOT}/bin\""
+
+case "$shell" in
+bash | zsh )
+ echo "source \"$root/completions/recit.${shell}\""
+ ;;
+esac
+
+commands=(`recit commands --sh`)
+IFS="|"
+cat <<EOS
+_recit_wrapper() {
+ local command="\$1"
+ if [ "\$#" -gt 0 ]; then
+ shift
+ fi
+
+ case "\$command" in
+ ${commands[*]})
+ eval \`recit "sh-\$command" "\$@"\`;;
+ *)
+ command recit "\$command" "\$@";;
+ esac
+}
+EOS
+
+# zsh can't pass argument with aliases, but bash can.
+# zsh can have functions with the name being only numbers, but bash can't.
+# fix both cases here by letting zsh have a function, and bash have its alias.
+case "$shell" in
+bash )
+ echo "alias recit=_recit_wrapper"
+ ;;
+zsh )
+ echo "recit=_recit_wrapper"
+ ;;
+esac
diff --git a/libexec/recit-open-projects b/libexec/recit-open-projects
new file mode 100755
index 0000000..efb323f
--- /dev/null
+++ b/libexec/recit-open-projects
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+source lib/loader
+
+recfile=$(load_recit)
+
+recsel -t Project ${recfile}
+
diff --git a/libexec/recit-setup b/libexec/recit-setup
new file mode 100755
index 0000000..d3da34c
--- /dev/null
+++ b/libexec/recit-setup
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+source lib/recfile_template
+
+if [[ -f "$HOME/.recit.location" ]]; then
+ recfile_location=$(cat "$HOME/.recit.location")
+ echo "looks like you already have recit set up at ${recfile_location}, aborting..."
+ exit 1
+fi
+
+recfile_template > "$HOME/.recit.db"
+echo "$HOME/.recit.db" > "$HOME/.recit.location"
diff --git a/libexec/recit-sh-shell b/libexec/recit-sh-shell
new file mode 100755
index 0000000..10ac03c
--- /dev/null
+++ b/libexec/recit-sh-shell
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+set -e
+
+echo $1
diff --git a/libexec/recit-today b/libexec/recit-today
new file mode 100755
index 0000000..96cbec4
--- /dev/null
+++ b/libexec/recit-today
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+# Usage: recit today
+
+set -e
+
+# shellcheck source=/dev/null
+source lib/loader
+
+recfile=$(load_recit)
+
+fmt_date=$(date '+%Y-%m-%d')
+
+recsel -e "(Time >> '$fmt_date 00:00:00' && Time << '$fmt_date 23:59:59') || Time == '$fmt_date'" -t Entry "${recfile}"
diff --git a/libexec/recit-tomorrow b/libexec/recit-tomorrow
new file mode 100755
index 0000000..c5802d1
--- /dev/null
+++ b/libexec/recit-tomorrow
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+# Usage: recit tomorrow
+
+# shellcheck source=/dev/null
+source lib/loader
+
+recfile=$(load_recit)
+
+fmt_date=$(perl -e "use POSIX qw(strftime); print strftime('%Y-%m-%d', localtime(time + 86400)), qq(\n);")
+
+recsel -e "(Time >> '$fmt_date 00:00:00' && Time << '$fmt_date 23:59:59') || Time == '$fmt_date'" -t Entry "${recfile}"
diff --git a/libexec/recit-yesterday b/libexec/recit-yesterday
new file mode 100755
index 0000000..ad6e804
--- /dev/null
+++ b/libexec/recit-yesterday
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+# Usage: recit yesterday
+
+# shellcheck source=/dev/null
+source lib/loader
+
+recfile=$(load_recit)
+
+fmt_date=$(perl -e "use POSIX qw(strftime); print strftime('%Y-%m-%d', localtime(time - 86400)), qq(\n);")
+
+recsel -e "(Time >> '$fmt_date 00:00:00' && Time << '$fmt_date 23:59:59') || Time == '$fmt_date'" -t Entry "${recfile}"
diff --git a/share/recit/example b/share/recit/example
new file mode 100644
index 0000000..8895044
--- /dev/null
+++ b/share/recit/example
@@ -0,0 +1,4 @@
+here's an example file you could read
+it has stuff in it
+maybe you want some config values
+or even some YAML?