feat(site): agent contract in llms.txt + FAQ/AEO with drift guards#229
Merged
Conversation
Make jamf-cli more discoverable and usable by LLM agents and answer engines, and guard the new hand-authored content against drift. llms.txt: - Add a "For AI Agents" operating contract: runtime discovery via `commands -o json`, unattended flags, the exit-code table + JSON error envelope, token-efficiency flags, and apply/destructive semantics. - Expand Output Formats (--select/--field/--compact, 50-row hint). - Guard the exit-code table against internal/exitcode (unit test). Site AEO (docs/site): - Add a visible FAQ section + matching FAQPage JSON-LD (shared text). - Add meta robots (max-snippet/max-image-preview). - Freshness signals: JSON-LD dateModified/softwareVersion and sitemap <lastmod>, plus a self-updating command count in the OG/Twitter meta, stamped at deploy from commands.json (fixes a stale 1,251 -> 1,299). Version parsing fix: - generator/site parseVersion now reads the JSON `version` field (the `version` command defaults to -o json), with a plain-text fallback; previously commands.json/llms.txt could record version "unknown". Drift guards (verify-site-output.sh): - FAQ questions must match between the visible section and FAQPage. - llms.txt flag names must exist in the CLI (--help globals plus commands.json per-command flags). - Every product in commands.json must be documented in llms.txt + FAQ. - The FAQ "over N commands" floor must not exceed the real count. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolve conflict in generator/site/main.go. main's #227 landed an equivalent "parse version from JSON" fix (extractRawVersion) that also trims the bare "-dirty" marker — strictly more complete than this branch's rawVersion, which missed the clean-tag-built-on-dirty-tree case. Kept main's extractRawVersion + parseVersion; dropped the branch's redundant rawVersion. The branch's real contribution (llms.txt "For AI Agents" contract + Output Formats expansion in renderLLMSTxt) auto-merged cleanly. Both branches' parseVersion JSON test cases were retained as complementary coverage. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address PR review nits: - verify-site-output.sh: the FAQ<->FAQPage guard now also compares answer text (acceptedAnswer.text vs the visible <p>), not just question text. Google's FAQ structured-data policy requires the markup answer to match the visible answer; question-only parity let answers drift silently. Tags are stripped, HTML entities decoded, and whitespace normalized before comparing. Verified to fail on an injected visible-answer change and pass on the in-sync content. - site-preflight.yaml: pass --binary ./bin/jamf-cli to verify-site-output.sh so the llms.txt flag-name guard runs on the release-tag preflight path too (previously only ci.yaml's make target exercised it; the binary is already built one step earlier). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
neilmartin83
approved these changes
May 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Make
jamf-climore discoverable and usable by LLM agents and answer engines (ChatGPT/Claude/Perplexity/Google AI), and make the new hand-authored content self-defending against drift. The CLI was already strong here (structured output, exit codes,commands -o json, an existingllms.txt/commands.json, an AI-crawler allowlist, schema.org JSON-LD) — this closes the remaining gaps.What
llms.txt— agent operating contract## For AI Agentssection: runtime discovery (commands -o json), unattended flags (--no-input/--quiet/JAMF_CLI_ARGS), the exit-code table + JSON error envelope, token-efficiency flags, andapply/destructive semantics.Output Formats(--select/--field/--compact, the 50-row hint).Site AEO (
docs/site)FAQPageJSON-LD (shared Q&A text).meta robots(max-snippet:-1, max-image-preview:large).dateModified/softwareVersion, sitemap<lastmod>, and a self-updating command count in the OG/Twitter meta — all stamped at deploy fromcommands.json(fixes a stale1,251→1,299).Version parsing fix
generator/siteparseVersionnow reads the JSONversionfield (theversioncommand defaults to-o json), with a plain-text fallback. Previouslycommands.json/llms.txt/version badges could record version"unknown".Drift guards — staleness fails CI loudly instead of rotting silently:
llms.txt↔internal/exitcode(Go unit test).FAQPageJSON-LD (verify-site-output.sh).llms.txtflag names must exist in the CLI (--helpglobals ∪commands.jsonflags).commands.jsonmust be documented inllms.txt+ FAQ.How it was verified
make verify-site-output— all checks green, including the new flag/product/count guards.--bogus-flag;over 99999 commands), and the exit-code guard proven to fail on a renumbered code.softwareVersion=…,dateModified=…, count →1,299), with a guard that fails the deploy if any placeholder survives.Notes for reviewers
__VERSION__/__BUILD_DATE__/__COMMAND_COUNT__are intentional placeholders committed inindex.html/sitemap.xml; theStamp build freshnessstep indeploy-site.yamlsubstitutes them in the throwaway runner checkout. Locally they appear unsubstituted in<head>/sitemap (invisible on the page).🤖 Generated with Claude Code