# Changelog
All notable changes to ntropy are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## v2.1.1 - 2026-09-14
### Changed
- Dependencies updated to their latest compatible releases: `jiff` 0.2.37,
`toml` 1.1.6.
### Fixed
- v2.0.0 and v2.1.0 still locked the yanked `chacha20` 0.10.1, as they were
released from a line without the v1.12.1 lockfile refresh. The lockfile
moves to 0.10.2.
## v2.1.0 - 2026-09-11
### Added
- A site theme may carry page templates: a `templates/<name>.html` in the
theme directory replaces the built-in template of that name, and the
built-in ones stay reachable as `ntropy/<name>` for a template that
extends one and fills a block. The built-in `page.html` defines empty
blocks at its seams (`head`, `header_nav`, `header_tools`,
`before_content`, `after_content`, `footer`, `scripts`). The same
templates render `render --to html`. A template that fails to parse or
render fails the export naming it.
- Templates receive the page's `kind` (`front`, `note`, `group`,
`document`), `path`, the `note` they render with its raw frontmatter,
the sidebar as data (`nav`), and `vars`, a free-form `[site.vars]` table
of the vault config.
- A note's `site.template` picks the theme template that renders its
pages and its html artifact; a name the theme lacks is a warning and
`page.html` is used.
### Changed
- `site theme init` writes the built-in templates into the new theme
beside the stylesheet, icons, and fonts.
- A sidebar rooted at a tag or view group (`[site] root`, or a single
`tag:` export query) shows the root's child groups as its sections
instead of one section wrapping them; breadcrumbs start at the
section. Every section's title now links to the section's page, in
place of the "All groups" and "Overview" rows.
## v2.0.0 - 2026-09-11
### Added
- `ntropy site -o <dir> [query]` exports the vault as a static website:
a page per note under `notes/`, a tag tree under `tags/` with a page per
tag, a tree per configured view under `views/`, and a front page (the
note named by `[site] index`, or a generated overview). Every page has a
sidebar with the current trail opened, breadcrumbs, an outline of the
note's headings, and previous/next links. Note links point at the
target's page; vault files the notes reference are copied under
`files/`, a linked directory with its whole tree. The site works when
opened from disk. An optional query restricts the exported notes,
`--force` empties a non-empty output directory, `-p` prints the front
page's path, and `--strict` fails on export warnings.
- `ntropy site theme init <name>` copies the built-in site theme into
`.ntropy/themes/site/<name>/` as the starting point for a custom one.
- A note's `site` frontmatter table shapes the site's navigation: `order`
places the note among its group's entries, `label` names it in the
sidebar and the pager, `hidden` keeps it out of the sidebar, the lists,
and the pager while its page stays exported and searchable, and `index`
makes it the landing note of its groups, whose pages then show the
note in place of the listing (`listing: true` keeps the listing
below it) and take the note's label and order. Group
entries read in order: ordered ones first, then notes newest first,
then groups by label; previous and next follow that reading order
across group boundaries.
- The agent skill covers the website export: a `site` row and an
html-aware `render` row, golden rules for the reserved `site`
frontmatter key and for naming notes by ULID in the site config, a
publish workflow, and two references, `site.md` (command, warnings,
`[site]` and the nav table, the `site` frontmatter keys, the standalone
page) and `site-themes.md` (layout, tokens, fonts, icons, markup). A test
pins the theme reference's names to the built-in theme.
- The search palette's note results show their icon: the built-in theme
was missing the `file-text` icon the palette uses.
- `ntropy render --to html` writes the site's page for one note as
`<stem>.html` plus `<stem>_files/` beside it, holding the theme, the
fonts, the page script, the grammars the page needs, and the images
and files the note references, so highlighting, the scheme switch,
the outline, the type, and the images all work as on the site. The
page has no sidebar, search, breadcrumbs, pager, or related notes.
- `[site] root` roots the sidebar at a tag or view group page
(`tags/docs`, `views/by-status/open`), and an export whose query is a
single `tag:` predicate is rooted at that tag; `[[site.nav]]` tables
assemble the sidebar by hand from notes, tag subtrees, views and their
groups, the whole tag tree, and labelled groups of items, and are then
the whole sidebar. Breadcrumbs follow the sidebar. `[site] related =
false` drops the related notes from the note pages, and a note's
`site.related` overrides that for its own page.
- Exported pages carry a scheme switch (system, light, dark) that
remembers the choice in the browser, an outline that follows the
reader's position, and code blocks highlighted by Shiki in the browser
for 72 languages; a page loads only the grammars its code blocks need,
and a fence language without a grammar is reported as an export
warning.
- The exported site's look: Fraunces for titles, Literata for note
bodies, DM Sans for the chrome, DM Mono for dates and counts, all
shipped with the site; warm paper with amber-brown ink in light mode,
warm black with gold in dark mode; Lucide icons in the chrome, on
tags, and in callouts.
Note lists are one row per note with the date, the title, and the
tags as links. The sidebar keeps view sections collapsed away from
their pages and lists the top-level tags with counts; on narrow
screens it is a drawer. A note page links its tags to their pages,
ends with the notes sharing the most tags with it, and drops a leading
heading that repeats its title. A view over the `tags` field is
skipped, since the tag section already is that view. `/` opens the
search, Escape closes it.
- A site theme's layout: `style.css`, `icons/*.svg`, `fonts/`, and any
other file, copied under `assets/`. The icons become a sprite every
page inlines; a vault theme's icons replace or add to the built-in
ones by file name. `site theme init` writes the complete built-in
theme, fonts and icons included.
- Exported sites search in the browser, in a palette over the page that
the header's button, `/`, or Ctrl+K opens. A word matches titles,
tags, frontmatter values, and text; two words need both; `tag:`,
`field:`, and `text:` narrow, `tag:` and `field:` partially, `text:`
as the same regex as in the CLI; `and`, `or`, `not`, and parentheses
combine. Results come grouped, tag and view pages before notes, with
the matches marked and a line of context per note; the arrow keys and
Enter open one. The search data is one script under `assets/`, loaded
the first time the palette opens.
- `render --to html` writes a note as one self-contained web page: the
site theme's stylesheet inlined, the title, tags, and remaining
frontmatter as a header, the converted body with heading anchors, and
note links pointing at `<slug>.html` beside the artifact. No external
tool is involved.
- Site themes. A directory `<vault>/.ntropy/themes/site/<name>/` holding a
`style.css` is a theme; `[site] theme` in the vault config selects it
and `--theme` overrides it for the `html` format. A built-in theme with
light and dark palettes ships in the binary.
- A `[site]` table in the vault config: `theme`, `index`, `title`, and
`lang`, all optional.
### Changed
- `render` refuses an existing artifact of any format, and for `html` a
non-empty `<stem>_files/` directory, unless `--force` replaces them;
it used to overwrite the artifact in place. A script that re-renders
into the same path needs the flag.
- Typst themes live in `<vault>/.ntropy/themes/typst/`, one subdirectory
per theme type under `themes/`. A theme still at
`.ntropy/themes/<name>.typ` fails the render with a message naming both
the old and the new path; move the file to `themes/typst/` once.
## v1.12.1 - 2026-09-07
### Changed
- Dependencies updated to their latest compatible releases:
`apple-native-keyring-store` 1.0.2, `clap` 4.6.6, `thiserror` 2.0.20,
`toml` 1.1.5.
### Fixed
- `chacha20` 0.10.1, which reaches ntropy transitively through `ulid`, was
yanked from crates.io, so building the previous release warned about it. The
lockfile moves to 0.10.2.
## v1.12.0 - 2026-09-07
### Added
- Vault render themes. A Typst file in `<vault>/.ntropy/themes/` named by
`[render] theme` in the vault config becomes the look of every rendered
document: after that one line, a plain `ntropy render` and a loop over the
whole vault both produce themed output with nothing theme-related on the
command line. `--theme <name>` overrides it for one invocation and
`--theme default` returns to the built-in look.
- A theme redefines only what it wants. Its source is emitted after the
engine's prelude, so `note`, `callout`, `notelink` and `task` shadow the
built-in versions and anything it leaves alone is inherited. The four
signatures are documented as the theme API in `docs/design/typst-engine.md`
and pinned by a snapshot test.
- Themes can use vault assets. `typst` now compiles with the vault as its
root, so a theme reaches a logo or a font outside `all-notes/` with a
root-absolute path such as `image("/assets/logo.svg")`, and `all-notes/` goes
on holding notes and nothing else.
### Changed
- Local image paths in a note body are emitted as root-absolute paths
(`#image("/all-notes/diagram.png")`). Typst places a document read from stdin
at the compile root, so with the root widened to the vault a bare filename
would otherwise be looked for in the wrong directory. A note can now also
reach a shared vault asset with `../assets/logo.svg`.
- Compiling a `--to typst` artifact by hand takes `typst compile --root <vault>
<file>.typ`, since the emitted document addresses its assets from the vault
root. An artifact whose note and theme reference no files still compiles on
its own.
- A missing or broken theme fails the render, naming the theme and the path it
looked for. There is no fall back to the built-in look, so a document is
never quietly produced in the wrong livery.
## v1.11.0 - 2026-09-07
### Changed
- A note link in a rendered document is now a real link, not just the target's
styled title. It points at `<target-slug>.pdf`, which is the name `render`
gives that target's own artifact by default, so a set of notes rendered
without `-o` into one directory cross-references itself. The target is read
from the vault at render time, so a slug that has drifted in the Markdown does
not reach the artifact. The link is a plain relative reference: following it
is the PDF viewer's behavior, and it finds nothing when the target was never
rendered or was renamed.
## v1.10.0 - 2026-08-18
### Added
- `new --empty` creates the note file with no content in it, skipping the
template step. ntropy still decides the identity, the location and the
filename; the caller writes the frontmatter and body. This is for scripts and
agents, which can then author a note in one write instead of parsing and
rewriting around a stamped skeleton. `--template` is rejected alongside it,
and until frontmatter is written the file is not a well-formed note, so a scan
skips it with a warning.
- `ntropy write <id|filename|path>` replaces one note's content with text read
from stdin. It names its target rather than searching for it, refuses text
that is not a well-formed note before writing anything, and then realigns the
filename and refreshes the views itself, so a written title cannot leave those
stale. It never prompts and never opens a picker, terminal or not.
- Authoring a note in an encrypted vault no longer needs an editor, which a
script cannot drive. `write` resolves its target from the directory listing
and reads no note, so it works on a locked vault too, the same way `new`
already did. Paired with `new --empty`, a caller composes a note and stores it
without ever reading one back, identically in both kinds of vault.
- The agent skill teaches `--empty` and `write`: when to reach for them, the
frontmatter and body contract a note written that way must satisfy, and the
obligation to fill the file in the same step it is created.
### Fixed
- The agent skill no longer tells agents that `render` needs pandoc, which
stopped being true in v1.7.0. It names `typst` as the only external tool, and
documents `--to typst`, which needs none.
## v1.9.1 - 2026-08-06
### Changed
- Dependencies updated to their latest compatible releases: `anyhow` 1.0.104,
`clap` 4.6.5, `ignore` 0.4.33, `jiff` 0.2.35, `libc` 0.2.189, `regex` 1.13.1,
`serde` 1.0.229, `serde_json` 1.0.151, `thiserror` 2.0.19, `toml` 1.1.4.
- `ulid` updated to 3.0.0 and `lsp-server` to 0.10.0.
## v1.9.0 - 2026-08-06
### Added
- At-rest vault encryption. `ntropy init --encrypted` produces a vault whose
notes are age ciphertext on disk, so whatever syncs the directory cannot read
them; after a one-time `ntropy unlock` every command behaves as before.
`ntropy lock` forgets the key again.
- `ntropy vault encrypt|decrypt|rekey|passphrase` convert an existing vault's
storage, re-encrypt it to a fresh key, or change its passphrase. Each rewrites
the whole vault and is safe to interrupt: every note is written and verified
before anything is deleted, and `--resume` finishes a run that was. `rekey`
keeps the vault's passphrase unless `--new-passphrase-file` names another.
- `search --print-content`/`-P` writes a note's text to stdout instead of
opening the editor, resolving to exactly one note. Unlike `--print`, which
names a file, it reads through the vault's cipher and so produces the same
bytes whether or not the vault is encrypted.
- `--identity <path>` (or `$NTROPY_IDENTITY`) and `--passphrase-file <path>`
supply a key and a passphrase without the OS credential store or a prompt,
which is what makes an encrypted vault usable from a script. Neither writes
to the credential store: only a passphrase you typed leaves the vault
unlocked afterwards.
- The language server works against encrypted vaults with the same feature set.
Following a link opens a decrypted, read-only copy outside the vault, since
the note itself is ciphertext; editing still goes through `ntropy search`.
- `reconcile` adopts a plaintext note dropped into an encrypted vault by hand,
encrypting it in place. It takes only files that already parse as notes and
never overwrites an existing one; because encrypting needs no key, this works
on a locked vault too.
- The `encryption` cargo feature, enabled by default, carries the cryptography
and OS credential-store dependencies. Building with `--no-default-features`
drops them; none links a C library, so distribution is unchanged either way.
The command surface is compiled either way and reports the missing support at
runtime.
### Changed
- Materialized views are unavailable in an encrypted vault, where a symlink tree
would spell out the tag taxonomy in plaintext directory names. `view add` says
so rather than accepting a definition that would produce nothing; `view list`
and `view remove` keep working.
- `info` reports whether a vault is encrypted and whether it is unlocked. A
locked vault says so instead of printing note statistics, which would be
indistinguishable from an empty vault.
- Editing a note in an encrypted vault decrypts it to an owner-only file outside
the vault, so a sync provider never sees the plaintext. Plaintext vaults are
unaffected and still open the note itself, leaving editor swap and undo files
exactly where they were. If the note changes underneath an open editor the
write is refused rather than silently overwriting, and your version is kept at
a path the error names.
- `render` warns when the artifact would land inside an encrypted vault: the
output is plaintext by nature and would sync unencrypted. The render still
proceeds and the exit code is unaffected.
## v1.8.0 - 2026-08-05
### Added
- `info --print`/`-p` reports the active vault's path alone instead of the
full report, skipping the scan behind the statistics. The path is absolute
whichever resolution rule matched, and an unresolvable vault exits non-zero
with nothing on stdout.
- Shell integration under `contrib/shell/`, sourced from your rc file rather
than installed: `ntropy.sh` (bash and zsh) defines `ncd`, which changes
directory to the active vault and forwards its arguments to `ntropy`, so
`ncd --vault ~/notes` works like any other command.
## v1.7.1 - 2026-07-10
### Changed
- Dependencies updated to their latest compatible releases: `anyhow` 1.0.103,
`ignore` 0.4.28, `jiff` 0.2.32, `regex` 1.13.0.
### Fixed
- `render` to `pdf` no longer dies silently when the typesetting tool exits
before reading the whole piped document; the broken pipe is tolerated and
the tool's own exit status decides success.
## v1.7.0 - 2026-07-10
### Added
- `render --to typst` writes the note as a Typst document, converted by
ntropy's own Markdown-to-Typst engine and needing no external tool on `PATH`.
- The render paper size is configurable per vault: a `[render]` section in
`.ntropy/config.toml` with `paper = "us-letter"` (or `a3`, `a5`, `iso-b5`,
`jis-b5`, `us-legal`, `us-tabloid`, `us-executive`, `us-oficio`; the
default stays `a4`).
### Changed
- `render` to `pdf` now uses ntropy's own typst engine, which converts the
note and compiles it with the external `typst` binary, so only `typst`
must be installed and on `PATH`.
- Rendered notes have a redesigned default look: a4 paper, tags as chips in
a subdued metadata block (fields with empty values are skipped), colored
callouts per kind, code chips and panels, drawn task-list checkboxes,
colored links with note links visually distinct, and lighter tables,
quotes, and rules.
### Removed
- The pandoc render engine and with it the `--engine pandoc` choice; pandoc
is no longer needed for anything.
- The content `init` seeds a vault with (the `README.md` and the `default` and
`today` templates) now lives as real files under `src/vault/seed/`, embedded
at compile time rather than written as Rust string literals (ADR 0039). A
seeded vault is byte-for-byte unchanged.
## v1.6.1 - 2026-07-09
### Fixed
- A clippy lint new in Rust 1.97 (`unnecessary_sort_by`) failed the build
under `-D warnings`; the pandoc engine's link-span sort now uses
`sort_by_key`. No behavior change.
## v1.6.0 - 2026-07-09
### Added
- `render [id|query]` produces a document artifact from a single note. v1
renders a PDF through pandoc with typst as the PDF engine, so both must be
installed and on `PATH`. The selector follows the id-or-query rule of
`search`/`delete` and, like `search`, may be omitted to fuzzy-pick from all
notes; an ambiguous selector opens the picker interactively and errors with
the candidate list under `-n`. `--to` selects the format
(default `pdf`), `--engine` overrides the format's default engine,
`--output`/`-o` names the artifact (default `./<slug>.pdf`, overwriting any
existing file), and `--print`/`-p` prints the artifact path to stdout on
success so `open "$(ntropy render -p ...)"` composes; without `-p` the
command narrates the render and reports the artifact, engine, and size.
Rendering is read-only with respect to the vault.
- `search` (and its aliases `list` and `edit`) now takes `--print`/`-p`: on a
TTY the selected note's path is printed to stdout instead of opening the
editor. A lone match prints directly; several matches open the picker and
the chosen note's path prints; cancelling the picker exits non-zero so
`p=$(ntropy search -p ...)` branches correctly. Non-interactively (`-n`)
every match prints as one path per line, newest first.
### Changed
- **Breaking:** interactivity now keys off the controlling terminal instead of
stdout. Redirecting output no longer switches ntropy into plain mode; only
`-n` (or the absence of a terminal, as in cron and CI) does. The picker, the
delete confirmation, and the editor all talk to the controlling terminal
directly, so `ntropy search -p | pbcopy` opens the picker and pipes only the
selected path. Scripts that piped or captured output without flags must now
say so: `ntropy search ... | grep` needs `-n` for the table, and
`path=$(ntropy new Title)` needs `-p` (or `-n`) to print the path instead of
opening the editor.
- The `--no-edit` flag on `new` and `today` is renamed to `--print`, which was
previously its alias, and gains the short form `-p`. `--no-edit` keeps
working as a hidden alias, so existing scripts are unaffected.
## v1.5.0 - 2026-07-07
### Added
- `init` now seeds a `README.md` in the vault root that identifies the
directory as an ntropy vault, links to <https://ntropy.westhoffswelt.de>,
and shows how to install the CLI (`cargo install ntropy`), so anyone who
discovers a vault knows how to access it. Like the templates, it is written
only when absent: a re-init restores a deleted README but never overwrites
an edited one. `README.md` is now a reserved name, so a view cannot clobber
it.
## v1.4.0 - 2026-07-06
### Added
- An agent skill under `skills/ntropy/` that teaches LLM coding agents how to
drive ntropy: a `SKILL.md` with the vault model and the non-interactive
ground rules (always `-n`, `--no-edit` on `new`/`today`, `reconcile` after
direct edits), plus reference docs on writing notes, querying, vaults, and
views. Install it with `npx skills add jakobwesthoff/ntropy` or by copying
the directory into an agent's skills folder; see the README's "Agent skill"
section.
### Fixed
- Piping ntropy's output into a reader that exits early (e.g. `ntropy info |
head -2`) no longer panics with `failed printing to stdout: Broken pipe (os
error 32)`. ntropy now exits quietly on a closed stdout pipe, like
conventional Unix tools (status 141).
- Titles with YAML-special characters (`Q3: Planning kickoff`, `[draft]
roadmap`, `#hashtag first`) previously made `new` fail. Frontmatter
placeholder substitution is now YAML-aware, quoting or escaping a
substituted value only when its surrounding YAML needs it, so such titles
work.
- A `new` whose template rendered an invalid note (e.g. one missing a `title`
field) previously left the malformed file behind in `all-notes/`; every
later command then warned about it until it was cleaned up by hand. The
rendered note is now validated before anything is written, so a failed
`new` leaves nothing behind.
## v1.3.0 - 2026-06-29
### Changed
- The plain tables (`search`/`list`, `tags`, `view list`) now render with
space-aligned columns for every invocation, including piped and `-n` output.
Columns are padded to their widest cell in Unicode display width with the last
column left unpadded, so values that overflow a tab stop no longer push the
following columns out of line. The tab-separated `awk`/`cut` positional format
is retired (ADR 0033); `tail -n +2` still drops the header. Structured (JSON)
output for machine consumers is planned.
### Fixed
- The interactive picker now fuzzy-searches the full note content. Titles and
tag lists are clipped to fit their columns, but the matcher previously only
saw the clipped text, so a long title's tail or a tag past the visible cap was
unfindable. Matching now runs over the untruncated title, tags and date, while
the columns stay width-capped; a match that lands in clipped-away text ranks
the note without painting a stray highlight.
## 1.2.0 - 2026-06-27
### Added
- ntropy maintains a root `.gitignore` listing the derived materialized view
directories, so committing a vault no longer tracks them. The entries stay in
sync with the configured views through `init`, `reconcile`, and `view
add`/`view remove`; lines you add to the file yourself are never touched.
### Changed
- Materialized views now refresh incrementally. After a mutation (and during
`reconcile`), each view is diffed against its on-disk tree and only the links
that actually changed are touched, instead of tearing down and regenerating
every view tree from scratch. Unchanged links keep their identity, and a
mutation's filesystem cost is proportional to what changed rather than to the
whole vault. On a 3000-note vault with two views (Apple M1), this cuts a
mutation or `reconcile` from roughly 820 ms to 135–150 ms (about 5–6×), with
the saved time being almost entirely filesystem syscalls. `reconcile`'s
summary now reads `synced N views` rather than `rebuilt N views`.
- `view remove` no longer deletes the view's directory. ntropy never deletes a
directory: it prunes the view's `.gitignore` entry and leaves the now-stale
directory in place, reporting it so you can delete it yourself. `reconcile`
likewise prunes entries for views removed from config without touching their
directories.
## 1.1.0 - 2026-06-26
### Fixed
- The interactive picker no longer panics on Ctrl-W when the query contains
multi-byte whitespace (e.g. a non-breaking space): word deletion now advances
by whole characters instead of bytes.
- Block-form tag completion no longer corrupts a tag containing a hyphen
(`area/work-home`): the list-item dash is located by structure rather than by
the last hyphen on the line, so accepting a suggestion replaces the whole tag.
- The picker now restores raw mode even when entering the alternate screen
fails on startup, instead of leaving the shell without echo for the rest of
the session.
- Link completion no longer drops a space from a display title that literally
contains `) $0`; the snippet placeholder cleanup is confined to the snippet
branch.
- Vault walk-up now reports a directory that looks like a vault but cannot be
canonicalized as an error, instead of silently treating it as "no vault found"
and falling through to the global default.
- The language server no longer points one character too far when a client
sends an out-of-spec position inside a surrogate pair; it clamps to the start
of the affected character.
### Changed
- Link completion now cooperates with editors that auto-close brackets: when the
closing `]` was already inserted, accepting a completion overwrites it instead
of leaving a duplicate.
- `reconcile` resolves link targets through an index rather than a linear scan
per link, so refreshing links in large, well-linked vaults is markedly faster.
## 1.0.0 - 2026-06-26
First stable release: the user-facing interface is polished and initial
language-server support is added.
### Fixed
- `init` now honors the global `--vault` flag as the target when no positional
path is given, instead of silently scaffolding the current directory. Passing
both a path and `--vault` is rejected as a conflict.
### Added
- `info` command: reports the active vault and how it resolved, the global
default vault, and vault statistics (note/tag/view/template counts, warnings,
creation-date span, top tags, and template names).
- `today` command: opens today's note (titled by the date), creating it from the
seeded `today` template on first use each day and reopening it afterward. `init`
now also seeds `.ntropy/templates/today.md`.
- `new --template <name>` / `-t <name>` selects a template from
`.ntropy/templates/<name>.md`; a missing named template is an error. Without
the flag, `default.md` is used as before. See the README Templates section.
- `list` is now a visible alias for `search`.
- `reconcile` now prints a start line and a closing summary (notes scanned,
files renamed, links relinked, views rebuilt, warnings). The summary always
prints, so a no-op run is no longer silent.
- Inter-note links: a standard Markdown link whose target is the note filename,
`[text](<ulid>-<slug>.md)`, is recognized by its leading 26-character ULID.
`reconcile` refreshes stale link slugs to a note's current filename, keeping
links resolvable and clickable after a rename. Links inside fenced or inline
code are left untouched.
- Language server (`ntropy lsp`): an editor-agnostic LSP server over stdin/stdout
that completes inter-note links (type `[`, pick a note by fuzzy-matching its
title and tags) and frontmatter tags (flow and block forms, hierarchy-aware),
and provides go-to-definition, document links, and workspace-symbol search
across notes. It resolves a vault per open document and keeps an in-memory
session cache refreshed by editor file-watch events. See
[docs/design/language-server.md](docs/design/language-server.md).
### Changed
- Plain tab-separated tables (`search -n`, `tags`, `view list`) now start with an
uppercase column header (docker-style). Strip it with `tail -n +2` if needed.
- The interactive fuzzy picker is now rendered in-house over `nucleo` and
`crossterm` instead of `nucleo-picker`. It is bottom-anchored: the query
prompt is framed by a blue divider line above and below it, with a dimmed
stats line beneath (under the query text) showing the cursor's rank within the
matches and the match/total counts, and the result list grows upward with the
best match nearest the prompt. Rows are an aligned title/date/tags grid
(widths measured in Unicode display columns) with the note's ULID shown dimmed
and never matched. Matched characters are highlighted in yellow and the
selected row in cyan with a `▌` bar, all from the terminal's own ANSI palette
so the picker adapts to its light/dark theme. Type to filter; Ctrl-W (delete
word), Ctrl-U (clear), and Up/Ctrl-P (toward worse matches) / Down/Ctrl-N
(toward the best) navigation (ADR 0027).
- A single note reference (`date title [tags] (id)`) is now used everywhere a
note is named to a person: delete prompts and confirmations and the
ambiguous-match list. The plain `search -n` table gained `date` and `tags`
columns: `id<TAB>date<TAB>title<TAB>tags<TAB>path` (tags comma-joined). This
changes the previous `id<TAB>title<TAB>path` format.
- `edit` is now a hidden alias of `search` rather than a separate command
(ADR 0031). `search`/`list` accepts a full ULID or a query and is the single
open entry point: on a TTY a single match opens directly in the editor and
several open the picker, while piped/`-n` prints the plain table without ever
opening an editor. A selector or listing that matches nothing now exits
non-zero with the message on stderr.
- Full-text search (`text:` and bare terms) now uses the `regex` crate in place
of the embedded ripgrep libraries (`grep-searcher`/`grep-regex`). Smart-case
and matching are unchanged, except a pattern that explicitly spans a newline
now matches across lines instead of being confined to one (ADR 0030).
## [0.9.0] - 2026-06-25
Initial release: a working, Unix-only (macOS, Linux) v1 of the ntropy CLI.
### Added
- Flat single-vault storage with canonical notes as
`all-notes/<ulid>-<slug>.md`; identity is carried by the filename ULID and
never stored in frontmatter.
- Permissive YAML frontmatter with recognized `title` (required) and `tags`,
plus arbitrary preserved fields, and slash-separated hierarchical tags with
German-aware slug/tag normalization.
- Stateless parallel scan of `all-notes/` that warns and skips malformed or
badly-named notes; `--strict` promotes those warnings to errors.
- Query DSL (precedence `not` > `and` > `or`, parentheses) with `tag:` segment
sub-path matching, `field:` equality and list membership, and regex `text:`
full-text search with smart-case via the embedded ripgrep libraries.
- Materialized symlink views: group by any frontmatter field, list fan-out,
`/`-nesting, normalized grouping values, `<date>-<slug>.md` leaves with
trailing-ULID collision disambiguation, and relocatable relative link targets.
- `reconcile` to realign drifted filenames and rebuild views; views are also
refreshed after every mutation.
- Note templates with `{{title}}`/`{{id}}`/`{{date}}`/`{{slug}}` substitution
and a default template.
- Two-tier TOML configuration (global default vault, per-vault view
definitions) and vault resolution order `--vault` > `$NTROPY_VAULT` > cwd
walk-up (honoring a `.ntropy-vault` pointer) > global default.
- Commands: `init` (idempotent, `--set-default`), `new`
(`--no-edit`/`--print`), `search`, `edit`, `delete` (`--force`),
`reconcile`, `view list|add|remove`, and `tags`; with global `--vault`,
`-n`/`--non-interactive`, and `--strict`.
- Interactive fuzzy picker on a TTY and `$VISUAL`/`$EDITOR` integration, with a
plain newest-first `id<TAB>title<TAB>path` table when piped or run with `-n`.
- Derived dates rendered in the system-local timezone.