Agent-friendly documentation: .md companions, llms.txt, coding-agent guide (HF-154) - #1703
Conversation
…cking Used by the prep-flip T2.5 tier to extract the HyperFormula public API surface at a given git ref and compare between develop and PR HEAD to detect breaking API changes (constructor arg additions, removed exports, lint-scope drift).
…int config Add scripts/*.js override to .eslintrc.js enabling node env and disabling TypeScript-specific rules that do not apply to CJS scripts. Auto-fix semicolons (project enforces no-semicolons style), rename lint_scope variable to lintScope (naming-convention), and add missing JSDoc param/returns descriptions.
Five-component design: VuePress md-companions plugin, Copy Markdown button, static setup-coding-agent page, interactive wizard, and llms.txt/robots.txt updates. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Address 5 findings grounded in VuePress core verification: - respect base '/docs/' and DOCS_DEST in all 5 components - compute Copy Markdown URL from $page.path, not window.location - forbid invented install commands; trace to published skill - token-aware container stripping with code-fence fixture test - flag robots.txt root-vs-/docs/ placement for confirmation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Six tasks: md-companions plugin (C1), Copy Markdown button (C2), setup-coding-agent page (C3), interactive wizard (C4), llms.txt/robots (C5), and config.js integration. Install commands traced to the published handsontable/handsontable-skills repo; code-fence stripping fixture test included. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Install commands traced to handsontable/handsontable-skills; no invented commands. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…mpanions Strip.js regex was limited to tip|warning|danger|details. The ::: example container (live code runner) leaked into companion .md files. Broadened match to \w+ and strip example containers entirely (they are demo markup, not prose). Added 8th assertion to the test fixture. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…p page - require md-companions plugin, register with DOCS_HOSTNAME - globalUIComponents: ['CopyMarkdownButton'] - add 'Set up your coding agent' to Getting started sidebar Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
strip.js: - track full fence marker length (CommonMark); inner ``` no longer closes ```` - bail out verbatim on unclosed ::: container (no silent page truncation) - 9th test assertion for nested fence lengths index.js: - wrap per-page work in try/catch; one bad page warns and continues - substitute real base/hostname in corpus header (was literal <base>) CopyMarkdownButton.vue + CodingAgentWizard.vue: - clipboard fallback via document.execCommand for non-HTTPS contexts - isSecureContext guard + .catch() on Clipboard API promise - null-guard on this.current in wizard copy() setup-coding-agent.md: - relative links to llms-full.txt (../llms-full.txt) to survive any base-path edge cases in static-file link resolution Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The md-companions markdown stripper is the acceptance gate for the agent-friendly docs (per-page .md companions + llms.txt), but its test lived at docs/.vuepress/plugins/md-companions/strip.test.js — a path and suffix Jest's testMatch (`test/**/*spec.(ts|js)`) never discovers, so it never ran in CI. - Move the assertions into test/docs/md-companions-strip.spec.js as a Jest spec, where the default glob picks them up. The file is `.js`, so Karma's `.spec.ts`-only require.context skips it (the stripper is plain Node code, not part of the browser bundle). - Add an ESLint override for `**/test/**/*.spec.js` mirroring the existing `scripts/*.js` relaxations (node env, allow require of untyped modules). - Remove the orphan strip.test.js. - CHANGELOG: note the agent-friendly docs under [Unreleased] > Added. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
These are internal implementation planning docs, not user-facing documentation. VuePress builds every file under docs/ into the public site (and the md-companions plugin would emit .md + llms.txt for them), so they must not live in the public repo. They belong in the internal dev-skills workspace instead.
…rve-out scripts/extract-public-api.js was an internal helper for the prep-flip review harness (per its own header), has no consumer anywhere in this repo, and is out of scope for an agent-friendly-docs PR. Removing it also resolves the two outstanding review threads it was the sole source of: the CodeQL 'indirect uncontrolled command line' alert (execSync git show) and Cursor's regex finding in extractHyperFormulaExports. The scripts/*.js ESLint override existed only to lint this file, so it is removed too; the **/test/**/*.spec.js override stays (needed by the md-companions strip spec). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Task linked: HF-154 Agent-friendly docs |
✅ Deploy Preview for hyperformula-dev-docs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
e79f1a8 to
ddfa8f0
Compare
Performance comparison of head (f2e95fc) vs base (23a7437) |
The docs build (`npm run docs:build`) is validated in CI on Node 22 (the build-docs workflow), but Netlify built the deploy preview on Node 18, where the build errored. Aligning the deploy runtime with the validated CI runtime. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ents - CopyMarkdownButton copies the page's .md URL (per its tooltip), so the label now reads 'Copy Markdown link' instead of 'Copy Markdown', which implied it copied the markdown content. Guide reference updated to match. - Removed the two 'AI/LLM agent index' comment lines from robots.txt: crawlers ignore comments, so they provided no machine-discoverability. llms.txt is discoverable at its well-known path; the Sitemap directive stays. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Verified the deploy-preview build output: llms-full.txt is populated (1.3 MB, page content present) and guide pages strip cleanly, but the typedoc API pages leaked ~638 inline `<Badge text="Class"/>` tags — the stripper only dropped component tags that occupied a whole line, not inline ones after heading text. Now strips inline self-closing capitalized components too; the [A-Z] guard leaves plain HTML (<br/>) alone and code fences are still preserved. +3 spec assertions (12 total). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ent tags - Extract the shared Clipboard-API-with-textarea-fallback logic from CopyMarkdownButton.vue and CodingAgentWizard.vue into a single components/clipboard.js (copyToClipboard); both now import it. - strip.js: also drop a whole-line closing tag of a paired block component (</Foo>); the opening tag and inner content were already handled, only the stray closing tag leaked. +1 spec assertion (13). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bugbot (Medium): ::: tip/warning bodies were blockquoted and pushed without the inline Vue-markup cleanup normal prose gets, so [[toc]], Vue-bound :href/:src and inline <Badge/> inside a container leaked into companions. Extracted the cleanup into cleanInlineMarkup() and apply it to container bodies as well as plain lines (also de-duplicates the rule set). +1 spec. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| v-if="mdUrl" | ||
| class="copy-md-button" | ||
| type="button" | ||
| :title="'Copy this page as Markdown URL for LLMs'" |
There was a problem hiding this comment.
Instedy of button "copy as markdown" I'd prefer "view as markdown" on top of each documentation page. I think agent will more likely click a link and read the clipboard content and then navigate to it.
There was a problem hiding this comment.
Done — the "copy as markdown" button is replaced by a "View as Markdown" link near the top of each guide page, linking to that page's .md companion.
There was a problem hiding this comment.
I don't see the "View as Markdown" link near the top of each guide. @cursor fix it.
…arkdown link Kuba's review ask: agents are more likely to click a link and read the resulting page than click a button that copies to the clipboard. - Rename CopyMarkdownButton.vue -> ViewMarkdownLink.vue; replace the clipboard-copy <button> with a plain <a :href="mdUrl"> that navigates to the page's .md companion. Drop the now-unused clipboard import/copy method and the fixed bottom-right styling (clipboard.js itself is kept - still used by CodingAgentWizard.vue). - Add a local VuePress theme (docs/.vuepress/theme) that extends @vuepress/theme-default and overrides layouts/Layout.vue to fill the theme's `page-top` slot with <ViewMarkdownLink/>, so the link renders above the article content on every guide page instead of as a global floating overlay. Drop the `globalUIComponents` config entry this replaces. - Update the stale "Copy Markdown link button" mention in setup-coding-agent.md to match the new label.
The md-companions plugin mirrored llms.txt / llms-full.txt one level above ctx.outDir (the served root), which contradicts netlify.toml: the root /llms.txt is owned by the website repo and advertises more than the docs. Write only the /docs/ copies and leave the root corpus alone. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…anions
The stripper dropped the destination of `<a :href="…">` (kept only the text)
and removed `<img :src="…">` entirely, and `{{ $page.* }}` interpolations
shipped raw — so the LLM corpus lost function counts, download/Stackblitz
links and architecture diagrams on exactly the pages agents want.
- cleanInlineMarkup now extracts the first string literal from the :href/:src
binding expression and emits a real Markdown link / image (falls back to
text / removal when there is no literal).
- index.js resolves the injected `{{ $page.version|buildDate|
buildDateURIEncoded|releaseDate|functionsCount }}` against the page before
stripping; other interpolations are left untouched.
Verified with a harness over the real strip pipeline (links, images, page
vars, plus <br/> and PascalCase-component regression cases).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The root /llms.txt (owned by the website repo) is the only agent-discovery entry point; /docs/llms.txt is off that path and cannot be promoted to root, so it added nothing. The docs corpus stays reachable at /docs/llms-full.txt. Addresses the review comment on the llms.txt duplication. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Dev/process guidance doesn't belong in the product repo; relocated to hyperformula-tests/dev_docs (same convention as the sort/unique ADR). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 91e00fd. Configure here.
…md companions Container-body emit ran every line through cleanInlineMarkup, including lines inside fenced code blocks, so Vue-shaped samples (`<a :href>`, `<Badge/>`, `[[toc]]`) shown in a :::tip/:::warning code sample were rewritten or dropped in the generated companions. The body scan already tracked the fences to find the real `:::` closer, but that state was discarded on emit. Re-derive fence state during emit and skip cleanInlineMarkup for code lines (still quoted with `> `). Prose lines in containers are unchanged and still cleaned. Reported by Cursor Bugbot on #1703. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@marcin-kordas-hoc , perhaps we should finish the docs migration to cloudflare first, and then use their markdown for agents feature, rather that write a custom vuepress->markdown script. We're already using this with the handsontable docs. Also, we plan to migrate the hyperformula docs to Atro, so the vuewpress->markdown script won't be needed after that either. |
Co-authored-by: Kuba Sekowski <sequba@gmail.com>
…avbar The link rendered via the theme's `page-top` slot lands at the very top of `main.page`, a zone the fixed navbar (z-index 20, height $navbarHeight) covers — the default theme clears the navbar only with margins inside `.theme-default-content`. The link was present in the DOM on every page but fully hidden behind the navbar, hence 'I don't see the link' in review. Give the wrapper navbar clearance and mirror the content wrapper's $wrapper geometry (max-width, centering, responsive side paddings) so the link sits right above the article column. Verified in a headless-browser check against a full local docs build: the link clears the navbar, aligns with the content column, and click-through serves the page's .md companion. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AnwZakwBQp8muhbPPP76xs
…title Float the link in the band between the fixed navbar and the page title: the zero-height wrapper mirrors $wrapper's geometry (max-width, centering, responsive paddings) and the anchor is absolutely positioned at the article column's right text edge, so the link no longer takes flow space and the article returns to its natural vertical position. Browser-verified on desktop and 390px mobile across guide pages: the link clears the navbar, sits just above the title line box, right edge matches the content text edge exactly, stays clickable (z-index above the heading's navbar-clearance padding), and click-through still serves the .md companion. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AnwZakwBQp8muhbPPP76xs
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #1703 +/- ##
========================================
Coverage 97.21% 97.21%
========================================
Files 178 178
Lines 15611 15611
Branches 3460 3429 -31
========================================
Hits 15177 15177
+ Misses 434 426 -8
- Partials 0 8 +8 🚀 New features to boost your workflow:
|

Context
HF-154 — make the HyperFormula docs friendlier to coding agents and LLMs. On top of the existing VuePress portal this adds:
.mdcompanions — every doc page is also served as clean, VuePress-stripped Markdown (a build-timemd-companionsplugin), plus an aggregatellms-full.txt. Both are also mirrored to the site root so/llms.txtand/llms-full.txtresolve on GitHub Pages (prod) and the Netlify preview.llms.txt— a top-level index pointing agents at the Markdown sources.ViewMarkdownLink.vue) — links to the page's.mdsource so an agent can open and read it directly.docs/guide/setup-coding-agent.md) plus aCodingAgentWizard.vuehelper.context7.jsonso agent doc-access tooling (Context7 / GitMCP) can discover the sources.The Markdown stripper (
md-companions/strip.js) is the fidelity-critical piece: it turns VuePress-flavoured Markdown (:::containers,<script>/Vue components,[[toc]],{{ }}bindings, Vue-bound<a :href>/<img :src>, live:::exampledemos, nested code fences) into clean Markdown.How did you test your changes?
<a :href>/<img :src>,[[toc]],<script>/component removal,:::exampledemos) — confirming code fences, including Vue-shaped samples inside container bodies, survive verbatim while prose is cleaned.llms-full.txtpopulated, per-page.mdclean, no leaked components).npm run lintclean.Note on test coverage
The
md-companionsstripper currently has no automated regression tests. The earliertest/docs/*.spec.jssuites (strip / corpus / generated) were removed in363a5e2— the public repo carries smoke tests only (test/README.md), and this is build-time docs tooling, not shippedsrc/code, so a stripper regression degrades the generated.md/llms.txt, not engine/product behaviour. The working safety net is Cursor Bugbot + the Netlify deploy-preview + review.That said, this is the second fidelity bug in the stripper (after the Vue-bound link/image fix), and the removed suites did not cover the case that regressed here — Vue markup inside a code fence sitting inside a container. If we want coverage, the right home is the private
hyperformula-testsrepo (restore the removed cases + this container-fence intersection).Types of changes
Related issues:
Checklist:
Notes
hyperformula-testsrepo). Currentdevelopis merged in.md-companionsplugin and.vuecomponents are VuePress-specific); the agent-friendly outputs (.mdcompanions,llms.txt, setup guide) would need re-homing in Astro.Note
Low Risk
Docs-site and build-time tooling only; no changes to
src/engine behavior. Main operational risk is stripper fidelity regressions in generated.md/llms-full.txt(currently manual/preview validation, no automated stripper tests in this PR).Overview
Adds LLM/agent-friendly documentation outputs on top of the existing VuePress docs build, without changing the spreadsheet engine.
A new
md-companionsVuePress plugin runs at build time: it strips VuePress-only syntax (:::exampledemos, Vue components,[[toc]], bound<a :href>/<img :src>, etc.) viastrip.js, resolves injected{{ $page.* }}values, rebases root-relative links for the docsbase, and writes a clean.mdcompanion beside each HTML page plus an aggregatedllms-full.txt(with absolute links in the corpus).context7.jsonpoints Context7-style tooling at thedocsfolder with project-specific rules.The local theme injects
ViewMarkdownLink(“View as Markdown”) on every page;setup-coding-agent.mdandCodingAgentWizarddocument Claude Code skills, Cursor/Copilot rules, MCP (GitMCP / Context7), and copyable snippets (viaclipboard.js). Sidebar andDEV_DOCS.md/docs/README.mdare updated accordingly. Netlify build uses Node 22;.eslintrc.jsgets a minor override-array syntax fix.Reviewed by Cursor Bugbot for commit f2e95fc. Bugbot is set up for automated code reviews on this repo. Configure here.