Introduction
wwid (what was I doing?) is a command-line tool for keeping portable,
ad-hoc, project-relative notes. It maps text files to relative paths inside
projects, makes no further assumptions, and does not attempt to manage your
workflow for you.
- Quick & simple CLI.
- Wrap it in a fuzzy picker, call it from your scripts, etc.
- Notes and metadata are stored separately from projects.
- No source tree pollution, easy syncing thanks to plain-text format.
- Notes can be in any plain-text format.
- Markdown, AsciiDoc, text, Python, Shell, you name it.
- Edit on your own terms.
- Interactively with your
$EDITOR, or non-interactively over standard IO.
- Interactively with your
For examples of the kinds of workflows wwid enables, please see the
workflows chapter.
Who is this for?
You may like wwid if you:
- Frequently stop work mid-task and struggle with forgetting context.
- Prefer plain-text, bring-your-own-editor note taking.
- Work across many projects & files simultaneously.
This tool probably works best for people who think in terms of files & directories, and who want to leave short, contextual notes without committing to a larger system. It is less suitable if you want long-lived knowledge storage or rich metadata.
License
wwid and this website are developed on
Codeberg and made available under the
OBSD license.
Design
We aim to be explicit about what wwid does (and does not) endeavour to do.
Thus, we define a set of goals and non-goals here. They should give you a better
idea of what the tool offers, and how it may grow in the future.
Goals
- Notes are always attached to paths so context is implicit.
- Do not impose a note format, editor, or workflow; make it easy for users to apply their own preferences.
- Notes are stored in a “system-agnostic” way, making them suitable for syncing between machines where the same project may be located at different paths.
- Creating, opening, and deleting notes should be cheap enough to do ad-hoc.
Non-goals
wwid does not aim to be:
- A task manager or TODO system.
- A personal knowledge management (PKM) tool or “second brain”.
- A note format or schema.
- A workflow framework or productivity methodology.
- A replacement for your current productivity tooling.
wwid does not structure, prioritize, or interpret notes. Its only function
is to map notes to paths and provide utilities for managing them; everything
else is left to the user.
Installation
Latest
You can install from the latest commit with Cargo:
cargo install --git "https://codeberg.org/ficd/wwid"
Stable
Prebuilt (Linux only)
Prebuilt, statically linked binaries for Linux are available on Codeberg.
Crates.io (any platform)
You can download and build the latest release via crates.io:
cargo install wwid
Quick Start
Setup
If you haven’t already, begin by installing wwid. If you
want, create the configuration file config.toml in
the standard config directory (most likely ~/.config/wwid). wwid requires no
initialization. It works immediately inside any project as soon as you create a
note.
Creating your first note
Enter a git repository and open the root note, a special note that covers the whole project:
cd my-project
wwid
This will:
- Create a note file (or open the existing one).
Important
All notes are stored in the user data directory. Your project will never be directly modified.
- Link it to the project root.
- Open your configured editor.
- Save your changes to the note.
Writing notes
Besides the root note, you can attach notes to files and directories inside the project. For example, you can add a note to a Rust project’s main file:
wwid edit src/main.rs
# to set a persistent extension for this note:
wwid edit -x md src/main.rs
The edit command is used for both creating new notes and editing
existing ones. It’s handled automatically, so you don’t need to worry about
it. Like the previous command, it will open your preferred editor and wait for
your changes.
Later, you can print the note to standard output:
wwid print src/main.rs
You can also create or overwrite notes non-interactively:
wwid write src/main.rs --content "This goes in the body!"
Omit the --content flag to use standard input. This allows wwid to be used
in pipelines. For example, let’s use sed to replace occurrences of TODO with
DONE:
wwid print foo/bar | sed 's/TODO/DONE/g' | wwid write foo/bar
Managing notes
Use the list command to see which notes exist in the project:
wwid list
You can also delete unwanted notes:
wwid rm src/main.rs
# or skip the confirmation prompt
wwid rm --force src/main.rs
Using the edit command to empty the note also automatically deletes it.
Important
- orphan
- A note whose target no longer exists in the project.
Finally, you can use the prune command to clean up notes that are orphaned or
empty:
wwid prune
# or skip the confirmation prompt
wwid prune --force
Project-relative paths
All paths given to wwid are resolved relative to the project root. You can
reference files using ., .., etc, but the stored note is always normalized.
Thus:
cd src
wwid edit ../README.md
Even though you used ../README.md, the note is associated with the path
README.md. You can verify this with the list command, whose output should
include README.md.
Overview
Projects
Notes must belong to a project, and most commands operate on the current project. It is not currently possible for notes to exist outside of projects.
Roots
wwid discovers projects by walking upward from the starting path and looking
for one of a configured list of root globs (.git
and .hg by default). Consider adding common roots like .venv or Cargo.toml
to this list if you want wwid to work without a VCS repository.
There is no requirement for repository names to be unique. A unique project ID is derived from the root directory’s name, and users are prompted to provide a “hint” to better describe the project.
For example, a project located at ~/dev/foo may be assigned an ID of foo-1,
while another project at ~/Documents/foo would be assigned ID foo-2. Users
can set any string as the project hint; the point is to contextualize its
purpose in case there are many projects with similar names & IDs.
Paths & storage
Projects are only aware of paths relative to their root. In other words, projects hold no information about their roots’ actual locations on the system. This is instead part of configuration, where users map project IDs to project root paths.
The actual data for projects, including their notes, are stored in your system’s
standard data directory in a plain-text format. On Linux, this is probably
~/.local/share/wwid. Please see
BaseDirs
for more details.
Notes
Targets
A note is a plain text file that is attached to some object inside the
project. For example, a note attached to . is the root note. You could
attach a note to src/main.rs (a file), src/lib/ (a directory), etc. As long
as it’s a real path, it’s allowed.
In other words, you point wwid at a target inside your project, and it
resolves the associated externally-stored note. The storage is abstracted away;
wwid instead provides tools to interact with the notes (like deletion,
reading, editing), detailed in the commands section.
Warning
Like projects, notes will be automatically created as needed. But unlike projects, empty notes are automatically deleted.
Extensions
You may want to assign some file extension to a note; such as md or
adoc. wwid offers two ways to do this. Setting the editor extension
means the temporary file opened by your editor will have that extension, but the
extension won’t persist. Setting a note extension means the extension will
persist, but only for that note, and is overridden by an editor extension.
Metadata
Notes can store a limited set of metadata. However, currently these are meant to
be used internally by wwid, not user-facing. Thus, we recommend against
modifying a note’s headers directly. f you use wwid’s commands to interact
with notes, you will only ever see the body. Despite that, we describe the
note format for transparency:
- Any number of headers, where the header key is followed by a colon, then its value, and each header occupies one line.
- A single blank line to denote the end of headers.
- The body.
For example:
HEADER_A: val1
HEADER_B: val2
This is the body!
A note is considered to be empty if the body contains only whitespace.
Commands
We detail here most of wwid’s commands and provide some usage examples. For an
exhaustive list, including command aliases, please see the built-in help text
with wwid --help.
Editing notes interactively
The most important subcommand is edit. Given a target, it’ll resolve the
attached note and open that in your configured editor. Relative and absolute
paths all work, as long as they resolve to a path inside the right project:
# note for the CWD
wwid edit .
# relative path
wwid edit ../Cargo.toml
# absolute path
wwid edit ~/foo/bar/src/main.rs
A common workflow may be to keep a general, project-wide task list attached to
the root, while more granular reminders and implementation details may be linked
to files. To make this use case more convenient, running wwid without any
subcommand opens the root note for the current project. In other words, given a
project located at /foo/bar, the following commands are equivalent:
# assuming $PWD = /foo/bar
wwid
wwid edit /foo/bar
wwid edit .
Editing notes non-interactively
Notes can be read and written to via the standard input & output streams.
Notes’ contents can be printed to standard output with the print command:
wwid print src/main.rs
# or omit target to print the root note
wwid print
The write command does what you expect. It takes some input and writes it to
the given note (the root note if omitted). Much like the
interactive edit, the note is created if it
doesn’t yet exist; and overwritten otherwise.
Using standard input:
echo "TODO: learn to use wwid" | wwid write
Alternatively, the input can be given on the command line with an option. If it’s set, standard input will be ignored:
wwid write src/main.rs --content "TODO: clean this file up"
These two commands enable a number of workflows involving pipelines & filter
commands, and also make text editor integrations/plugins easy to write. For
example, let’s use sed to replace all occurrences of TODO with DONE in a
note:
wwid print foo/bar | sed 's/TODO/DONE/g' | wwid write foo/bar
Deleting notes
There is a command to delete a note by its target:
wwid remove src/main.rs
The command will prompt you for confirmation. To do it non-interactively, set a flag:
wwid remove --force src/main.rs
Finally, using any method to edit a note’s body to be empty (only whitespace) automatically deletes it as well.
Querying the current project
We provide commands for querying some information with the current project. You can see information like the current project’s ID and hint, the locations of its data & config files, etc:
wwid status
You can also list the notes that exist inside this project. This will also show you which notes are orphaned or empty:
wwid list
Pruning
There is a command for pruning (or cleaning up) unneeded notes from the current project. An unneeded note is one whose body is empty, or an orphan (a note whose “owned” file/directory no longer exists.)
wwid prune
Like remove, the prune command prompts you for confirmation of each note
before deleting it. To bypass it, pass the same flag as before:
wwid prune --force
Project commands
There is a small set of projects subcommands, which can operate on specific
projects (not just the current one). You can list all available projects,
including their metadata:
wwid projects list
Additionally, you can remove an entire project, including all of its notes. This is a highly destructive action, so it cannot be performed directly on the current project; the project ID must be specified. Like the other deletion commands, confirmation is prompted for, which can be bypassed with a flag.
wwid projects remove my-project-1
wwid projects remove --force another-proj-1
Configuration
wwid has two configuration files: the user config and the
roots mapping. They are both written in the
TOML format, and are expected to be in the standard
config directory for your system. On most Linux systems, this will be
~/.config/wwid/. See
BaseDirs
for more details.
User Config
This file is called config.toml, is safe to sync across machines, and has the
following keys:
| Key | Type | Default | Description |
|---|---|---|---|
editor | String | Unset | Program that will be used for interactive editing. This is prioritized over $EDITOR if it’s set. |
editor-extension | String | Unset | Sets the extension of the temporary file opened by the editor. Consider setting this if you want your editor to provide syntax highlighting or other language-specific features, such as a Markdown preview. |
default-note-extension | String | Unset | Sets the default extension for newly created notes. Always overridden by -x. |
root-globs | List of Strings | .git and .hg | A list of globs used to identify the root during project discovery. |
# A sample config.toml with some settings changed
editor = "kak"
editor-extension = "txt"
default-note-extension = "md"
root-globs = [".git", ".venv", "Cargo.toml"]
Roots
This file is called roots.toml, and is not safe to sync across machines.
The root config does not have set keys; instead, it holds a mapping of project
IDs to absolute paths. For example, if you created a project at
/foo/bar, your roots.toml may look like this:
bar-1 = "/foo/bar"
You can see all your project IDs with wwid projects list. And don’t worry,
this file is automatically created and updated as you initialize projects. Its
purpose is to allow the same project to be stored at different locations
across machines.
If you don’t intend to sync your notes, you won’t need to edit this file.
Workflow Suggestions
Listed here are some loose workflow ideas to get you started. They all rely on the same simple idea: attach a note to a path, and let your editor and habits do the rest.
- Project journal:
- Attach a note the root and use it as a running journal: what you were doing when you stopped, what to do next, etc.
- File-level “why” notes:
- Add notes to implementation heavy or non-obvious files to capture invariants and assumptions, tradeoffs that aren’t obvious from the code, “don’t forget to refactor XYZ”, etc.
- Low-friction scratchpads:
- TODOs that aren’t ready for issues
- dead ends you might want to revisit
- Directory scoped task lists:
- refactors scoped to that subtree
- cleanup reminders
- (avoids one giant project TODO list and keeps tasks in their domain)
- Debugging breadcrumbs
- attach notes to files where symptoms manifest, what hypotheses you’ve tried, etc.
And finally, don’t forget to use wwid prune periodically! wwid works best
with temporary notes that are intentionally disposable. Think of it like a
stack, and don’t be afraid to pop notes when you’re done with them!
Development
Environment
wwid is written in Rust and developed on
Codeberg. CI is through a self-hosted instance
of woodpecker. We use
prek for pre-commit hooks, which we recommend setting
up with prek install.
Contributing
Contributions are very welcome. This is my first Rust project, so there is a lot of room for improvement. The codebase is currently a bit messy and you’ll see a mishmash of different “styles” as I’ve been experimenting with Rust’s features.
As such, I’m happy to accept contributions from experienced and beginner Rust programmers alike. However, PRs suspected of being vibe-coded will be closed immediately.