Aru’s Information Management System (AIMS)

shell
productivity
Author

Arumoy Shome

Published

February 28, 2022

Abstract

AIMS or Aru’s Information Management System is a collection of shellscripts to manage information in plaintext. It is inspired by org-mode, and tries to replicate a subset of its functionalities which I frequently use. AIMS is completely tuned towards my workflow as a researcher and how I manage my digital notes.

AIMS or Aru’s Information Management System is a collection of shellscripts to manage information in plaintext. It is inspired by org-mode, and tries to replicate a subset of its functionalities which I frequently use. AIMS is completely tuned towards my workflow as a researcher and how I manage my digital notes.

Although org-mode is great, the primary motivation for writing AIMs is because I was feeling a lot of resistance when trying to tune it to my workflow, primarily because of Elisp. Org-mode also requires that you use Emacs as your text editor. I did not appreciate the “vendor lock-in” enforced by org-mode.

You can find the latest version of the script on my dotfiles repo, below is the script as it stands on 2022-02-28.

aims
#!/usr/bin/env bash

NOTESDIR="$HOME/org"
INBOX="$NOTESDIR/inbox.md"
TEMPLATESDIR="$XDG_DATA_HOME/aims"
[[ ! -e "$INBOX" ]] && touch "$INBOX"

__capture() {
  # Capture incoming info quickly. All items are appended to INBOX
  # which defaults to `inbox.md' in NOTESDIR. Optionally a template
  # can be specified using the --template| -t flag.
  local TEMPLATE="$TEMPLATESDIR/default"

  while [[ "$1" =~ ^-+.* && ! "$1" == "--" ]]; do
    case "$1" in
      --template | -t)
        shift
        TEMPLATE="$TEMPLATESDIR/$1"
        ;;
      *)
        echo "Error: unknown option $1."
        return 1
        ;;
    esac; shift
  done

  local ITEM=$(mktemp)
  if [[ -e "$TEMPLATE" && -x "$TEMPLATE" ]]; then
    eval "$TEMPLATE $ITEM"
  fi

  if eval "$EDITOR -c 'set ft=markdown' $ITEM"; then
    [[ "$1" && -e "$NOTESDIR/$1" ]] && INBOX="$NOTESDIR/$1"
    cat "$ITEM" >> "$INBOX"
    echo "Info: captured in $INBOX."
  fi

  echo "Info: cleaning up $(rm -v "$ITEM")"
}

__capture "$@"
1
Store all notes in $HOME/org/inbox.md, creating it if necessary. Also look for template scripts in ~/.local/share/aims.
2
Parse the flags passed to AIMS. Currently it only supports the --template/-t flag which accepts the name of the template to use. Use the default template if none is provided. More on this later.
3
Create a temporary file and insert the contents of the template.
4
Edit the temporary file using $EDITOR (here I assume its vim or neovim), setting the filetype to markdown. If the first positional argument passed to AIMS is a valid file inside $NOTESDIR then set that to the $INBOX file. Finally, prepend the contents of the temporary file to $INBOX file, if vim does not report an error.
5
Cleanup, remove the temporary file.

For the time being, it only provides the capture functionality. A temporary file is used to compose the text first. Upon successful completion, the contents of the temporary file are appended to the default $INBOX file if no other files are specified.

What I find really neat is the templating system. An arbitrary name for a template can be passed to aims using the --template (or -t for short) flag. aims looks for a shellscript with the same name in the ~/.local/share/aims directory and executes it if it exists. The beauty of this design is in its simplicity. Since templates are shellscripts, it gives us the full expressiveness of the shell. This is best demonstrated with some examples. Here is my default template as of 2022-02-28 which is used when no template is specified.

~/.local/share/aims/default
#!/usr/bin/env bash

[[ -z "$1" ]] && return 1

echo >> "$1"
echo "# [$(date +'%Y-%m-%d %a %H:%M')]" >> $1
1
Sanity check, ensure that a positional argument was passed (that is, the temporary file path).
2
Insert an empty line and a level 1 markdown header with a time stamp.

It simply adds a level 1 markdown header followed by a timestamp. Here is another for capturing bibtex information for research papers.

Tip

I also wrote aocp.el, an emacs package to capture bibtex information of research papers using org-mode.

#!/usr/bin/env bash

[[ -z "$1" ]] && return 1

echo >> "$1"

BIBKEY=$(pbpaste | grep '^@.*' | sed 's/^@.*{\(.*\),/\1/')
if [[ -n "$BIBKEY" ]]; then
  echo "# [$(date +'%Y-%m-%d %a %H:%M')] $BIBKEY" >> $1
else
  echo "# [$(date +'%Y-%m-%d %a %H:%M')]" >> $1
fi

echo >> "$1"
echo '+ **Problem Statement:**' >> "$1"
echo '+ **Solution**' >> "$1"
echo '+ **Results**' >> "$1"
echo '+ **Limitations**' >> "$1"
echo '+ **Remarks**' >> "$1"
echo >> "$1"
echo '```bibtex' >> "$1"

if [[ -n "$BIBKEY" ]]; then
  pbpaste | sed '/^$/d' >> "$1"
  pbcopy <(echo "$BIBKEY")
fi

echo '```' >> "$1"
1
Check that the bibtex information is currently in the system clipboard by attempting to extract the key using grep and sed. If a key was successfully extracted, then create a level 1 markdown header with a time stamp and the key. Otherwise, fall back to just a time stamp.
2
Add my prompts for note-taking when reading scientific papers.
3
Remove empty lines and put the bibtex information in a markdown source block.

This one is a bit more involved but highlights the power of using shellscripts for templating. Given that a bibentry is copied in the clipboard, this template adds a level 1 markdown header with a timestamp and the bibkey. It adds my note-taking prompts and sticks the bibentry at the bottom.

Back to top