diff --git a/README.md b/README.md new file mode 100644 index 0000000..176f269 --- /dev/null +++ b/README.md @@ -0,0 +1,267 @@ +# 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|txt' + ``` + + 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: + +```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.