feat(cli): generate .well-known agent skills bundle from docs.yml skills path#16510
feat(cli): generate .well-known agent skills bundle from docs.yml skills path#16510matlegault wants to merge 6 commits into
Conversation
| if (declaredSkillsPath != null) { | ||
| return validateDeclaredSkillsPath({ | ||
| absolutePathToFernFolder: workspace.absoluteFilePath, | ||
| absolutePathToSkillsDirectory: resolve( | ||
| dirname(workspace.absoluteFilepathToDocsConfig), | ||
| declaredSkillsPath | ||
| ) | ||
| }); |
There was a problem hiding this comment.
🟡 .well-known/agent-skills/ validation skipped when a declared skills path is set, but files are still uploaded
When config.pageActions?.options?.skills?.path is non-null, the validator at valid-well-known-skills.ts:37-44 returns early from validateDeclaredSkillsPath(), which only validates the declared path and warns about .well-known/skills/. It never validates .well-known/agent-skills/. However, DocsDefinitionResolver.ts:879-890 still uploads .well-known/agent-skills/ files via collectWellKnownSkillsFiles when a declared path is set. This is a regression: before this PR, both directories were always validated. Now, an invalid .well-known/agent-skills/index.json (e.g., not valid JSON) or broken SKILL.md frontmatter would pass fern check but still be served, silently breaking npx skills add for the agent-skills layout.
Prompt for agents
In valid-well-known-skills.ts, the file-level visitor (lines 35-57) returns early when a declared skills path is set, skipping validation of .well-known/agent-skills/. But the DocsDefinitionResolver (lines 879-890) still uploads .well-known/agent-skills/ files in that scenario.
The fix: after calling validateDeclaredSkillsPath, also validate the .well-known/agent-skills/ directory (WELL_KNOWN_AGENT_SKILLS_DIRECTORY) by appending its violations before returning. Something like:
const violations = await validateDeclaredSkillsPath({...});
violations.push(...(await validateWellKnownSkillsDirectory({
absolutePathToFernFolder: workspace.absoluteFilePath,
wellKnownDirectory: WELL_KNOWN_AGENT_SKILLS_DIRECTORY
})));
return violations;
This ensures agent-skills bundles that are still uploaded verbatim continue to be validated, matching pre-PR behavior.
Was this helpful? React with 👍 or 👎 to provide feedback.
Docs Generation Benchmark ResultsComparing PR branch against median of 5 nightly run(s) on
Docs generation runs |
SDK Generation Benchmark ResultsComparing PR branch against median of 5 nightly run(s) on Full benchmark table (click to expand)
main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via |
f38ee66 to
9418294
Compare
…lls path Skills are now declared explicitly via page-actions.options.skills.path (a same-repo directory, resolved relative to docs.yml; ../ allowed). At generate/check time the CLI discovers every subdirectory containing a SKILL.md, validates the bundle, generates the index.json discovery manifest (v0.1.0, in a temp dir — nothing written back to the repo), and re-homes every file in the publish payload at .well-known/skills/<name>/…. A declared path wins over a hand-populated fern/.well-known/skills/ folder (warned on conflict); the raw passthrough remains the quiet fallback when no path is declared. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
With skills now declared locally via path and the CLI publishing the bundle, the "View source" repository link is redundant display sugar that can drift from what's actually served — and it's useless when the docs repo is private. install-command stays as the escape hatch for auth-walled docs, private registries, and multi-step installs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
46b30ab to
f462b38
Compare
…s-yml-path # Conflicts: # packages/cli/docs-resolver/src/DocsDefinitionResolver.ts
- Keep accepting the deprecated `repository` skills field (accept-and-ignore) instead of removing it, so existing docs.yml files that still declare it continue to load rather than failing schema validation. - Skip Agent Skills collection during validation-only resolves so its errors no longer surface as a misattributed failure of an unrelated rule (valid-markdown-links); publish/preview still collect and validate. - Treat a `skills.path` that points at a file as a clean violation instead of crashing with ENOTDIR (doesPathExist "directory"/"file" modes). - Ignore link/image references shown inside fenced or inline code so example syntax with `../` no longer blocks publishing. - Keep validating the `.well-known/agent-skills/` passthrough when a skills path is declared (it is still uploaded verbatim). Adds regression tests for the file-path, code-fence, and agent-skills cases. Co-Authored-By: Claude <noreply@anthropic.com>
…prefix
`relative(skillDir, resolved).startsWith("..")` false-positived on in-directory
targets like `...` (the ellipsis placeholder in `[x](...)`) and `..foo`. Test
for a leading `..` path segment instead. fs-utils `relative` always returns
forward slashes, so the separator is `/`, not the platform `sep`.
Co-Authored-By: Claude <noreply@anthropic.com>
…egenerate The earlier accept-and-ignore fix hand-edited the generated schema/SDK files without updating their source definition, so the "regenerate and diff" CI guard failed. Add `repository` (deprecated, ignored) back to fern/apis/docs-yml and regenerate docs-yml.schema.json via `pnpm docs-yml:jsonschema`; align the generated SDK types to the generator's output. Co-Authored-By: Claude <noreply@anthropic.com>
Description
Linear ticket: Refs FER-11043
Reworks the docs-site Agent Skills feature so skills are declared explicitly in docs.yml instead of authors hand-building
fern/.well-known/skills/(follow-up to #16446, serving side in fern-api/fern-platform#11947 — which needs zero changes). A new load-bearingpage-actions.options.skills.pathfield points at a same-repo directory of skills (resolved relative to docs.yml,../allowed, e.g. a repo-root.agents/skills/); atfern generate --docsthe CLI discovers every subdirectory containing aSKILL.md, validates the bundle, generates the v0.1.0index.jsondiscovery manifest in a temp dir (nothing written back to the repo), and re-homes every file in the publish payload at.well-known/skills/<name>/…sonpx skills add https://<docs-domain>works. A declared path wins over a hand-populatedfern/.well-known/skills/folder (warning on conflict), while the raw well-known passthrough remains the quiet, undocumented fallback when no path is declared.Changes Made
pathadded toSkillsPageActionConfig(docs.yml definition, generated SDK types, both JSON schemas); parsed intoParsedPageActionsConfig.options.skillsDirectory(CLI-only — never sent to FDR)repositorydisplay field is removed fromSkillsPageActionConfig— with skills published from a local path it's redundant "View source" sugar that can drift (and is useless for private docs repos);install-commandstays as the escape hatch for auth-walled docs, private registries, and multi-step installsdocs-resolver/src/utils/declaredSkills.ts: hard errors for missing/empty path, non-kebab-case or over-long names, name ≠ directory, empty description, duplicate names, and markdown references escaping a skill's directory; manifest format verified against Stripe's live/.well-known/skills/index.json(sha256 digests / v0.2.0 left as fast-follow)DocsDefinitionResolver.collectAgentSkillsUploads()builds the re-homed upload payload (declared path) or falls back to verbatim passthrough (no path)valid-well-known-skillsrule repointed at the declared path (with conflict warning), keeping the raw-bundle validation for the fallback;valid-skills-page-actionrejects an emptypathsrc/utils/__test__/so they actually run in CI — the docs-resolvertestscript skipssrc/__test__/, so feat(cli): agent skills for docs sites — upload .well-known bundles and add the Install-skills page action config #16446's tests were never executedTesting
🤖 Generated with Claude Code