Home / CLI
CLI
The wikidown dotnet tool reads, writes, moves, reorders, searches, and
link-checks pages in a Wikidown wiki. It keeps .order files, breadcrumb
navigation, and (on move) links in sync automatically. It's also the
surface AI agents use to maintain a wiki — see Agents.
Install
dotnet tool install -g Wikidown.Cli
To update an existing install, see
Updating — a plain push to main doesn't
always mean a new NuGet version is available.
Quick install (no .NET required)
curl -fsSL https://wikidown.org/install.sh | sh
irm https://wikidown.org/install.ps1 | iex
Both scripts auto-detect: if dotnet is on PATH they run
dotnet tool install -g Wikidown.Cli exactly as above; otherwise they
download a self-contained single-file binary for your platform
(win-x64/win-arm64/linux-x64/linux-arm64/osx-x64/osx-arm64) from this repo's
GitHub Releases (tagged cli-v*), extract it, and add it to PATH. Either
way nothing else needs installing — the binaries bundle their own .NET
runtime.
The binaries aren't code-signed or notarized yet, so the scripts work around
the resulting OS friction themselves: install.ps1 runs Unblock-File on
the downloaded exe so Windows SmartScreen doesn't flag it, and install.sh
strips the com.apple.quarantine attribute on macOS so Gatekeeper doesn't
block it. This is a known gap, not a bug — signing/notarization is planned
but not done.
Either install path, the next step is the same: wikidown init.
Wiki root
The CLI defaults to ./docs. Override with --root <path>:
wikidown list --root ./my-wiki
Page paths may be written with or without the leading slash —
--path Getting-Started/Format and --path /Getting-Started/Format are the
same page, and . is the wiki root (--folder .). Prefer the slash-less
form under Git Bash on Windows, which rewrites arguments that start with /
into C:/Program Files/Git/... before the CLI sees them. PowerShell and cmd
pass them through untouched.
The CLI behaves the same on Windows, macOS, and Linux:
- Text is UTF-8 everywhere, including piped
--stdinand redirected output (wikidown read --path Home > home.md). For multi-line text--fileis identical in every shell. With--stdin, note that Windows PowerShell 5.1 re-encodes piped text as ASCII unless$OutputEncodingis set to UTF-8; PowerShell 7 already is. - Files keep their own line endings — and their UTF-8 BOM, if they have
one — when the CLI rewrites them. New files match the rest of the wiki,
and a brand-new wiki uses the platform's default (CRLF on Windows). CRLF
and LF both read fine, in pages and in
.order; there is nothing to normalize. - New page names must be portable. Characters Windows forbids
(
\ : * ? " < > |, plus#), names ending in a dot or a space, and reserved device names (CON,NUL,COM1, …) are refused on every platform, so a wiki authored on Linux still checks out on Windows.
Commands
init [--root docs] [--agents claude|copilot|all|none] [--force]— seed an empty wiki with a/Homepage and install the AI agent configs (Claude Codelist [--path /P]— list children of a page (or root), one per line aspath<TAB>title, where the title is the page's first# Heading(the file name, hyphens as spaces, when it has none).wikidown listwalk [--path /P]— list every page in the wiki, depth-first in.orderorder, one per line aspath<TAB>title(title from the page's first# Heading, file name as fallback); with--path, only that page's descendants. The one-call way for an agent to orient itself before editing.wikidown walk·wikidown walk --path /Getting-Startedread --path /P [--section "<heading>"]— print page markdown to stdout, or just one section of it.--sectionmatches a heading case-insensitively, ignoring leading#s and whitespace, and prints that heading plus everything below it up to the next heading of the same or higher level (a##section includes its###children; headings inside code fences don't count). A miss lists the page's headings; if several headings match, the first is printed with a trailing note. A heading written as a link matches by its visible text, so--section "To Do"finds a heading that links the To Do page; the same goes for--afteronappendand--sectiononwrite-section.wikidown read --path /Getting-Started·wikidown read --path /CLI --section "Wiki root"write --path /P [--file F | --stdin]— overwrite a page. Auto-injects or refreshes the page's breadcrumb line — see Format § Breadcrumb Navigation.edit --path /P (--old T | --old-file F) (--new T | --new-file F | --stdin | --delete) [--all]— replace an exact substring of a page in place, leaving the rest of the page untouched; the small-change alternative towrite. The old text must match the page's raw markdown exactly (line endings are normalized to the file's, so CRLF vs LF never matters) and, without--all, exactly once — a miss or an ambiguous match is an error that says how many times the text matched. Use--old-file/--new-file(or--stdinfor the replacement) for multi-line text. Never touches the breadcrumb line or.order, and never creates a page. Prints the changed line numbers with two lines of context. To remove text, pass--deleteinstead of a replacement — Windows PowerShell 5.1 drops an empty--new ""before the CLI ever sees it.wikidown edit --path /Home --old "coming soon" --new "shipped"·wikidown edit --path /Home --old " (beta)" --deletewrite-section --path /P --section "<heading>" (--file F | --stdin) [--create]— replace the body under one heading, keeping the rest of the page. The heading line is preserved verbatim and the body from a file or stdin replaces everything after it up to the next heading of the same or higher level —###children inside a##section included. Exactly one blank line is kept around the new body, so repeated writes don't stack blank lines. A miss lists the page's headings (--createappends a new## <heading>instead), an ambiguous match is an error, and breadcrumb/.order/line endings are untouched.wikidown write-section --path /CLI --section "Wiki root" --file root.mdappend --path /P [--after "<heading>"] (--file F | --stdin)— add a block at the end of a page, or with--afterat the end of that section's body, just before the next heading of the same or higher level. Exactly one blank line separates the block from existing content and the file ends with a single newline, so repeated appends never stack blank lines. A miss lists the page's headings, an ambiguous match is an error, and breadcrumb/.order/line endings are untouched.wikidown append --path /Home --after "Open concerns" --file concern.mdnew --path /P [--title T] [--file F | --stdin]— create a new page.move --from /A --to /B [--dry-run]— rename or move a page (subpages travel with it). Rewrites inbound links from every other page that pointed at the old path, rewrites the moved page's own relative links and images if the move changed its folder depth (e.g. moving/Encounters/Footo/Adventures/Bar/Footurns../.attachments/x.pnginto../../.attachments/x.png), and regenerates the breadcrumb for the moved page and every moved descendant. Reports a count and a per-link list of what changed.--dry-runpreviews the rewrite without touching any files.kanban init [--path /Kanban]— create a simple to-do board: a dashboard page (default/Kanban) with To Do, Doing and Done sub-pages and one##section per sub-page, each heading linking its sub-page. Items are ordinary sub-pages added withnewand moved withmove. See Kanban.wikidown kanban init·wikidown kanban init --path /Home-Projectsdelete --path /P [--recursive]— delete a page (and optionally its subpages).reorder --folder /P --names a,b,c— rewrite.orderfor a folder.search --query <text>— full-text search across page bodies.check-links [--no-absolute-check] [--no-index-check] [--fix [--dry-run]]— walk every page and validate that relative markdown links ([x](../Foo/Bar.md)), image references (, raw<img src="...">, and both targets of a click-through[](full.png)) resolve to real files relative to the linking page's folder (links inside code blocks and inline code are examples and are skipped). The match is case-exact on every platform, so a link that only resolves because Windows or macOS ignores case is reported before it 404s on a case-sensitive host such as GitHub Pages. By default also:- flags every "/"-rooted link in page bodies that names something in the
wiki — a title path like
[x](/Foo/Bar)or a root path to a wiki file likeor[x](/Cards.md)— as(absolute link (404s on github.com and Pages project sites; run check-links --fix)), since github.com resolves those against the repo root and a GitHub Pages project site against the domain root (--no-absolute-checkto skip).--fixrewrites them relative to the linking page. A "/"-rooted link that names nothing in the wiki (/Nope) is reported as a plain(broken link)instead, even with--no-absolute-check; a bare/link is ignored; - audits that every subpage folder has an index page and that the index
page links every child in its body, since
WikiRepository.Writecan create a grandchild page without its parent ever existing, silently orphaning the subtree fromwikidown list/wikidown search/ the rest ofcheck-linksitself (--no-index-checkto skip). See Format § Index Pages.
Always (even with
--no-absolute-check) it also reports:- links and images whose target is a file outside the wiki root
(
../../assets/x.png, or/assets/x.pngresolving to the Git repo root), as(outside the wiki; run check-links --fix)— no publishing route (Jekyll branch deploy,export-html,export-pdf) ships files outside the wiki. Links to.mdfiles outside the wiki are not flagged. A target starting with/resolves against the wiki root if the file exists there (then it's an absolute link, above), otherwise against the project root (the Git repo root, or the wiki's parent folder when there's no Git repo); - managed copies under
.attachments/from-repo/that are stale (<path> (copy differs from its source; run check-links --fix)) or unused (<path> (copy no page references; run check-links --fix)).
--fixresolves those: it copies each outside file byte-for-byte into.attachments/from-repo/, mirroring its path from the repo root (/assets/cards/duck.png→.attachments/from-repo/assets/cards/duck.png), and rewrites only the link target to the page-relative path of the copy (fragments such as#page=2and line endings are kept; no resizing). Files outside the repo entirely (C:\art\x.png, afile:URI) go to.attachments/from-external/with a warning and are never refreshed.--fixalso refreshes stale copies, deletes unused ones (they're Git-tracked, so review the diff), and keeps a copy whose source was deleted, with a warning. It also makes "/"-rooted links relative to the linking page, keeping any fragment — on page/Cards/Movement,/Rules#setup→../Rules.md#setupand/.attachments/map.png→../.attachments/map.png. It printsrelink page:line old -> new, thencopy|refresh|delete <path>lines;--fix --dry-runprints the same prefixed with "would" and writes nothing.wikidown check-links --fix --dry-run·wikidown check-links --fixPrints one line per issue as
page:line -> target (reason)(link issues) orfolder -> detail (reason)(index issues), and exits non-zero if any issues are found, so it's CI-friendly. See Format § Fixing check-links failures for how to resolve each kind of reported issue.- flags every "/"-rooted link in page bodies that names something in the
wiki — a title path like
backfill-breadcrumbs [--dry-run]— one-time catch-up for a wiki that predates breadcrumb navigation: re-saves every page that has an ancestor but is missing its breadcrumb line, so it picks one up. Only needed once per existing wiki —write,new, andmoveall maintain breadcrumbs automatically going forward, so a wiki that's always been edited through this CLI never needs it.--dry-runlists which pages would change without writing anything. Prints a count.export-pdf --output <path> [--from /Link/Path] [--title T] [--no-cover] [--no-toc] [--allow-html-skip]— combine the whole wiki (or a subtree, with--from) into a single linked PDF:- an optional cover page (title defaults to the repo's folder name,
override with
--title, skip with--no-cover); - an optional in-document table of contents page with page numbers and
clickable entries, indented to match the nav hierarchy (skip with
--no-toc); - one section per wiki page, with each page's own leading
# Titleheading placed at its nav depth, so the PDF's bookmark/outline panel mirrors the wiki's folder structure instead of listing every page flat; - internal wiki links — relative
.mdlinks, legacy absolute title-paths, and same-page#fragmentlinks to headings — become real in-PDF jumps, not dead hrefs; - full markdown fidelity: headings, bold/italic, inline code, nested bullet/numbered lists, fenced code blocks, block quotes, pipe tables, and images (embedded when the relative path resolves).
A missing/broken image renders a visible placeholder and prints
warning: {page}: image not found: {target}(mirroringcheck-links); the export still completes, but the command exits 1 if there were any warnings (0 otherwise). Raw HTML in a page's markdown (e.g. a<div>) is unsupported and fails the whole export by default — pass--allow-html-skipto instead render a[unsupported HTML block omitted]placeholder and continue. An image or link pointing at a file outside the wiki printswarning: {page}: outside the wiki: {target} (run check-links --fix)and also makes the command exit 1; nothing is rewritten. A target starting with/follows the same rule ascheck-links(wiki root if the file is there, else the repo root — never the drive root). For example:wikidown export-pdf --output wiki.pdf.PDF font resolution is cross-platform: an embedded
EmbeddedFontResolverships Atkinson Hyperlegible Next (body text) and Atkinson Hyperlegible Mono (code) TrueType fonts inside the Wikidown.Pdf assembly instead of resolving fonts from the host OS (SIL Open Font License 1.1). Characters those fonts lack (arrows, box drawing, Greek, Cyrillic) are set in the also-embedded DejaVu Sans / DejaVu Sans Mono instead of printing blank.export-pdfworks identically on Windows, Linux, and macOS.- an optional cover page (title defaults to the repo's folder name,
override with
pages [--title T] [--force]— scaffold everything GitHub Pages' built-in Jekyll needs to publish the wiki as a static site, straight from the repo with no build pipeline:_config.yml, a starter theme (top bar, collapsible left nav that follows.order, content column),assets/wikidown.css, and a rootindex.htmlredirect. Nothing to install locally — GitHub runs Jekyll for you. The nav tree lives in_data/navigation.yml, which the CLI regenerates on every write/move/delete/reorder once it exists.--titlesets the site title (defaults to the repo folder name);--forceoverwrites theme files you've edited. Commit the result, point Settings → Pages at the/docsfolder, and the wiki is live. See Publishing to GitHub Pages for setup steps, what each scaffolded file does, and gotchas.export-html --output <dir> [--base-url /prefix] [--title T] [--clean]— render the wiki to a complete static site using the same themepagesscaffolds, in-process (Markdig + Fluid) with no Jekyll or Ruby. One.htmlper page,index.htmlredirect,assets/and.attachments/copied through, along with every other root static file (images, install scripts, favicons); relative.mdlinks rewritten to.html, titles from the first heading, nav built live from.order, and_config.yml'sfaviconandwikidown.exclude_from_sitehonored. Uses theme files from the wiki root when present (so customizations apply) and the built-in copy otherwise — runningpagesfirst is optional.--base-urlprefixes theme links for sites served under a path (GitLab project sites);--cleanempties the output folder first. Each reference to a file outside the wiki printswarning: {page}: outside the wiki: {target} (run check-links --fix)and the command exits 1; it never rewrites anything. This is the path for GitLab Pages, Azure Static Web Apps, any static host, and local preview — see Publishing to GitHub Pages § Any other host.
See Format for the on-disk format the CLI
maintains, including the relative-link convention that check-links
enforces and the breadcrumb navigation it injects.