hyalo

@ractive·ractive.hyalo

CLI for exploring and managing Markdown knowledge bases with YAML frontmatter

Hyalo is a fast command-line tool for searching, filtering, and mutating collections of Markdown files with YAML frontmatter. It supports property queries, tag filtering, regex search, wikilink tracking, and bulk metadata updates. Compatible with Obsidian vaults, Zettelkasten systems, and any structured Markdown documentation.

winget install --id ractive.hyalo --exact --source winget

Latest 0.20.0·July 19, 2026

Release Notes

[0.20.0] - 2026-07-19 Added

  • Broken-anchor detection in find --broken-links (iter-190, L-21): links now carry their #fragment (heading anchor) through parsing and resolution. find --broken-links reports a broken anchor — a link whose target file exists but whose #heading does not — as a category distinct from a broken target. In JSON, an anchored link gains fragment and (when the heading is missing) broken_anchor: true; text output renders "Foo#Real" → "Foo.md" and marks a missing heading as (broken anchor). The two categories are never both reported on one link (a broken target skips the anchor check), and broken anchors do not inflate links fix's broken / fixable counts or its "Apply N fixes" hint — links fix stays target-only, letting anchor semantics soak one release behind find before any lint/CI gate consumes them. Anchor matching is exact and case-insensitive (Obsidian convention: [[Foo#tasks]] matches ## Tasks), decodes percent-encoded markdown fragments (foo.md#my%20heading) for comparison only (the written form is preserved), and skips ^block-id refs (block ids are not indexed). Validation reads headings from the already-materialized index/scan sections — zero extra file reads on the --index path, and no per-file re-read on disk scan. mv and links fix preserve fragments byte-exact (the rewrite span stops before #). The Link wire-shape gained an additive fragment: Option field (#[serde(default)]), serialized into .hyalo-index entries and the persisted link graph. The field is backward compatible: existing .hyalo-index snapshots load unchanged (fragments read as None, so no false anchor reports from stale entries). To pick up fragment data for anchor validation on the --index path, rebuild the index with hyalo create-index after upgrading.
  • HYALO006 / broken-link lint rule (iter-188): hyalo lint now flags wikilinks and markdown links that point at a vault file which does not exist. Enabled and warn by default; hyalo lint --strict promotes it to an error so CI can gate broken links. The vault-wide resolution context (case/stem index) is built once per invocation — from the .hyalo-index snapshot when --index is active, else a single vault walk — and shared across workers, so the rule adds no per-file graph rebuild. Respects [lint.rules.HYALO006] enable/severity overrides, --rule HYALO006 / --rule-prefix HYALO, and --files-from (resolution stays vault-wide even when the linted set is scoped, so a scoped file linking to an unscoped-but-existing file does not false-positive).
  • Honest partial-failure envelopes for link write paths (iter-187): when a file write fails mid-batch, hyalo links fix --apply, hyalo links auto --apply, and batch hyalo mv --apply now emit a complete JSON envelope rather than aborting with a bare error. links fix --apply gains failed / failed_fixes buckets (each with the per-file error string); links auto --apply gains files_applied / files_skipped / files_failed counts plus a per-file apply_outcomes list (applied/skipped/failed with reason, so skips that previously only went to stderr are now in the envelope). Any partial failure yields a non-zero exit code. Files written before the failure are reported as applied, never silently kept and unreported. Fixed
  • find --orphan / --dead-end / --fields backlinks count case-insensitive inbound links (L-6 tail): [[foo]] pointing at Foo.md now counts as inbound in find, matching the backlinks command and summary (all three route through the same case-insensitive graph lookup). Previously find --orphan could list a file that summary and backlinks agreed had inbound links.
  • Percent-encoded markdown link destinations now resolve (iter-188, L-23): x previously never resolved (the %20 was compared literally against the on-disk filename my dest.md), so find --broken-links false-positived and backlinks "my dest.md" missed the linker. The path portion is now percent-decoded during resolution and in the link graph, so encoded and angle-bracket (x) forms resolve to the same file. Malformed (%2, %zz) or non-UTF-8 (%FF) escapes keep the literal text — a filename with a stray % still resolves as written. Rewrite keeps the destination as-authored (the %20 form is preserved on mv).
  • Batch mv --apply no longer leaves dangling links after a rolled-back rename (PR #221 review): when a mid-batch write failure rolled back file renames, a "self-rewrite" plan — one whose rewritten content was written to a file's own new (renamed) location, e.g. a moved file's outbound link rewrite — was previously left in place even though its rename was undone, stranding the file at its old path with content referencing the (now reverted) new layout. Such plans are now identified by path coinciding with one of the batch's own rename destinations, and their pre-batch content is restored alongside the rename rollback. Plans on files outside the rename set (pure external linker files) still keep the original DEC-056 behavior of being kept and honestly reported.
  • hyalo links fix --apply no longer aborts the whole batch on a per-file I/O error (PR #221 review): a stat/read failure for one source file (e.g. deleted between detection and apply) now lands that file's fixes in the failed/failed_fixes envelope and the remaining files in the batch still get their fixes applied, instead of propagating the error and losing all progress.
  • hyalo summary orphan/dead-end counts are now case-insensitive (iter-189, L-6): inbound-link membership for orphan/dead-end classification went through a case-sensitive target-set check, so on a case-insensitively-written vault a file Foo.md linked only as [[foo]] was miscounted as an orphan even though hyalo backlinks Foo.md found the linker. Inbound membership now uses the lower_index-backed backlinks_ci lookup (the same one backlinks uses), so such a file is correctly reported as a dead-end and orphan counts agree with the backlink view. Outbound membership is unchanged (on-disk paths compared against on-disk paths — no case divergence). Note: find --orphan / find --dead-end still compute inbound via the case-sensitive backlinks path; aligning them is a documented follow-up (see iter-189) so this release ships exactly one observable orphan/dead-end change. Changed
  • hyalo links fix dry-run validates plans against on-disk text (iter-187): dry-run now runs the identical plan-building phase as --apply, so its unapplied / unapplied_fixes fields report exactly the fixes --apply would refuse (stale index / concurrent edit) instead of always being empty. The "Apply N fixes" hint count now discounts would-be-stale fixes so it matches what --apply actually writes.
  • Classify-side link resolution collapsed onto the shared resolver (iter-189, refactor only): the links fix verdict logic (resolve_and_classify_link, classify_link, classify_short_form_wikilink, plus the LinkResolution / StemIndex types) moved out of link_fix.rs into discovery.rs as classify_link_from_source — the Classify-mode sibling of the Exists-mode resolve_link_from_source. Both now route their kind-dependent normalization through one private normalize_link_target helper, so Exists ("does this link resolve?") and Classify ("full fix-policy verdict") can no longer drift on the wikilink/markdown/site-absolute/bare-basename branching. The test-only detect_broken_links(&[FileLinks]) twin was deleted and its five unit tests ported onto detect_broken_links_from_index. No user-visible behavior change (locked by e2e capturing the broken/case_mismatches/ambiguous buckets).
  • Shared link-existence resolver entry point (iter-188, task 0): the "does this link exist?" resolution that find --broken-links / find --orphan / find --dead-end and the new HYALO006 rule both need is now a single discovery::resolve_link_from_source function. It owns the kind-dependent normalization (wikilink vault-relative, markdown site-absolute / path-qualified / bare-basename) and the final resolve_target call, so find/mod.rs no longer inlines that branching and the lint rule does not reimplement it.
  • Unified link write path (iter-187): auto_link now builds RewritePlans and writes through the shared execute_plans_partial machinery instead of a hand-rolled line splitter (removed split_lines_preserving_endings), keeping its stronger full-content TOCTOU guard. Batch mv reports which link rewrites were durably applied before a mid-batch abort (DEC-056: completed content writes on untouched linker files are not rolled back; the renames are, along with the content of any self-rewrite plan whose path coincided with a rename destination). What's Changed Other Changes
  • feat(links): honest partial-failure envelopes + dry-run parity + unified auto write path (iter-187) by @ractive in #221
  • iter-188: HYALO006 broken-link rule + L-19/L-23 link semantics & review close-out by @ractive in #222
  • refactor(links): collapse Classify-side resolution onto shared discovery entry point (iter-189) by @ractive in #223
  • feat(links): broken-anchor detection in find --broken-links (iter-190, L-21) by @ractive in #224
  • fix(output): broken-anchor marker missing from find text output by @ractive in #225
  • fix(find): case-insensitive inbound for --orphan/--dead-end (L-6 tail) by @ractive in #226 Full Changelog: v0.19.0...v0.20.0

Installer type: zip

x64B439A82EE1C7F0B0EB6ED1048B6F915715EEC4ACD70C2A76A63565470551FBC3

Details

Homepage
https://github.com/ractive/hyalo
License
MIT
Publisher
@ractive
Support
https://github.com/ractive/hyalo/issues
Moniker
hyalo

Tags

agent-skillagentic-workflowscliknowledge-baseknowledge-managementmarkdownobsidianrustzettelkasten

Older versions (15)

0.19.0
x6424D86285A69A93F195CD2225E6F327BC57B63B97DAEBC1AB6D0FD7504EFD933C
0.18.0
x64802BD68133EA530B48E273F077D4094BAB31F5CF24ADFFB99FBE0694133A3ECB
0.17.0
x64020F3B67C6D394E620CB549866726C46CBD1D76476B31287A042D6A0A97DC6DB
0.16.1
x6407578AFC215A4B7CD010E8F9D4B43EE505A5E2BE5422F67748D3F144977D72FF
0.16.0
x64CA2D3B35B43C3A8E7CB6AFCE98DAA76B64D43CF0D950F47915F7FEAE31F73D1E
0.14.0
x640BF9664BE8379EEB39BFBB05D96C3A96C82D50EDDBBC43632A427D865EEDEE4A
0.13.2
x6454043C0F42D26066732E2A043DBA4B93B70DB555A1CF4E2D9F7964B4FA85D3CB
0.13.0
x645F2EE6941F92E6E8EC17D09DA9C217F7897A380923D01ABE903BC90A00FD5EFC
0.12.0
x64B63F81C17FEA154F3DDEC2E41E7307AA5A7DF85D1F2603E2922BB09DA0A3AEDB
0.10.0
x640ED1A0AAEF1DE647E83CE34E67EFBAE58677F2B7684643EA9CAAF2E0DB810179
0.9.0
x64C7EC80F54B707627BF90D3B060C392A54B5FFE42C9E7C3C4BA39F1733A25EDB9
0.8.0
x64C8338C03CE1C77553C76A4C4DCB47C5ECA078AAD8163CE8E4A2E77F8C506301E
0.7.3
x642340F53BA4E9FBDFF6243AB2E50BF7E266B45288D406D64BEFDE64E73D376D20
0.7.1
x64633C01F1A352652BE42E778411EB576AF8AC95F090C8413CC7BF8A2FAE3BC2ED
0.7.0
x649938EC0E448B94E20B5C65B38F87E60381F4299E603B72F8C4025C8C0B7202E1