{"id":47735657,"url":"https://github.com/ractive/hyalo","last_synced_at":"2026-04-14T11:01:00.874Z","repository":{"id":346371714,"uuid":"1187259645","full_name":"ractive/hyalo","owner":"ractive","description":"CLI tool to manage md files","archived":false,"fork":false,"pushed_at":"2026-04-12T15:58:50.000Z","size":2349,"stargazers_count":12,"open_issues_count":0,"forks_count":2,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-12T16:12:04.469Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ractive.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-20T14:20:21.000Z","updated_at":"2026-04-12T15:58:45.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ractive/hyalo","commit_stats":null,"previous_names":["ractive/hyalo"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/ractive/hyalo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ractive%2Fhyalo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ractive%2Fhyalo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ractive%2Fhyalo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ractive%2Fhyalo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ractive","download_url":"https://codeload.github.com/ractive/hyalo/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ractive%2Fhyalo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31793225,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-14T02:24:21.117Z","status":"ssl_error","status_checked_at":"2026-04-14T02:24:20.627Z","response_time":153,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-02T22:33:41.437Z","updated_at":"2026-04-14T11:01:00.851Z","avatar_url":"https://github.com/ractive.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# hyalo\n\n**Your markdown collection deserves a powerful tool to manage it.**\n\nIf you maintain an [Obsidian](https://obsidian.md/) vault, a Zettelkasten, documentation site, or any folder of `.md` files with YAML frontmatter, you've probably hit the limits of `grep` and manual editing. Hyalo gives you a fast, structured way to search, filter, and bulk-edit your markdown files from the command line.\n\n### What it does\n\n- **Find files** by frontmatter properties, tags, body content (regex), section headings, task status, or title\n- **Bulk-update metadata** — set, remove, or append to properties and tags across hundreds of files at once\n- **Move files safely** — rename or reorganize files and hyalo rewrites all `[[wikilinks]]` and `[markdown](links)` across the vault\n- **Fix broken links** — detect unresolved links and auto-repair them with fuzzy matching\n- **Validate \u0026 fix** — lint frontmatter against type schemas, auto-fix defaults, typos, and date formats\n- **Read content** — extract specific sections or line ranges from files\n- **Get an overview** — see property/tag distributions, task counts, orphan files, and link health at a glance\n\n### Why hyalo?\n\n- **Fast.** Parallel scanning, streaming I/O, optional snapshot index. Handles 10,000+ file vaults in under a second.\n- **Structured output.** JSON by default with built-in `--jq` support. Easy to pipe into scripts, CI, or AI agents.\n- **AI-agent friendly.** Designed as a tool for Claude Code and other LLM coding agents. One command sets up the integration: `hyalo init --claude`.\n- **Safe mutations.** Dry-run mode on all write operations. Preview before committing changes.\n- **Cross-platform.** Works on macOS, Linux, and Windows. No runtime dependencies.\n\n\u003e \"Hyalo\" — short for [hyaloclastite](https://en.wikipedia.org/wiki/Hyaloclastite) — is a volcanic glass, just like obsidian. The project started as a high-performance CLI for [Claude Code](https://claude.ai/claude-code) to maintain Obsidian-compatible knowledgebases.\n\n## Installation\n\n### Homebrew (macOS \u0026 Linux)\n\n```sh\nbrew tap ractive/tap\nbrew install ractive/tap/hyalo\n```\n\n### Scoop (Windows)\n\n```powershell\nscoop bucket add hyalo https://github.com/ractive/scoop-hyalo\nscoop install hyalo\n```\n\n### winget (Windows)\n\n```powershell\nwinget install ractive.hyalo\n```\n\n### Cargo (from crates.io)\n\n```sh\ncargo install hyalo-cli\n```\n\n\u003e **Intel Mac users:** Homebrew bottles and pre-built binaries are only provided for Apple Silicon. If you're on an Intel Mac, use `cargo install` above.\n\n### Manual download\n\nDownload pre-built binaries from the [GitHub Releases](https://github.com/ractive/hyalo/releases) page. Binaries are available for Linux (x86_64, ARM64, glibc and musl), macOS (Apple Silicon), and Windows (x86_64, ARM64).\n\n## Build\n\n```sh\ncargo build --release\n```\n\n## Usage\n\nAll commands accept these global flags:\n\n| Flag | Description |\n|------|-------------|\n| `-d/--dir \u003cPATH\u003e` | Root directory (default: `.`, override via `.hyalo.toml`) |\n| `--format json\\|text` | Output format (default: `json`, override via `.hyalo.toml`) |\n| `--jq \u003cFILTER\u003e` | Apply a jq expression to the JSON output (incompatible with `--format text`) |\n| `--count` | Print total as bare integer — shortcut for `--jq '.total'` (list commands only) |\n| `--hints` / `--no-hints` | Enable/disable drill-down command hints (default: on) |\n| `--site-prefix \u003cPREFIX\u003e` | Override site prefix for resolving root-absolute links |\n| `--index \u003cPATH\u003e` | Use a pre-built snapshot index (see [Snapshot Index](#snapshot-index)) |\n| `-q/--quiet` | Suppress warnings on stderr |\n\nAll JSON output uses a consistent envelope: `{\"results\": \u003cpayload\u003e, \"total\": N, \"hints\": [...]}`. `total` is present for list commands (find, tags summary, properties summary, backlinks). `hints` is always present (empty `[]` when `--no-hints`). `--jq` operates on the full envelope, e.g. `--jq '.results[].file'` or `--jq '.total'`.\n\nMost flags have short aliases for quick interactive use:\n\n| Short | Long | Available in |\n|-------|------|-------------|\n| `-d` | `--dir` | all commands |\n| `-e` | `--regexp` | find |\n| `-p` | `--property` | find, set, remove, append |\n| `-t` | `--tag` | find, set, remove |\n| `-s` | `--section` | find, read |\n| `-f` | `--file` | find, read, set, remove, append, task, backlinks, mv |\n| `-g` | `--glob` | find, set, remove, append, properties summary, properties rename, tags summary, tags rename, summary, links fix |\n| `-n` | `--limit` | find |\n| `-n` | `--recent` | summary |\n| `-l` | `--lines` | read |\n| `-l` | `--line` | task read, task toggle, task set |\n| `-s` | `--status` | task set |\n| `-o` | `--output` | create-index |\n\nGlob patterns use standard shell semantics: `*` matches within a single directory, `**` matches across directory boundaries. For example, `*.md` matches top-level files only, while `**/*.md` matches all `.md` files recursively.\n\n### Configuration\n\nPlace a `.hyalo.toml` file in your working directory to set defaults for global flags:\n\n```toml\n# .hyalo.toml\ndir = \"./my-vault\"   # default: \".\"\nformat = \"text\"      # default: \"json\"\nhints = false        # default: true (set to false to suppress drill-down hints)\nsite_prefix = \"docs\" # override auto-derived prefix for absolute link resolution\n```\n\nAll fields are optional. CLI flags always take precedence over config values. If `.hyalo.toml` is missing, hyalo silently uses built-in defaults; if the file is present but cannot be read or is malformed/invalid, hyalo warns on stderr and falls back to the built-in defaults.\n\nUse `--no-hints` to explicitly disable hints when the config file enables them.\n\n### Absolute link resolution (site prefix)\n\nDocumentation sites often use root-absolute links like `/docs/guides/setup.md`. Hyalo resolves these by stripping a site prefix — turning `/docs/guides/setup.md` into the vault-relative path `guides/setup.md`.\n\nBy default, hyalo auto-derives the prefix from the last component of `--dir`:\n\n```\n--dir ../vscode-docs/docs  →  prefix = \"docs\"   (/docs/foo.md → foo.md)\n--dir /home/me/wiki        →  prefix = \"wiki\"    (/wiki/foo.md → foo.md)\n--dir .                    →  prefix = current directory name (e.g. \"wiki\")\n```\n\nOverride when the directory name doesn't match the URL prefix:\n\n```sh\n# Directory is \"content/\" but links use \"/docs/...\" prefix\nhyalo --site-prefix docs --dir ./content find --fields links\n\n# Disable absolute-link resolution entirely\nhyalo --site-prefix \"\" find --fields links\n```\n\nPrecedence: `--site-prefix` flag \u003e `site_prefix` in `.hyalo.toml` \u003e auto-derived from `--dir`.\n\n### init\n\nInitialize hyalo in the current project. Creates a `.hyalo.toml` config file with a `dir` setting pointing to your markdown directory.\n\n```sh\n# Basic init — creates .hyalo.toml\nhyalo init\n\n# Specify the markdown directory explicitly\nhyalo init --dir my-vault\n\n# Also set up Claude Code integration (skill + CLAUDE.md hint)\nhyalo init --claude\n```\n\nWithout `--dir`, hyalo auto-detects common documentation directories (`docs/`, `knowledgebase/`, `wiki/`, `notes/`, `content/`, `pages/`) by looking for a subdirectory that contains `.md` files. Falls back to `.` if none is found.\n\nWith `--claude`, hyalo additionally installs two skills, one rule, and a managed hint section for [Claude Code](https://claude.ai/claude-code):\n\n```\n.claude/\n├── CLAUDE.md                        # managed section appended (hyalo usage hint)\n├── skills/\n│   ├── hyalo/SKILL.md               # auto-triggered skill for knowledgebase operations\n│   └── hyalo-tidy/SKILL.md          # user-invoked skill for knowledgebase consolidation\n└── rules/\n    └── knowledgebase.md             # path-triggered rule for markdown files\n```\n\n**`hyalo` skill** — Automatically triggered whenever Claude Code works with markdown files in your vault. Teaches Claude to use `hyalo find`, `hyalo set`, `hyalo mv`, etc. instead of raw `Read`/`Edit`/`Grep`/`Glob` — giving it structured access to frontmatter, tags, links, and tasks.\n\n**`hyalo-tidy` skill** — Invoked manually with `/hyalo-tidy`. Runs a five-phase knowledgebase consolidation: orients with `hyalo summary`, gathers recent signal from git history, detects structural issues (broken links, orphan files, stale statuses, missing metadata), applies conservative fixes, and reports a health summary.\n\n**`knowledgebase` rule** — A [path-triggered rule](https://docs.anthropic.com/en/docs/claude-code/settings#rules) scoped to `\u003cyour-vault\u003e/**`. Whenever Claude Code touches files in the vault directory, this rule reminds it to prefer `hyalo` CLI commands over built-in file tools.\n\n**`.claude/CLAUDE.md` hint** — A managed section (between `\u003c!-- hyalo:start --\u003e` and `\u003c!-- hyalo:end --\u003e` markers) with a short reminder to use `hyalo` for knowledgebase operations.\n\nAll steps are idempotent — re-running `hyalo init --claude` overwrites skills and the rule with the latest versions, and the managed section in `CLAUDE.md` is replaced in-place without duplicating.\n\n### deinit\n\nRemove all artifacts created by `init`. The inverse of `hyalo init --claude`.\n\n```sh\nhyalo deinit\n```\n\nRemoves:\n- `.hyalo.toml`\n- `.claude/rules/knowledgebase.md`\n- `.claude/skills/hyalo/SKILL.md`\n- `.claude/skills/hyalo-tidy/SKILL.md`\n- The managed section (between `\u003c!-- hyalo:start --\u003e` and `\u003c!-- hyalo:end --\u003e` markers) from `.claude/CLAUDE.md`\n\nEmpty parent directories left behind by the removal are cleaned up automatically. The command is idempotent — safe to run when some or all artifacts are already absent.\n\n### find\n\nSearch and filter files. Returns a JSON envelope `{\"results\": [...], \"total\": N, \"hints\": [...]}` where each item in `results` contains frontmatter properties, tags, sections, tasks, and links.\n\n```sh\n# All files\nhyalo find\n\n# Files with broken links (unresolved wikilinks or markdown links)\nhyalo find --broken-links\n\n# Orphan files (no inbound or outbound links)\nhyalo find --orphan\n\n# Dead-end files (have inbound links but no outbound links)\nhyalo find --dead-end\n\n# BM25 ranked full-text search (stemmed, relevance-ranked)\nhyalo find \"retry backoff\"               # AND: both words required\nhyalo find \"retry OR backoff\"            # OR: either word matches\nhyalo find \"retry -deprecated\"           # NOT: exclude \"deprecated\"\nhyalo find '\"retry backoff\"'             # Phrase: exact consecutive match\nhyalo find \"retry\" --tag research        # combine with filters\n\n# Regex content search (case-insensitive by default, unranked)\nhyalo find -e \"retry.*backoff\"\nhyalo find -e \"TODO|FIXME|HACK\"\nhyalo find -e \"fn\\s+\\w+_test\" --tag rust\n\n# Filter by property (operator: =, !=, \u003e, \u003e=, \u003c, \u003c=, existence, absence, or regex)\nhyalo find --property status=draft\nhyalo find --property status!=done\nhyalo find --property priority\u003e=3\nhyalo find --property status          # existence check (has this property)\nhyalo find --property '!status'       # absence check (missing this property)\nhyalo find --property 'title~=draft'  # regex match on property value (unanchored)\nhyalo find --property 'title~=/^Draft/i'  # regex with flags (i = case-insensitive)\nhyalo find --property status=draft --property topic=cli   # AND\n\n# Filter by tag (prefix-matches hierarchy: --tag inbox matches inbox/processing)\nhyalo find --tag inbox\n\n# Filter by task status\nhyalo find --task todo    # open tasks\nhyalo find --task done    # completed tasks\nhyalo find --task any     # any tasks\n\n# Filter by section heading (case-insensitive substring match by default)\nhyalo find --section \"Tasks\" --task todo          # matches \"Tasks\", \"Tasks [4/4]\", etc.\nhyalo find --section \"## Design\" \"TODO\"           # content search scoped to level-2 Design sections\nhyalo find --section \"# Introduction\" --fields sections  # level-pinned: only # Introduction, not ## Introduction\nhyalo find --section \"Tasks\" --section \"Notes\"    # OR: match either section\nhyalo find --section \"/DEC-03[12]/\"               # regex section match\n\n# Scope to file(s) (--file is repeatable)\nhyalo find --file path/to/note.md\nhyalo find --file a.md --file b.md\nhyalo find --glob \"notes/*.md\"\nhyalo find --glob '!**/draft-*'      # exclude files matching a pattern (glob negation)\n\n# Control returned fields (default: all except properties-typed and backlinks)\nhyalo find --fields properties,tags\nhyalo find --fields sections,tasks,links\nhyalo find --fields properties-typed     # [{name, type, value}] array instead of {key: value} map\nhyalo find --fields backlinks --file my-note.md    # show who links to this note\nhyalo find --fields properties,backlinks           # combine with other fields\n\n# Sort and limit\nhyalo find --sort modified --limit 10\nhyalo find --sort modified --reverse --limit 5      # newest first\nhyalo find --sort title                             # sort by title (frontmatter or first H1)\nhyalo find --sort date                              # sort by frontmatter date\nhyalo find --sort property:priority --reverse       # highest priority first\n```\n\n### views\n\nManage saved views — named filter sets stored in `.hyalo.toml` and recalled with `hyalo find --view \u003cname\u003e`.\n\n```sh\n# List all saved views (bare `hyalo views` also works)\nhyalo views list\n\n# Save a view (create or overwrite)\nhyalo views set drafts --property status=draft\nhyalo views set recent-todos --tag project --task todo --sort modified --reverse --limit 20\n\n# Delete a view\nhyalo views remove drafts\n\n# Use a saved view\nhyalo find --view drafts\nhyalo find --view recent-todos --tag rust   # extend the view with additional filters\n```\n\n**Merge behavior when combining `--view` with extra flags:**\n\n| Field type | Behavior |\n|------------|----------|\n| Vec fields (`--property`, `--tag`, `--section`, `--file`, `--glob`) | Extended — extra filters are ANDed on top of the view |\n| Option fields (`--sort`, `--limit`, `--regexp`, `--title`, `--task`, `--fields`) | Override — CLI value takes precedence over the view |\n| Bool fields (`--broken-links`, `--reverse`) | OR'd — enabled if either the view or the CLI sets them |\n\n**Storage:** Views are persisted as TOML tables in `.hyalo.toml` under `[views.\u003cname\u003e]`:\n\n```toml\n[views.drafts]\nproperties = [\"status=draft\"]\n\n[views.recent-todos]\ntags = [\"project\"]\ntask = \"todo\"\nsort = \"modified\"\nreverse = true\nlimit = 20\n```\n\n### read\n\nRead the body content of a markdown file, optionally filtered by section or line range. Defaults to plain text output.\n\n```sh\n# Read full body (text output)\nhyalo read --file path/to/note.md\n\n# Read a specific section\nhyalo read --file path/to/note.md --section \"Proposal\"\nhyalo read --file path/to/note.md --section \"## Proposal\"\n\n# Read a line range (1-based, inclusive)\nhyalo read --file path/to/note.md --lines 5:10\nhyalo read --file path/to/note.md --lines 5:\nhyalo read --file path/to/note.md --lines :10\n\n# Show frontmatter only\nhyalo read --file path/to/note.md --frontmatter\n\n# JSON output\nhyalo read --file path/to/note.md --format json\nhyalo read --file path/to/note.md --format json --jq '.results.content'\n```\n\n### properties\n\nSubcommand group for property operations.\n\n```sh\n# Aggregate summary of unique property names with inferred types and file counts\nhyalo properties summary\nhyalo properties summary --glob \"notes/*.md\"\n\n# Bulk rename a property key across all files\nhyalo properties rename --from old-key --to new-key\nhyalo properties rename --from old-key --to new-key --glob \"notes/*.md\"\n```\n\n### tags\n\nSubcommand group for tag operations.\n\n```sh\n# Aggregate summary of unique tags with file counts\nhyalo tags summary\nhyalo tags summary --glob \"notes/*.md\"\n\n# Bulk rename a tag across all files\nhyalo tags rename --from old-tag --to new-tag\nhyalo tags rename --from old-tag --to new-tag --glob \"notes/*.md\"\n```\n\n### summary\n\nCompact, fixed-size vault overview: file counts, property and tag aggregates, status counts, tasks, link health, orphan/dead-end counts, and recently modified files. Drill down with `find --orphan`, `find --dead-end`, `find --broken-links`.\n\n```sh\nhyalo summary\nhyalo summary --glob \"notes/*.md\"\nhyalo summary --recent 5          # control how many recent files to show (default: 10)\nhyalo summary --depth 2           # override default depth-1 directory listing\nhyalo summary --format text\nhyalo summary --jq '.results.tasks.total'\nhyalo summary --format text --hints\n```\n\n### set\n\nSet (create or overwrite) frontmatter properties and/or add tags across one or more files.\n\n```sh\nhyalo set --property status=done --file path/to/note.md\nhyalo set --property status=active --glob \"notes/*.md\"\nhyalo set --tag cli --file path/to/note.md\nhyalo set --property status=done --tag reviewed --file path/to/note.md\n\n# Set a list-type (YAML sequence) property\nhyalo set --property 'authors=[Alice, Bob, Charlie]' --file path/to/note.md\n\n# Multi-file targeting (--file is repeatable)\nhyalo set --property status=reviewed --file a.md --file b.md\n\n# Bulk-update: set status on files matching a filter\nhyalo set --property status=completed --where-property status=done --glob '**/*.md'\n\n# Add tag to files matching a tag filter\nhyalo set --tag reviewed --where-tag research --glob '**/*.md'\n```\n\n### remove\n\nRemove frontmatter properties and/or tags from file(s).\n\n```sh\nhyalo remove --property status --file path/to/note.md          # remove property\nhyalo remove --property tags=serde --file path/to/note.md      # remove value from list\nhyalo remove --tag cli --file path/to/note.md\nhyalo remove --property status --glob \"draft/*.md\"\n\n# Remove tag from files matching a property filter\nhyalo remove --tag deprecated --where-property status=completed --glob '**/*.md'\n```\n\n`remove --property K` (no value) removes the property entirely. `remove --property K=V` removes V from a list property, or removes the property if it is a scalar matching V.\n\n### append\n\nAppend values to list properties, promoting scalars to lists if needed.\n\n```sh\nhyalo append --property tags=serde --file path/to/note.md\nhyalo append --property tags=serde --glob \"crates/*.md\"\n\n# Append to list property on files matching a tag\nhyalo append --property aliases=old-name --where-tag renamed --glob '**/*.md'\n```\n\n### task\n\nRead, toggle, or set the status of task checkboxes. Supports three mutually exclusive selectors: `--line` (single, comma-separated, or repeatable), `--section`, and `--all`.\n\n```sh\n# Single task\nhyalo task read --file note.md --line 42\nhyalo task toggle --file note.md --line 42\n\n# Multiple tasks by line number (comma-separated or repeatable)\nhyalo task toggle --file note.md --line 5,7,9\nhyalo task toggle --file note.md --line 5 --line 7 --line 9\n\n# All tasks under a heading (case-insensitive substring, ##-pinned, or /regex/)\nhyalo task toggle --file note.md --section \"Acceptance criteria\"\n\n# Every task in the file\nhyalo task toggle --file note.md --all\n\n# Set custom status on all tasks in a section\nhyalo task set --file note.md --section Tasks --status /\n```\n\nTasks are markdown checkboxes (`- [ ]`, `- [x]`, `- [/]`, etc.) in the file body. Checkboxes inside fenced code blocks and `%%comment%%` blocks are ignored. Bulk mutations use a single atomic read-modify-write pass.\n\n### backlinks\n\nReverse link lookup — find all files that link to a given file. Scans all `.md` files in the vault and builds an in-memory link graph, then returns every incoming link (both `[[wikilinks]]` and `[markdown](links)`) pointing to the target file.\n\n```sh\nhyalo backlinks --file path/to/note.md\n```\n\n**JSON output** (default):\n\n```json\n{\n  \"results\": {\n    \"file\": \"path/to/note.md\",\n    \"backlinks\": [\n      {\n        \"source\": \"index.md\",\n        \"line\": 5,\n        \"target\": \"note\"\n      },\n      {\n        \"source\": \"journal/2026-03-20.md\",\n        \"line\": 12,\n        \"target\": \"note\",\n        \"label\": \"project notes\"\n      }\n    ]\n  },\n  \"total\": 2,\n  \"hints\": []\n}\n```\n\n**Text output** (`--format text`):\n\n```\n2 backlinks to path/to/note.md:\n  index.md:5\n  journal/2026-03-20.md:12 (\"project notes\")\n```\n\nThe `label` field (and the parenthesised text in text mode) appears only for aliased wikilinks (`[[target|label]]`) and titled markdown links (`[label](target.md)`).\n\n### mv\n\nMove or rename a markdown file and update all links across the vault. Builds an in-memory link graph, moves the file on disk, then rewrites all inbound `[[wikilinks]]` and `[markdown](links)` in other files that pointed to the old path. Also rewrites relative markdown links inside the moved file whose targets changed due to the new directory context.\n\n```sh\nhyalo mv --file old/path.md --to new/path.md\nhyalo mv --file note.md --to archive/note.md --dry-run   # preview without writing\n```\n\n**JSON output** (default):\n\n```json\n{\n  \"results\": {\n    \"from\": \"old/path.md\",\n    \"to\": \"new/path.md\",\n    \"dry_run\": false,\n    \"updated_files\": [\n      {\n        \"file\": \"index.md\",\n        \"replacements\": [\n          { \"line\": 5, \"old_text\": \"[[old/path]]\", \"new_text\": \"[[new/path]]\" }\n        ]\n      }\n    ],\n    \"total_files_updated\": 1,\n    \"total_links_updated\": 1\n  },\n  \"hints\": []\n}\n```\n\n**Text output** (`--format text`):\n\n```\nMoved old/path.md → new/path.md\nUpdated 1 link in 1 file:\n  index.md:5  [[old/path]] → [[new/path]]\n```\n\nUse `--dry-run` to preview which files and links would change without modifying anything.\n\nRoot-absolute links (e.g. `/docs/guides/setup.md`) are also rewritten during a move. Hyalo uses the site prefix to map these to vault-relative paths. If `mv` reports 0 links updated but you expect absolute links to be rewritten, check your `--site-prefix` setting (see [Absolute link resolution](#absolute-link-resolution-site-prefix)).\n\n### links\n\nSubcommand group for link operations.\n\n```sh\n# Preview broken link fixes (dry-run is the default)\nhyalo links fix\n\n# Apply fixes to disk\nhyalo links fix --apply\n\n# Adjust fuzzy matching threshold (0.0–1.0, default: 0.8)\nhyalo links fix --threshold 0.9\n\n# Scope to specific files\nhyalo links fix --glob \"notes/*.md\"\n\n# Skip links that contain Hugo/template syntax\nhyalo links fix --ignore-target '{{ ref' --ignore-target '{{ relref'\n\n# Text output\nhyalo links fix --format text\n```\n\n`links fix` detects broken `[[wikilinks]]` and `[markdown](links)` across the vault and attempts auto-repair using four strategies (in priority order): case-insensitive exact match, extension mismatch (`.md` present/absent), unique stem match anywhere in the vault (shortest-path resolution), and Jaro-Winkler fuzzy match above `--threshold`.\n\nDefault is `--dry-run` (preview only). Pass `--apply` to write fixes to disk. Use `--ignore-target` (repeatable) to skip links containing specific substrings — useful for template syntax, external paths, or anchors that aren't real files.\n\n### lint\n\nValidate frontmatter properties against the schema defined in `.hyalo.toml` (read-only).\n\n```sh\n# Lint the whole vault\nhyalo lint\n\n# Lint a single file\nhyalo lint iterations/iteration-101-bm25.md\n\n# Lint with a glob\nhyalo lint --glob \"iterations/*.md\"\n\n# JSON output\nhyalo lint --format json\n```\n\n**Exit codes:** 0 = clean, 1 = errors found, 2 = internal error.\n\nDefine a schema in `.hyalo.toml`:\n\n```toml\n[schema.default]\nrequired = [\"title\"]\n\n[schema.types.iteration]\nrequired = [\"title\", \"date\", \"status\", \"branch\", \"tags\"]\nfilename-template = \"iterations/iteration-{n}-{slug}.md\"\n\n[schema.types.iteration.defaults]\nstatus = \"planned\"\ndate = \"$today\"\ntype = \"iteration\"\n\n[schema.types.iteration.properties.status]\ntype = \"enum\"\nvalues = [\"planned\", \"in-progress\", \"completed\", \"superseded\", \"shelved\", \"deferred\"]\n\n[schema.types.iteration.properties.branch]\ntype = \"string\"\npattern = \"^iter-\\\\d+/\"\n\n[schema.types.iteration.properties.date]\ntype = \"date\"\n\n[schema.types.iteration.properties.tags]\ntype = \"list\"\n```\n\n**Property types:** `string` (with optional `pattern` regex), `date` (YYYY-MM-DD), `number`, `boolean`, `list`, `enum` (with `values`).\n\n**Severity levels:**\n- `error` — schema violation (missing required, wrong type, invalid enum value, pattern mismatch)\n- `warn` — soft issue (no `type` property, no `tags`, property not in schema)\n\n**Schema merging:** `schema.default` is the baseline. Type-specific `required` extends (not replaces) default required. Type-specific `properties` override defaults for the same property name.\n\nWhen no `[schema]` block is configured, `hyalo lint` exits 0 with zero violations (backwards compatible).\n\n`hyalo summary` shows a one-line lint count (`schema.errors/warnings`) when a schema is configured.\n\n### Lint with auto-fix\n\n```sh\n# Preview what lint --fix would change (no files written)\nhyalo lint --fix --dry-run\n\n# Apply auto-fixes: insert missing defaults, correct enum typos, normalize dates\nhyalo lint --fix\n\n# Fix a single file\nhyalo lint --fix my-doc.md\n```\n\nAuto-fix handles four categories:\n- **Insert defaults** — adds missing required properties with their schema default values\n- **Fix enum typos** — corrects near-matches to valid enum values (Levenshtein distance ≤ 2)\n- **Normalize dates** — rewrites dates to ISO 8601 (YYYY-MM-DD) format  \n- **Infer type** — sets `type` from filename template matches when absent\n\n### types\n\nManage document-type schemas in `.hyalo.toml` without hand-editing TOML. All mutations preserve existing comments and formatting.\n\n```sh\n# List all defined types and their required fields\nhyalo types\nhyalo types list\n\n# Show the full merged schema for a type\nhyalo types show iteration\n\n# Add a new type entry to .hyalo.toml\nhyalo types create iteration\n\n# Print the TOML snippet instead of writing it\nhyalo types create iteration --print\n\n# Remove a type entry\nhyalo types remove iteration\n\n# Add required fields to a type\nhyalo types set iteration --required title,date,status\n\n# Set a default value (auto-applies to existing vault files of that type)\nhyalo types set iteration --default 'status=planned' --default 'date=$today'\n\n# Preview changes without writing (dry-run)\nhyalo types set iteration --default \"status=planned\" --dry-run\n\n# Add a property type constraint\nhyalo types set iteration --property-type \"status=string\"\nhyalo types set iteration --property-type \"date=date\"\n\n# Define an enum constraint\nhyalo types set iteration --property-values \"status=planned,in-progress,completed\"\n\n# Set a filename template\nhyalo types set iteration --filename-template \"iterations/iteration-{n}-{slug}.md\"\n```\n\nWhen `--default` is used, hyalo walks all vault files of that type and writes the default value to any file that does not already have the property. Use `--dry-run` to preview which files would be updated.\n\n### Hints\n\nAdd `--hints` to any read-only command to get suggested drill-down commands:\n\n```\n$ hyalo summary --format text --hints\nFiles: 32 total\n  \".\": 5\n  \"backlog\": 7\n  \"iterations\": 12\n  \"research\": 8\nProperties: 8 unique\nTags: 15 unique\nStatus: completed (10), in-progress (2), planned (2)\nTasks: 89/174\nOrphans: 3\n  \"backlog/old-idea.md\"\n  \"research/scratch.md\"\n  \"research/unused-ref.md\"\n\n  -\u003e hyalo --dir . properties summary\n  -\u003e hyalo --dir . tags summary\n  -\u003e hyalo --dir . find --task todo\n  -\u003e hyalo --dir . find --property status=in-progress\n```\n\nIn JSON mode, hints populate the `\"hints\"` array in the standard envelope: `{\"results\": ..., \"hints\": [{\"description\": \"...\", \"cmd\": \"hyalo ...\"}]}`. The envelope shape is always the same regardless of `--hints`/`--no-hints` — only the array contents change. Each hint has a short description and a concrete, copy-pasteable command. Suppressed when combined with `--jq`.\n\n## Snapshot Index\n\nThe snapshot index is a MessagePack file that captures a point-in-time snapshot of the vault's metadata (frontmatter, tags, sections, tasks, links) for faster repeated queries. It is **short-lived and ephemeral** — it becomes stale as soon as any file in the vault is modified outside of hyalo.\n\n**Usage:**\n\n```sh\n# Create the index (one disk scan)\nhyalo create-index\n\n# Run read-only queries against the index (no disk scan)\n# --index alone defaults to .hyalo-index in the vault directory\nhyalo find --property status=in-progress --index\nhyalo summary --index\nhyalo tags summary --index\n\n# Drop the index when done\nhyalo drop-index\n```\n\n**When to use:** workflows that run many queries in a short window — CI pipelines, automation scripts, LLM tool loops. Create the index at the start, query and mutate against it, then drop it.\n\n**Read-only commands** (`find`, `summary`, `tags summary`, `properties summary`, `backlinks`) skip disk scans entirely when using `--index`.\n\n**Mutation commands** (`set`, `remove`, `append`, `task`, `mv`, `tags rename`, `properties rename`) still read and write individual files on disk, but when `--index` is provided they also patch the index entry in-place after each mutation — keeping the index current for subsequent queries. This is safe as long as no external tool modifies files in the vault while the index is active.\n\nNever commit `.hyalo-index` files to version control — they are throwaway artifacts.\n\n## Common pitfalls\n\n| Mistake | Correct usage |\n|---------|--------------|\n| `--property 'title=~/pat/'` (Perl-style `=~`) | `--property 'title~=/pat/'` (hyalo uses `~=`) |\n| `--property title~=draft` to search all titles | `--title draft` (searches frontmatter title AND H1 headings) |\n| `--tag projects` expecting substring match | `--tag project` (prefix match: matches `project`, `project/backend`, but not `projects`) |\n| `--glob '/absolute/path/*.md'` | `--glob 'relative/*.md'` (globs are relative to `--dir`) |\n| `--format text --jq '.total'` | Remove `--format text` — `--jq` is incompatible with text format |\n| `--count --jq '.results'` | Use one or the other — `--count` is a shortcut for `--jq '.total'` |\n\n## Benchmarking\n\nOn-demand performance benchmarks using [Criterion](https://github.com/bheisler/criterion.rs) and [Hyperfine](https://github.com/sharkdp/hyperfine):\n\n```sh\ncargo bench --bench micro                # pure-function micro-benchmarks\ncargo bench --bench vault                # vault-scale benchmarks (needs obsidian-hub)\n./bench-e2e.sh                           # end-to-end CLI benchmarks\n./bench-e2e.sh target/release/hyalo /tmp/hyalo-baseline   # A/B comparison\n```\n\nSee `benches/README.md` for full setup, A/B comparison workflows, profiling with [samply](https://github.com/mstange/samply), and memory measurement.\n\n## Releasing\n\n1. Bump the version in `Cargo.toml`\n2. Commit: `git commit -am \"Bump version to X.Y.Z\"`\n3. Create a GitHub release with tag `vX.Y.Z` (must match `Cargo.toml`)\n\nThe [release workflow](.github/workflows/release.yml) automatically builds binaries for all platforms, uploads them to the release, and updates the Homebrew formula.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fractive%2Fhyalo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fractive%2Fhyalo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fractive%2Fhyalo/lists"}