# um An (U)ltralight *zettelkasten* for (M)arkdown composition. `um` is a *zettelkasten* specification, a CLI for creating compositions, and an emacs toolkit. It uses unique filenames, plaintext tags, the builtin emacs `project` package, and simple commandline conventions. It's grown out of my own scripts and emacs hacks over the years. It tries to be stupid-simple on the filesystem side, while offering powerful conveniences on the tooling side. The idea is to prioritize the moment of creation and get all noise out of the way. It consists of two parts: 1. A commandline interface written in Go. I prefer a CLI for file creation and management rather than more emacs functions, because chaining shell commands is easy and I think of the commandline as the point of reference for all filesystem management. 2. [Elisp](https://git.bth.moi/bth/um-mode) for functionality within emacs. Much of it is integrated directly with the `project` interface, and consists of the ability to find a file from a filename and search for tags. Conceptually, `um` is somewhat like org-roam, except without any database dependency. And it assumes Markdown rather than org, which I find too heavy-handed. This "database" depends on a few simple ideas: 1. A sequentially numbered filename specification which serves as unique id, like this: `001.foo.md`. The filesystem is the database. Note that the string descriptor is optional. In regex terms: ```sh ls | egrep '^[[:digit:]]+\.?.*\.md' ``` Or: ``` digits.[descriptor.]md ``` 2. A file header consisting of the title, date, and optional tags. These tags can be used by the CLI to construct expressive queries. ```markdown # 001.foo.md : 2024.01.14 - place_optional + tag_optional ``` 3. Using a "root" project defined by `um-root-glob`, where source files should first be composed and where we can assume a file exists if not elsewhere. This matters when trying to navigate back to a source file. 4. Using the built-in emacs `project` package and the CLI to organize compositions built from these source files. # install ```sh go install https://git.bth.moi/bth/um@latest ``` # CLI The command line interface is a set of conveniences: since this is all plain text, we could just as easily create everything manually. Try: ``` um help um tag --help ``` ## seed To get started, create an empty directory to serve as content origin. It doesn't matter where or what it's called, since the CLI only assumes a sequentially numbered collection of files. Then create your first file, while seeding the zero-width. 4 zeros is plenty, since that means 10k files. My zettelkasten is 20 years old and has about 3000 entries with almost a million words: ``` touch 0000.md ``` ## um next To create a new file: ```sh um next ``` This will create the file, write the header, and echo the filename to stdout. In practice I pipe it into [this function](https://gist.github.com/brtholomy/1c152af3c09be4d3f916f0e19f70f47b) that calls `emacsclient`: ```sh um next | e ``` Which is effectively: ```sh um next | xargs emacsclient -n ``` The generated header would be: ```markdown # 01.md : 2024.01.14 ``` If you run `um next` again you get: ```markdown # 02.md : 2024.01.14 ``` Create a new file with an optional descriptor: ```sh um next foo ``` Yields: ```markdown # 03.foo.md : 2024.01.14 ``` Create a new file with an optional descriptor and tag: ```sh um next foo bar ``` Yields: ```markdown # 04.foo.md : 2024.01.14 + bar ``` Or just append `+` to add the descriptor as a tag: ```sh um next foo + ``` Yields: ```markdown # 05.foo.md : 2024.01.14 + foo ``` Or send a list of tags separated by commas. `+` still works: ```sh um next foo +,bar,baz ``` Yields: ```markdown # 05.foo.md : 2024.01.14 + foo + bar + baz ``` ## um last `um last` will print the name of the last numbered file. ## um mv The `um mv` command makes it easier to add or change the string descriptor, while also updating the header: ```sh um mv 02.foo.md bar ``` And we get `02.bar.md`: ```markdown # 02.bar.md : 2024.01.14 ``` ## um tag The file header allows for an optional list of tags, one per line, marked by a leading `+`: ```markdown # 02.md : 2024.01.14 + foo + bar ``` ```markdown # 03.md : 2024.01.14 + foo ``` This is the most powerful aspect of `um`: a simple list of tags applied to source files. It encourages small files organized from the bottom up, rather than topdown management - which gets in the way of good creative moods. We can then search for files containing these tags. This list just goes to stdout, so the idea is to pipe it into a file for reordering within emacs: ```sh um tag foo > some/filelist.um ``` ### set logic The query supports union as `+` and intersection as `,`: ```sh > um tag foo+bar 02.md > um tag foo,bar 02.md 03.md ``` And the complement: ```sh > um tag foo --invert 03.md ``` Pipe them together to use a big union from which to subtract: ```sh um tag foo,bar | um tag baz --invert ``` Run `um tag --help` to see what it can do. ## um sort When working with the filelists produced by `um tag`, we'll want to rearrange the order of files and add or remove tags. Then when we update our filelist by rerunning `um tag`, we want the output to respect our updated order. `um sort` does this: ```sh um tag foo+bar | um sort --key some/filelist.um ``` And we can tell `um sort` to write new list back to the `--key`: ```sh um tag foo+bar | um sort --key some/filelist.um --write ``` This allows me to write book outlines as a shell script: ```sh # chapter 1: foo um tag book+foo | um sort -w -k ../projects/book/1.foo.um # chapter 2: bar um tag book+bar | um sort -w -k ../projects/book/2.bar.um ``` ## um cat This command is designed to work with the filelists produced by `um tag`. It separates files with a Markdown horizontal rule `---` while stripping their headers: ```sh um tag foo | um cat ``` As the last step in composing larger pieces, it accepts a filelist and a base directory to find those files. We can then pipe the output wherever we like: ```sh um cat filelist.um --base ../ > finished.md ``` Or from a real project to give you an idea: ```sh um cat $SOT/1*um --strip-file-links --base $JRN >> $SOT/concat/100.foreword.md ``` And there you have the virtue of the Unix philosophy.