From 13d790217f0327c759acf4b7463ea3885d3d6593 Mon Sep 17 00:00:00 2001 From: Dustin VanKrimpen Date: Sun, 19 Jul 2026 13:01:47 -0400 Subject: [PATCH] wherefore: standardize cross-links as relative Markdown links Inline cross-references between wherefore items now use a standard relative Markdown link to the target file (e.g. [P-004: label](P-004-slug.md)), never a bare slug or an Obsidian [[wikilink]]. Markdown links render as real links everywhere -- GitHub, Obsidian, editors, and the dashboard -- which wikilinks do not. Frontmatter refs stay bare; this is for body prose only. Docs + skills: - AGENTS.md gains a canonical "Linking" section (propagates to the scaffolded template); capture/slate/supersede/resolve skills, seed's embedded plan README, wherefore/plan/README.md, and a CLAUDE.md pointer teach the same format. Dashboard: - New src/lib/md-links.mjs: config-time filename->route map + pure rewriteMdHref. - New rehype plugin rewrites .md hrefs to routes for pipeline-rendered bodies (plan/questions/now-view); log bodies get the same rewrite threaded through renderInline (which also now protects link syntax shown inside code spans). - Requires @astrojs/markdown-remark so Astro 7's rehype path is available. Dogfood: - Migrated this repo's 4 "See also" lines to Markdown links; captured the decision in 2026-07-19-markdown-crosslinks (itself using the new format). Tests: unit (map + rewrite + renderInline) and integration (both render paths). Co-Authored-By: Claude Opus 4.8 (1M context) --- AGENTS.md | 22 + CLAUDE.md | 1 + packages/wherefore-dashboard/astro.config.mjs | 10 + .../wherefore-dashboard/package-lock.json | 1322 ++++++++++++++++- packages/wherefore-dashboard/package.json | 1 + .../src/lib/entry-sections.ts | 17 +- .../wherefore-dashboard/src/lib/md-links.mjs | 83 ++ .../src/lib/rehype-md-links.mjs | 29 + .../src/pages/log/[slug].astro | 19 +- .../tests/entry-sections.test.mjs | 26 + .../log/2026-01-01-active-example.md | 1 + .../plan/P-002-paginate-catalog-search.md | 3 +- .../tests/md-links.test.mjs | 48 + .../wherefore-dashboard/tests/render.test.mjs | 16 + plugins/wherefore/commands/seed.md | 9 +- plugins/wherefore/skills/capture/SKILL.md | 4 +- plugins/wherefore/skills/resolve/SKILL.md | 5 +- plugins/wherefore/skills/slate/SKILL.md | 5 + plugins/wherefore/skills/supersede/SKILL.md | 4 +- wherefore/log/2026-06-24-adr-positioning.md | 2 +- wherefore/log/2026-06-24-rename-to-lore.md | 2 +- wherefore/log/2026-07-03-plan-directory.md | 2 +- ...2026-07-18-cross-agent-skill-validation.md | 2 +- .../log/2026-07-19-markdown-crosslinks.md | 40 + wherefore/plan/README.md | 9 +- 25 files changed, 1624 insertions(+), 58 deletions(-) create mode 100644 packages/wherefore-dashboard/src/lib/md-links.mjs create mode 100644 packages/wherefore-dashboard/src/lib/rehype-md-links.mjs create mode 100644 packages/wherefore-dashboard/tests/md-links.test.mjs create mode 100644 wherefore/log/2026-07-19-markdown-crosslinks.md diff --git a/AGENTS.md b/AGENTS.md index 9e88960..81e6994 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -169,6 +169,28 @@ Body: break the work into `- [ ]` checkboxes concrete enough to check off; prose `active`/`superseded`/`obsolete` on a plan item, or `todo`/`doing`/`done`/`dropped` on a decision. Retiring or replacing a decision is a supersession on the decision, not a plan edit. +## Linking + +When a body (an entry, a question, or a plan item) refers to another wherefore item, +write a standard relative Markdown link to that item's file, never a bare slug and never +a `[[wikilink]]` (wikilinks render as literal text on GitHub and most editors). Start the +link text with the target's ID so the raw file stays greppable, then a short label: + +```markdown +See [P-004: verified brokers](P-004-m9-populate-verified-brokers.md) for the broker work. +Blocked by [Q-007: token store](../questions/Q-007-token-store.md). +Supersedes [2026-07-03-plan-directory](../log/2026-07-03-plan-directory.md). +``` + +- The path is relative to the file you are writing: same directory is `NAME.md`; a sibling + collection is `../log/NAME.md`, `../questions/NAME.md`, or `../plan/NAME.md`. +- Link text is the target ID (`P-NNN`, `Q-NNN`, or a `YYYY-MM-DD-slug`) then `: short label`. +- This is for body prose only. Frontmatter refs (`decision_ref`, `question_ref`, + `supersedes`, `superseded_by`, `asked_slug`, `resolution_slug`) stay bare IDs/slugs. +- Standard Markdown links render as real links everywhere: GitHub, Obsidian, editors, and + the dashboard. Obsidian users: turn off Files & Links -> "Use [[Wikilinks]]" so Obsidian + writes this same portable format (it resolves Markdown links either way). + ## Conventions - All frontmatter keys use underscore style (superseded_by, superseded_date, diff --git a/CLAUDE.md b/CLAUDE.md index eba7422..5097448 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -79,3 +79,4 @@ The `wherefore/` directory itself is **not** in this repo -- it lives in each co - Entry filenames: `YYYY-MM-DD-short-slug.md`; the entry frontmatter (`date`, `title`, `areas`, `topics`, `stories`, `status`, `supersedes`, `superseded_by`, `superseded_date`) is what readers shortlist on. - Question files: `questions/Q-NNN-short-slug.md` (the `Q-NNN` prefix is the zero-padded ID; the slug is a short, lowercase, hyphenated summary of the question, same style as a log slug and only for human scanning). One per question, with `id`, `question`, `status`, `areas`, `asked_date`, `asked_slug`, `resolution`, `resolution_slug` frontmatter. The `id:` field is the authoritative ID (the dashboard keys off it, not the filename). IDs are sequential and never reused; the next ID is (highest existing `id:`) + 1. - Plan items: `plan/P-NNN-short-slug.md` (the `P-NNN` prefix is the zero-padded ID; the `id:` field is authoritative, and the loader globs `plan/P-*.md` so `plan/README.md` is never collected). Frontmatter: `id`, `title`, `status` (todo|doing|done|dropped), `created`, `updated` (bumped on any write, including a checkbox toggle), plus optional `area` (single, not the plural `areas` decisions use), `topics`, `milestone`, `decision_ref`, `question_ref`, `answers` (a single `Q-NNN` a spike is the work of answering, the opposite of `question_ref` and not a blocker), `dropped_reason`. Plan status is a separate state machine from decision status: `capture` never creates or mutates plan items, and plan transitions never touch a decision's status (that stays with `supersede`). `blocked` is derived from an open `question_ref` (at most one per item), never written. IDs are sequential and never reused; the next ID is (highest existing `id:`) + 1. +- Cross-links: body prose links another item with a relative Markdown link to its file (`[P-004: label](P-004-slug.md)`, `[Q-007: label](../questions/Q-007-slug.md)`), ID-first text, never a bare slug or a `[[wikilink]]`. Frontmatter refs stay bare IDs/slugs. See the AGENTS.md "Linking" section; the dashboard rewrites these `.md` links to routes at build time (`src/lib/md-links.mjs`). diff --git a/packages/wherefore-dashboard/astro.config.mjs b/packages/wherefore-dashboard/astro.config.mjs index d4ef4ec..ae97024 100644 --- a/packages/wherefore-dashboard/astro.config.mjs +++ b/packages/wherefore-dashboard/astro.config.mjs @@ -2,6 +2,8 @@ import { defineConfig } from 'astro/config'; import { createRequire } from 'node:module'; import { resolve, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; +import { buildMdLinkMap } from './src/lib/md-links.mjs'; +import rehypeMdLinks from './src/lib/rehype-md-links.mjs'; const __dirname = dirname(fileURLToPath(import.meta.url)); const PACKAGE_ROOT = __dirname; @@ -14,9 +16,17 @@ const astroDir = dirname(_require.resolve('astro/package.json')); const SRC = process.env.WHEREFORE_SRC; +// Filename -> route map for rewriting relative Markdown cross-links in bodies. +// Built once here (WHEREFORE_SRC is set by the CLI before build() runs); empty +// during type-gen when SRC is undefined, same guard the fs.allow list uses. +const mdLinkMap = buildMdLinkMap(SRC); + export default defineConfig({ output: 'static', site: process.env.WHEREFORE_SITE, + markdown: { + rehypePlugins: [[rehypeMdLinks, { map: mdLinkMap }]], + }, vite: { server: { fs: { diff --git a/packages/wherefore-dashboard/package-lock.json b/packages/wherefore-dashboard/package-lock.json index be2e405..7717f67 100644 --- a/packages/wherefore-dashboard/package-lock.json +++ b/packages/wherefore-dashboard/package-lock.json @@ -9,6 +9,7 @@ "version": "0.2.0", "license": "MIT", "dependencies": { + "@astrojs/markdown-remark": "^7.2.1", "astro": "^7.0.0" }, "bin": { @@ -19,7 +20,7 @@ "jsdom": "^29.1.1" }, "engines": { - "node": ">=22.12.0" + "node": "^22.18.0 || >=24.0.0" } }, "node_modules/@asamuzakjp/css-color": { @@ -277,6 +278,31 @@ "unified": "^11.0.5" } }, + "node_modules/@astrojs/markdown-remark": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-7.2.1.tgz", + "integrity": "sha512-jPVNIqTvk+yKviikszv/Y1U4jGUSKpp/Nw48QZV4qjWgp70j4Lkq3lhSDRbWwCfgKvEyO9GHuVbV1dM2WYXy1w==", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.10.1", + "@astrojs/prism": "4.0.2", + "github-slugger": "^2.0.0", + "hast-util-from-html": "^2.0.3", + "hast-util-to-text": "^4.0.2", + "mdast-util-definitions": "^6.0.0", + "rehype-raw": "^7.0.0", + "rehype-stringify": "^10.0.1", + "remark-gfm": "^4.0.1", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.1.2", + "remark-smartypants": "^3.0.2", + "unified": "^11.0.5", + "unist-util-remove-position": "^5.0.0", + "unist-util-visit": "^5.1.0", + "unist-util-visit-parents": "^6.0.2", + "vfile": "^6.0.3" + } + }, "node_modules/@astrojs/markdown-satteri": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@astrojs/markdown-satteri/-/markdown-satteri-0.3.3.tgz", @@ -2126,6 +2152,15 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", @@ -2159,6 +2194,12 @@ "@types/unist": "*" } }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, "node_modules/@types/nlcst": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", @@ -2242,6 +2283,16 @@ "node": ">= 0.4" } }, + "node_modules/array-iterate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-2.0.1.tgz", + "integrity": "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/astro": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/astro/-/astro-7.0.6.tgz", @@ -2371,6 +2422,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/character-entities-html4": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", @@ -2574,6 +2635,23 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/decimal.js": { "version": "10.6.0", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", @@ -2581,6 +2659,19 @@ "dev": true, "license": "MIT" }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/defu": { "version": "6.1.7", "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", @@ -2775,6 +2866,18 @@ "@esbuild/win32-x64": "0.28.1" } }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eventemitter3": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", @@ -2910,6 +3013,143 @@ "uncrypto": "^0.1.3" } }, + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/hast-util-from-html/node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", + "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/hast-util-raw/node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/hast-util-to-html": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", @@ -2933,6 +3173,41 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", + "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-text": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", + "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-whitespace": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", @@ -2946,6 +3221,23 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/html-encoding-sniffer": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", @@ -3402,6 +3694,16 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/lru-cache": { "version": "11.5.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", @@ -3431,56 +3733,668 @@ "source-map-js": "^1.2.1" } }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", - "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-definitions": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-6.0.0.tgz", + "integrity": "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==", "license": "MIT", "dependencies": { - "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/mdn-data": { - "version": "2.27.1", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", - "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", - "license": "CC0-1.0" + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } }, - "node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/mdast-util-from-markdown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", + "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", "license": "MIT", "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "license": "CC0-1.0" + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", "funding": [ { @@ -3494,6 +4408,60 @@ ], "license": "MIT" }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, "node_modules/micromark-util-sanitize-uri": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", @@ -3515,6 +4483,28 @@ "micromark-util-symbol": "^2.0.0" } }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, "node_modules/micromark-util-symbol": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", @@ -3556,6 +4546,12 @@ "node": ">=10" } }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/nanoid": { "version": "3.3.15", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", @@ -3725,6 +4721,24 @@ "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==", "license": "MIT" }, + "node_modules/parse-latin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz", + "integrity": "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "@types/unist": "^3.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-modify-children": "^4.0.0", + "unist-util-visit-children": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/parse5": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", @@ -3871,6 +4885,117 @@ "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", "license": "MIT" }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-stringify": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz", + "integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-to-html": "^9.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-smartypants": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/remark-smartypants/-/remark-smartypants-3.0.2.tgz", + "integrity": "sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==", + "license": "MIT", + "dependencies": { + "retext": "^9.0.0", + "retext-smartypants": "^6.0.0", + "unified": "^11.0.4", + "unist-util-visit": "^5.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -3890,6 +5015,37 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/retext": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz", + "integrity": "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "retext-latin": "^4.0.0", + "retext-stringify": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-latin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retext-latin/-/retext-latin-4.0.0.tgz", + "integrity": "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "parse-latin": "^7.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/retext-smartypants": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz", @@ -3905,6 +5061,21 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/retext-stringify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retext-stringify/-/retext-stringify-4.0.0.tgz", + "integrity": "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/rolldown": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.4.tgz", @@ -4319,6 +5490,20 @@ "ohash": "^2.0.11" } }, + "node_modules/unist-util-find-after": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", + "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/unist-util-is": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", @@ -4332,6 +5517,20 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/unist-util-modify-children": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-4.0.0.tgz", + "integrity": "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "array-iterate": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/unist-util-position": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", @@ -4345,6 +5544,20 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/unist-util-stringify-position": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", @@ -4373,6 +5586,19 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/unist-util-visit-children": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-3.0.0.tgz", + "integrity": "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/unist-util-visit-parents": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", @@ -4497,6 +5723,20 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/vfile-message": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", @@ -4620,6 +5860,16 @@ "node": ">=18" } }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/webidl-conversions": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", diff --git a/packages/wherefore-dashboard/package.json b/packages/wherefore-dashboard/package.json index ea1ffa0..581b0a3 100644 --- a/packages/wherefore-dashboard/package.json +++ b/packages/wherefore-dashboard/package.json @@ -41,6 +41,7 @@ "test": "node --test --test-concurrency=1" }, "dependencies": { + "@astrojs/markdown-remark": "^7.2.1", "astro": "^7.0.0" }, "publishConfig": { diff --git a/packages/wherefore-dashboard/src/lib/entry-sections.ts b/packages/wherefore-dashboard/src/lib/entry-sections.ts index 7536692..93d19d4 100644 --- a/packages/wherefore-dashboard/src/lib/entry-sections.ts +++ b/packages/wherefore-dashboard/src/lib/entry-sections.ts @@ -4,6 +4,8 @@ // ## Summary / ## Decisions / outcomes / ## Why / // ## Alternatives considered / ## Open questions / follow-ups +import { rewriteMdHref } from './md-links.mjs'; + export interface Decision { verdict: string; detail: string; } export interface Alternative { option: string; reason: string; } export interface OpenQuestion { id: string | null; text: string; } @@ -113,14 +115,23 @@ export function parseEntry(body: string): EntrySections { * code spans, links, and bold. Used via set:html so authored `code`, links, and * **emphasis** don't leak through as literal markup. Content is repo-trusted. */ -export function renderInline(text: string): string { +export function renderInline(text: string, mdLinkMap?: Map): string { if (!text) return ''; let h = text .replace(/&/g, '&') .replace(//g, '>'); - h = h.replace(/`([^`]+)`/g, (_m, c) => `${c}`); - h = h.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, (_m, t, u) => `${t}`); + // Pull code spans out to placeholders first, so link/bold syntax shown INSIDE a + // code span (e.g. a `[label](path.md)` example) is not reprocessed into a real + // link, matching how a real Markdown parser protects code spans. + const codes: string[] = []; + h = h.replace(/`([^`]+)`/g, (_m, c) => { + codes.push(`${c}`); + return `\u0000${codes.length - 1}\u0000`; + }); + h = h.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, + (_m, t, u) => `${t}`); h = h.replace(/\*\*([^*]+)\*\*/g, '$1').replace(/__([^_]+)__/g, '$1'); + h = h.replace(/\u0000(\d+)\u0000/g, (_m, i) => codes[Number(i)]); return h; } diff --git a/packages/wherefore-dashboard/src/lib/md-links.mjs b/packages/wherefore-dashboard/src/lib/md-links.mjs new file mode 100644 index 0000000..2c9251b --- /dev/null +++ b/packages/wherefore-dashboard/src/lib/md-links.mjs @@ -0,0 +1,83 @@ +// Rewrites the relative Markdown cross-links that wherefore items use in their +// bodies (e.g. `[P-004: label](P-004-slug.md)`, `[..](../log/2026-..-slug.md)`) +// onto the dashboard's routes. Authored links point at the target FILE so they +// stay portable (GitHub, Obsidian, VS Code); the dashboard serves routes, so we +// map filename -> route at build time and swap the href. +// +// buildMdLinkMap() runs at config time (node:fs), rewriteMdHref() is pure so it +// is shared by the rehype plugin (render() pages) and renderInline (log page). + +import { readdirSync, readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; + +function readMdFiles(dir) { + try { + return readdirSync(dir).filter(f => f.endsWith('.md')); + } catch { + return []; + } +} + +function frontmatterId(dir, file) { + try { + const content = readFileSync(resolve(dir, file), 'utf-8'); + const fm = content.match(/^---\r?\n([\s\S]*?)\r?\n---/); + if (!fm) return null; + const m = fm[1].match(/^id:\s*(\S+)\s*$/m); + return m ? m[1] : null; + } catch { + return null; + } +} + +/** + * Build a `Map` for every linkable wherefore item under `src`. + * Keyed by bare filename: the three collections use disjoint name shapes + * (`P-*.md`, `Q-*.md`, `YYYY-MM-DD-*.md`), so a filename is globally unambiguous + * and callers never have to resolve `../` paths. + * plan/P-*.md -> /plan/ (route keys off the id: frontmatter) + * questions/Q-*.md -> /questions (single page; no per-question route) + * log/*.md -> /log/ (slug = filename without .md) + */ +export function buildMdLinkMap(src) { + const map = new Map(); + if (!src) return map; + + const planDir = resolve(src, 'plan'); + for (const file of readMdFiles(planDir)) { + if (!file.startsWith('P-')) continue; // skip README.md and other non-items + const id = frontmatterId(planDir, file); + if (id) map.set(file, `/plan/${id}`); + } + + for (const file of readMdFiles(resolve(src, 'questions'))) { + if (!file.startsWith('Q-')) continue; + map.set(file, '/questions'); + } + + for (const file of readMdFiles(resolve(src, 'log'))) { + map.set(file, `/log/${file.replace(/\.md$/, '')}`); + } + + return map; +} + +/** + * Rewrite one href. Relative `*.md` links whose filename is known become the + * dashboard route (preserving any `#fragment`); everything else is untouched: + * external URLs, anchors, absolute paths, and `.md` links to unknown targets + * (which pass through rather than breaking). + */ +export function rewriteMdHref(href, map) { + if (!href) return href; + if (/^(https?:|mailto:|tel:|#|\/)/i.test(href)) return href; + + const hashAt = href.indexOf('#'); + const path = hashAt === -1 ? href : href.slice(0, hashAt); + const frag = hashAt === -1 ? '' : href.slice(hashAt); + if (!/\.md$/i.test(path)) return href; + + const filename = path.split('/').pop(); + const route = map && map.get(filename); + return route ? route + frag : href; +} diff --git a/packages/wherefore-dashboard/src/lib/rehype-md-links.mjs b/packages/wherefore-dashboard/src/lib/rehype-md-links.mjs new file mode 100644 index 0000000..d91bf6a --- /dev/null +++ b/packages/wherefore-dashboard/src/lib/rehype-md-links.mjs @@ -0,0 +1,29 @@ +// Rehype plugin: rewrite `` cross-links to dashboard routes, +// using a prebuilt filename->route map. Applies to every body rendered through +// the Astro markdown pipeline (render()/): plan detail, questions, +// and the now-view in-flight cards. Log bodies bypass this pipeline (they use +// renderInline), so they call rewriteMdHref directly. +// +// Hand-walks the hast tree to avoid a unist-util-visit dependency. + +import { rewriteMdHref } from './md-links.mjs'; + +export default function rehypeMdLinks(options = {}) { + const map = options.map; + + function visit(node) { + if ( + node.type === 'element' && + node.tagName === 'a' && + node.properties && + typeof node.properties.href === 'string' + ) { + node.properties.href = rewriteMdHref(node.properties.href, map); + } + if (node.children) { + for (const child of node.children) visit(child); + } + } + + return (tree) => visit(tree); +} diff --git a/packages/wherefore-dashboard/src/pages/log/[slug].astro b/packages/wherefore-dashboard/src/pages/log/[slug].astro index de4d999..9bf410a 100644 --- a/packages/wherefore-dashboard/src/pages/log/[slug].astro +++ b/packages/wherefore-dashboard/src/pages/log/[slug].astro @@ -2,6 +2,7 @@ import { getCollection } from 'astro:content'; import Base from '../../layouts/Base.astro'; import { parseEntry, renderInline } from '../../lib/entry-sections'; +import { buildMdLinkMap } from '../../lib/md-links.mjs'; export async function getStaticPaths() { const entries = await getCollection('log'); @@ -18,6 +19,12 @@ const prev = supersedes ? allEntries.find(e => e.id === supersedes) ? const sections = parseEntry(entry.body ?? ''); +// Log bodies render through renderInline (not the markdown pipeline), so rewrite +// their relative .md cross-links to routes here with the same map the rehype +// plugin uses on the other pages. +const mdLinkMap = buildMdLinkMap(process.env.WHEREFORE_SRC); +const md = (t: string) => renderInline(t, mdLinkMap); + const statusClass = status === 'superseded' ? 'is-superseded' : status === 'obsolete' ? 'is-obsolete' : ''; @@ -65,7 +72,7 @@ const pad = (n: number) => String(n).padStart(2, '0'); {sections.summary.length > 0 && (
- {sections.summary.map(p =>

)} + {sections.summary.map(p =>

)}

)} @@ -76,8 +83,8 @@ const pad = (n: number) => String(n).padStart(2, '0');
{pad(i + 1)}
-

