Blog · 2026-03-14
Caspira 0.2 — recursive links, template variables, and a faster graph
Caspira 0.2.0 is out. The short version: links now resolve across the full directory tree, template variables include dates, and the graph command no longer stalls on large vaults. There are also a few fixes for edge cases that turned out to be surprisingly common.
Recursive link resolution
The original link resolver in 0.1 only looked at files in the same directory as the note being parsed. That was a deliberate simplification — I wasn't sure how often cross-directory links would come up in practice. Turns out: constantly.
The new resolver builds a stem index of the entire vault at startup. A [[note-stem]] reference matches against this index, resolving to the full path of the first file with that stem. If more than one file shares a stem, Caspira emits a warning and picks the closer one (fewest directory hops from the referencing file).
The index is built once per run and held in memory. On a vault of 4,200 notes the index build takes about 18ms on my M2 — fast enough that I didn't bother with caching.
Template variables
Templates now support three variables: {date}, {title}, and {author}. The first two are filled automatically when you run caspira new; the third comes from author in your .caspira/config.toml.
I kept the variable syntax intentionally simple. No conditionals, no loops. If you need more than substitution, write a small shell script that generates the file directly and skip the template system.
Graph rewrite
The old graph renderer accumulated all edges into a Vec, sorted it, and printed. That worked fine up to a few hundred notes. Above that, the sort was the bottleneck and anything over 5,000 notes started to feel sluggish.
The new renderer uses an adjacency map built in a single pass and prints nodes in DFS order from the most-linked node. The output format changed slightly: nodes with no outgoing links are now grouped at the bottom of the output rather than interleaved. If you're piping caspira graph into another tool and the new ordering breaks something, let me know.
Bug fixes
- Links inside fenced code blocks are no longer parsed. Previously
[[link]]inside a code fence would be treated as a real link and reported as broken if the target didn't exist. (changelog entry) - Filenames with non-ASCII characters now work correctly on all platforms. There was a UTF-8 normalization inconsistency on Windows that caused some links to silently fail. Reported by two separate users in the same week.
caspira checkno longer panics on symlinks. It follows them if they point to.mdfiles, skips them otherwise.
Upgrading
cargo install caspira
# or
brew upgrade caspira
No config changes are required for the upgrade. The .caspira/ directory format is unchanged.
The next release will focus on caspira find — a fuzzy search across note content with link-aware ranking. If you have thoughts on how that should behave, open an issue.