- {d.detail &&

} +

+ {d.detail &&

}

))} @@ -87,7 +94,7 @@ const pad = (n: number) => String(n).padStart(2, '0'); {sections.why.length > 0 && ( <>
Why
- {sections.why.map(p =>

)} + {sections.why.map(p =>

)} )} @@ -100,7 +107,7 @@ const pad = (n: number) => String(n).padStart(2, '0'); Rejected

{a.option} - {a.reason && <> } + {a.reason && <> }
))} @@ -115,7 +122,7 @@ const pad = (n: number) => String(n).padStart(2, '0'); {sections.questions.map(q => (
{q.id && {q.id}} - +
))} diff --git a/packages/wherefore-dashboard/tests/entry-sections.test.mjs b/packages/wherefore-dashboard/tests/entry-sections.test.mjs index fa52cac..b02eb6f 100644 --- a/packages/wherefore-dashboard/tests/entry-sections.test.mjs +++ b/packages/wherefore-dashboard/tests/entry-sections.test.mjs @@ -68,6 +68,32 @@ test('renderInline escapes HTML then renders code, links, and bold', () => { assert.equal(renderInline('**bold** and __also__'), 'bold and also'); }); +test('renderInline rewrites relative .md links when given a map', () => { + const map = new Map([['2026-01-03-replacement-example.md', '/log/2026-01-03-replacement-example']]); + assert.equal( + renderInline('see [it](2026-01-03-replacement-example.md)', map), + 'see
it' + ); + // Without a map, the authored link is left as-is. + assert.equal( + renderInline('see [it](2026-01-03-replacement-example.md)'), + 'see it' + ); +}); + +test('renderInline protects link syntax shown inside a code span', () => { + // A `[label](path.md)` example in backticks stays literal, not a real . + assert.equal( + renderInline('use `[P-004: x](P-004-x.md)` here'), + 'use [P-004: x](P-004-x.md) here' + ); + // Numbers in surrounding prose are not mistaken for code-span placeholders. + assert.equal( + renderInline('at step 3 run `x` then step 4'), + 'at step 3 run x then step 4' + ); +}); + test('parseEntry drops "None" items and structures every section', () => { const body = [ '## Summary', diff --git a/packages/wherefore-dashboard/tests/fixtures/wherefore/log/2026-01-01-active-example.md b/packages/wherefore-dashboard/tests/fixtures/wherefore/log/2026-01-01-active-example.md index aa47dc6..c71cf6b 100644 --- a/packages/wherefore-dashboard/tests/fixtures/wherefore/log/2026-01-01-active-example.md +++ b/packages/wherefore-dashboard/tests/fixtures/wherefore/log/2026-01-01-active-example.md @@ -31,3 +31,4 @@ later context. ## Open questions / follow-ups - Q-001: Should we rate-limit per user or per IP? +- See also [2026-01-03-replacement-example: the replacement](2026-01-03-replacement-example.md) diff --git a/packages/wherefore-dashboard/tests/fixtures/wherefore/plan/P-002-paginate-catalog-search.md b/packages/wherefore-dashboard/tests/fixtures/wherefore/plan/P-002-paginate-catalog-search.md index d76000b..76b963d 100644 --- a/packages/wherefore-dashboard/tests/fixtures/wherefore/plan/P-002-paginate-catalog-search.md +++ b/packages/wherefore-dashboard/tests/fixtures/wherefore/plan/P-002-paginate-catalog-search.md @@ -7,4 +7,5 @@ area: catalog topics: [performance] --- -Cursor pagination for the search results list. Not started yet. +Cursor pagination for the search results list. Not started yet. Builds on the +flag pattern from [P-001: rate limiter](P-001-checkout-rate-limiter.md). diff --git a/packages/wherefore-dashboard/tests/md-links.test.mjs b/packages/wherefore-dashboard/tests/md-links.test.mjs new file mode 100644 index 0000000..0beb263 --- /dev/null +++ b/packages/wherefore-dashboard/tests/md-links.test.mjs @@ -0,0 +1,48 @@ +// Unit tests for the cross-link map builder and href rewriter. buildMdLinkMap +// scans the fixture wherefore dir; rewriteMdHref is pure. + +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { buildMdLinkMap, rewriteMdHref } from '../src/lib/md-links.mjs'; +import { FIXTURES } from './helpers.mjs'; + +test('buildMdLinkMap maps plan by id, questions to /questions, log by slug', () => { + const map = buildMdLinkMap(FIXTURES); + + // plan: filename -> /plan/ (id from frontmatter, not the filename) + assert.equal(map.get('P-001-checkout-rate-limiter.md'), '/plan/P-001'); + assert.equal(map.get('P-004-billing-webhook-retries.md'), '/plan/P-004'); + // plan/README.md is not an item and must not be mapped + assert.equal(map.has('README.md'), false); + + // questions: filename -> /questions (single page, no per-question route) + assert.equal(map.get('Q-001.md'), '/questions'); + + // log: filename -> /log/ (slug = filename without .md) + assert.equal(map.get('2026-01-03-replacement-example.md'), '/log/2026-01-03-replacement-example'); +}); + +test('buildMdLinkMap returns an empty map when src is undefined', () => { + assert.equal(buildMdLinkMap(undefined).size, 0); +}); + +test('rewriteMdHref resolves known relative .md links, preserving fragments', () => { + const map = buildMdLinkMap(FIXTURES); + + assert.equal(rewriteMdHref('P-001-checkout-rate-limiter.md', map), '/plan/P-001'); + // cross-collection relative path resolves by basename + assert.equal(rewriteMdHref('../log/2026-01-03-replacement-example.md', map), '/log/2026-01-03-replacement-example'); + // fragment is carried through + assert.equal(rewriteMdHref('P-001-checkout-rate-limiter.md#why', map), '/plan/P-001#why'); +}); + +test('rewriteMdHref leaves everything else untouched', () => { + const map = buildMdLinkMap(FIXTURES); + + assert.equal(rewriteMdHref('https://example.com/a.md', map), 'https://example.com/a.md'); + assert.equal(rewriteMdHref('/log/already-a-route', map), '/log/already-a-route'); + assert.equal(rewriteMdHref('#anchor', map), '#anchor'); + assert.equal(rewriteMdHref('P-999-does-not-exist.md', map), 'P-999-does-not-exist.md'); + assert.equal(rewriteMdHref('notes.txt', map), 'notes.txt'); + assert.equal(rewriteMdHref('', map), ''); +}); diff --git a/packages/wherefore-dashboard/tests/render.test.mjs b/packages/wherefore-dashboard/tests/render.test.mjs index e2d2d59..34fb4d2 100644 --- a/packages/wherefore-dashboard/tests/render.test.mjs +++ b/packages/wherefore-dashboard/tests/render.test.mjs @@ -243,6 +243,22 @@ test('plan detail: dropped item shown (not hidden) with reason and retired-by li assert.equal(d.querySelector('.ref-list a').getAttribute('href'), '/log/2026-01-03-replacement-example'); }); +// ---- cross-link rewriting (.md links -> routes) ---------------------------- + +test('plan body: relative .md cross-link is rewritten to a route (rehype path)', () => { + const d = parse('plan/P-002/index.html'); + const link = d.querySelector('.plan-body a[href="/plan/P-001"]'); + assert.ok(link, 'the [P-001](P-001-...md) body link resolves to /plan/P-001'); + assert.match(text(link), /P-001/); +}); + +test('log body: relative .md cross-link is rewritten to a route (renderInline path)', () => { + const d = parse('log/2026-01-01-active-example/index.html'); + const link = d.querySelector('.wf-qs a'); + assert.ok(link, 'the "See also" body link is present'); + assert.equal(link.getAttribute('href'), '/log/2026-01-03-replacement-example'); +}); + // ---- client-side filters (scripts executed by jsdom) ----------------------- test('questions filter: resolved tab and no-results search', () => { diff --git a/plugins/wherefore/commands/seed.md b/plugins/wherefore/commands/seed.md index f9aac9d..46ff116 100644 --- a/plugins/wherefore/commands/seed.md +++ b/plugins/wherefore/commands/seed.md @@ -109,7 +109,8 @@ approval; wait for their reply before writing anything. Body: freeform. The `slate` skill breaks the work into `- [ ]` checkboxes by default (steps concrete enough to check off); prose stays valid, and older items may be - prose-only. + prose-only. References to other items in the body use a relative Markdown link (see + Linking conventions below). ## Status vocabulary @@ -123,6 +124,12 @@ approval; wait for their reply before writing anything. ## Linking conventions + The frontmatter refs below stay bare IDs/slugs. In the BODY, link to another item with a + relative Markdown link, ID-first in the text: `[P-005: CI validator](P-005-ci-schema-validator.md)` + (same directory) or `[Q-007: token store](../questions/Q-007-token-store.md)` (sibling + collection). Never a bare slug or a `[[wikilink]]`: those render as literal text on GitHub + and most editors. + - `milestone`: a single milestone ID (`M1`) defined in `wherefore/ROADMAP.md`. One-way: the item points up at the milestone. - `decision_ref`: one or more decision log slugs in `YYYY-MM-DD-slug` form (no `.md`), diff --git a/plugins/wherefore/skills/capture/SKILL.md b/plugins/wherefore/skills/capture/SKILL.md index a8a2344..ea3db22 100644 --- a/plugins/wherefore/skills/capture/SKILL.md +++ b/plugins/wherefore/skills/capture/SKILL.md @@ -117,7 +117,7 @@ scalar to a one-line summary and move the detail into a body section. 1. Get the source. A summary, or raw pasted discussion. If raw, distill it: extract decisions and rationale, drop the chatter. -2. One entry or many? Split into one file per thread when threads are independently queryable: different areas, different stories, and reversible without affecting each other. Keep one file when causally linked (one decision led to or constrained another). When splitting, run each thread through the whole workflow and cross-link companions in each entry's Open questions section ("See also: 2026-06-24-foo"). Report how many files and why. +2. One entry or many? Split into one file per thread when threads are independently queryable: different areas, different stories, and reversible without affecting each other. Keep one file when causally linked (one decision led to or constrained another). When splitting, run each thread through the whole workflow and cross-link companions in each entry's Open questions section with a relative Markdown link, e.g. `See also [2026-06-24-foo: title](2026-06-24-foo.md)` (same directory for log-to-log). Any in-body reference to another item uses this Markdown-link form, never a bare slug or a `[[wikilink]]`; see AGENTS.md "Linking". Report how many files and why. 3. Date. Default to today. Use a stated date only if the user gives one ("yesterday's huddle"). @@ -142,7 +142,7 @@ scalar to a one-line summary and move the detail into a body section. On a confirmed replacement: - New entry frontmatter: `supersedes: `. - Old entry frontmatter: `status: superseded`, `superseded_by: `, `superseded_date: YYYY-MM-DD`. - - Old entry first body line (after frontmatter, before `## Summary`): `SUPERSEDED YYYY-MM-DD -> see . Kept for history, not current.` + - Old entry first body line (after frontmatter, before `## Summary`): `SUPERSEDED YYYY-MM-DD -> see [](.md). Kept for history, not current.` (relative Markdown link, per AGENTS.md "Linking"). 8. Write `wherefore/log/YYYY-MM-DD-short-slug.md`. Slug short, lowercase, hyphenated, recognizable (`oauth-token-refresh`, not `discussion-about-the-auth-stuff`). If the name exists, add a short suffix; never overwrite. diff --git a/plugins/wherefore/skills/resolve/SKILL.md b/plugins/wherefore/skills/resolve/SKILL.md index f60d0ab..430e994 100644 --- a/plugins/wherefore/skills/resolve/SKILL.md +++ b/plugins/wherefore/skills/resolve/SKILL.md @@ -66,8 +66,9 @@ summary and move detail into a `## Resolution` body section. `status` and 4. **Update the source wherefore entry (if applicable).** If the resolution came from a specific wherefore entry, open `wherefore/log/.md` and add a note in its - "Open questions / follow-ups" section next to the relevant item: - `- Q-042: (resolved, see wherefore/questions/Q-042-.md)` + "Open questions / follow-ups" section next to the relevant item, linking the question + with a relative Markdown link (per AGENTS.md "Linking"): + `- Q-042: (resolved, see [Q-042](../questions/Q-042-.md))` 5. **Report back.** Show: - The question text and its ID diff --git a/plugins/wherefore/skills/slate/SKILL.md b/plugins/wherefore/skills/slate/SKILL.md index 4c8c690..37e28ca 100644 --- a/plugins/wherefore/skills/slate/SKILL.md +++ b/plugins/wherefore/skills/slate/SKILL.md @@ -141,6 +141,11 @@ done for specifics ("what are the actual steps?") rather than writing filler. Never bulk-generate items from a vague prompt: a plan item is a commitment, and manufacturing commitments in bulk is the failure this collection exists to avoid. + When a checkbox or prose references another wherefore item, link it with a relative + Markdown link, e.g. `- [ ] fold in [P-005: CI validator](P-005-ci-schema-validator.md)` + or `[Q-007: token store](../questions/Q-007-token-store.md)`, never a bare slug or a + `[[wikilink]]` (see AGENTS.md "Linking"). Frontmatter refs (`decision_ref`, + `question_ref`, `milestone`, `answers`) stay bare, not links. 3. Set frontmatter: `id`, `title`, `status: todo`, `created` today. Add `area`/`topics` from `topics.md` if the item has them; add `milestone`, `question_ref`, or `decision_ref` if the user gives them. Do not set `updated` on a brand-new item. diff --git a/plugins/wherefore/skills/supersede/SKILL.md b/plugins/wherefore/skills/supersede/SKILL.md index ff21ea2..b04a782 100644 --- a/plugins/wherefore/skills/supersede/SKILL.md +++ b/plugins/wherefore/skills/supersede/SKILL.md @@ -65,9 +65,9 @@ Never delete anything under a `wherefore/` data dir. Retire, do not delete. 4. **Add a one-line banner** as the first body line of the target entry, after the closing `---` of the frontmatter, before `## Summary`. No emoji. - Superseded: + Superseded (the pointer is a relative Markdown link, per AGENTS.md "Linking"): ``` - SUPERSEDED YYYY-MM-DD -> see . Kept for history, not current. + SUPERSEDED YYYY-MM-DD -> see [](.md). Kept for history, not current. ``` Obsolete: diff --git a/wherefore/log/2026-06-24-adr-positioning.md b/wherefore/log/2026-06-24-adr-positioning.md index 08fbcc1..b2a868c 100644 --- a/wherefore/log/2026-06-24-adr-positioning.md +++ b/wherefore/log/2026-06-24-adr-positioning.md @@ -36,4 +36,4 @@ and supersession bookkeeping, so the log actually gets maintained. - ADR in the product name. Rejected: caps the concept at "an ADR tool". ## Open questions / follow-ups -- See also: 2026-06-24-rename-to-lore (the naming side of the same instinct). +- See also: [2026-06-24-rename-to-lore](2026-06-24-rename-to-lore.md) (the naming side of the same instinct). diff --git a/wherefore/log/2026-06-24-rename-to-lore.md b/wherefore/log/2026-06-24-rename-to-lore.md index e9d4829..2caacac 100644 --- a/wherefore/log/2026-06-24-rename-to-lore.md +++ b/wherefore/log/2026-06-24-rename-to-lore.md @@ -39,4 +39,4 @@ being created, because renaming after installs breaks the install string. - ADR in the name. Rejected: see 2026-06-24-adr-positioning. ## Open questions / follow-ups -- See also: 2026-06-24-adr-positioning. +- See also: [2026-06-24-adr-positioning](2026-06-24-adr-positioning.md). diff --git a/wherefore/log/2026-07-03-plan-directory.md b/wherefore/log/2026-07-03-plan-directory.md index 6aae826..29dc0ab 100644 --- a/wherefore/log/2026-07-03-plan-directory.md +++ b/wherefore/log/2026-07-03-plan-directory.md @@ -37,4 +37,4 @@ actually used. ## Open questions / follow-ups - None. Rendering plan/ in the dashboard is out of scope until entries exist. -- See also: 2026-07-03-wherefore-cli-and-multi-agent-skills. +- See also: [2026-07-03-wherefore-cli-and-multi-agent-skills](2026-07-03-wherefore-cli-and-multi-agent-skills.md). diff --git a/wherefore/log/2026-07-18-cross-agent-skill-validation.md b/wherefore/log/2026-07-18-cross-agent-skill-validation.md index da05e73..0a48854 100644 --- a/wherefore/log/2026-07-18-cross-agent-skill-validation.md +++ b/wherefore/log/2026-07-18-cross-agent-skill-validation.md @@ -31,4 +31,4 @@ Both mechanisms work, which is the evidence Q-008 was waiting on. ## Open questions / follow-ups - Q-008 stays open: ship per-agent skills, or rely solely on the AGENTS.md floor. This session is evidence that both work, not a decision on which to keep. -- See also: 2026-07-04-init-skills-on-by-default and 2026-06-25-agents-md-cross-tool. +- See also: [2026-07-04-init-skills-on-by-default](2026-07-04-init-skills-on-by-default.md) and [2026-06-25-agents-md-cross-tool](2026-06-25-agents-md-cross-tool.md). diff --git a/wherefore/log/2026-07-19-markdown-crosslinks.md b/wherefore/log/2026-07-19-markdown-crosslinks.md new file mode 100644 index 0000000..b76306b --- /dev/null +++ b/wherefore/log/2026-07-19-markdown-crosslinks.md @@ -0,0 +1,40 @@ +--- +date: 2026-07-19 +title: "Standardize cross-links as relative Markdown links" +areas: [plugin, dashboard] +topics: [data-model, docs] +stories: [] +status: active +supersedes: +superseded_by: +superseded_date: +--- + +## Summary +Standardized how wherefore items reference each other inside a body. Cross-references +now use a standard relative Markdown link to the target file, not a bare slug and not an +Obsidian `[[wikilink]]`. The skills write this format, the docs specify it, and the +dashboard rewrites the `.md` links to routes at build time. + +## Decisions / outcomes +- Link to another item in body prose with a relative Markdown link to its file, ID first in the text: `[P-004: label](P-004-slug.md)` (same directory) or `[Q-007: label](../questions/Q-007-slug.md)` (sibling collection). +- Reject Obsidian `[[wikilinks]]`. They render as literal text on GitHub and most editors, which is the opposite of the portability goal. +- Keep frontmatter refs (`decision_ref`, `question_ref`, `supersedes`, `asked_slug`, `resolution_slug`) as bare IDs/slugs. The standard is for body prose only. +- The dashboard resolves these `.md` links to routes at build time: a rehype plugin for the pipeline pages and `renderInline` for log bodies, both keyed off one filename-to-route map. + +## Why +Universal compatibility was the priority. A standard Markdown link renders as a real link +everywhere: GitHub, Obsidian, VS Code, and the dashboard. Wikilinks only resolve in the +Obsidian family, so they trade away the GitHub and generic-editor reading that wherefore +depends on. Frontmatter stays bare because it is parsed, never rendered, so linkifying it +only adds fragility. The dashboard rewrite closes the one gap: authored links point at the +source file, and the dashboard serves routes. Running a rehype plugin on this Astro version +meant adding the first-party `@astrojs/markdown-remark` processor. + +## Alternatives considered +- Obsidian `[[wikilinks]]`, rejected. Most Obsidian-native, but the least portable: literal text on GitHub and outside the Obsidian ecosystem. +- Standardize the docs only and defer the dashboard, rejected. It would leave new `.md` links pointing at files that 404 inside the dashboard. + +## Open questions / follow-ups +- Question deep-link anchors (`/questions#Q-NNN`) are deferred; a link to a question resolves to the `/questions` page for now. +- See also [2026-06-25-underscore-frontmatter-keys](2026-06-25-underscore-frontmatter-keys.md), the other repo-wide format standard. diff --git a/wherefore/plan/README.md b/wherefore/plan/README.md index 118474d..4a63ed7 100644 --- a/wherefore/plan/README.md +++ b/wherefore/plan/README.md @@ -37,7 +37,8 @@ superseded / obsolete here, and do not put todo / doing / done / dropped on deci Body: freeform. The `slate` skill breaks the work into `- [ ]` checkboxes by default (steps concrete enough to check off); prose stays valid, and older items may be -prose-only. +prose-only. References to other items in the body use a relative Markdown link (see +Linking conventions below). ## Status vocabulary @@ -51,6 +52,12 @@ investigating a question) is not blocked; `answers` is the opposite relationship ## Linking conventions +The frontmatter refs below stay bare IDs/slugs. In the BODY, link to another item with a +relative Markdown link, ID-first in the text: `[P-005: CI validator](P-005-ci-schema-validator.md)` +(same directory) or `[Q-007: token store](../questions/Q-007-token-store.md)` (sibling +collection). Never a bare slug or a `[[wikilink]]`: those render as literal text on GitHub +and most editors. + - `milestone`: a single milestone ID (`M1`) defined in `wherefore/ROADMAP.md`. One-way: the item points up at the milestone. - `decision_ref`: one or more decision log slugs in `YYYY-MM-DD-slug` form (no `.md`),