Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions app/pages/@[login]/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ const {
const skills = computed(() => skillsData.value?.items ?? [])
type AuthorSkill = NonNullable<typeof skillsData.value>['items'][number]

// `/@<login>` is an Author when skilld.dev holds an account or Skills for the
// login. A collection needs an account, so these two cover every Author. Any
// other login is a made-up URL: it answers 404, never an empty 200 page.
const { error: accountError } = await useFetch(
() => `/api/users/${login.value}`,
{ key: () => `author-account-${login.value}` },
)
if (accountError.value?.statusCode === 404 && !skillsError.value && skills.value.length === 0)
throw createError({ statusCode: 404, message: 'Author not found', fatal: true })

interface SkillGroup {
key: string
owner: string
Expand Down
5 changes: 4 additions & 1 deletion layers/registry/server/utils/skills-route-policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ export function resolveSkillsRoute(
): SkillsRouteDecision {
if (!pathname.startsWith('/skills/') && pathname !== '/skills')
return { _tag: 'pass' }
if (marketingPaths.has(pathname)
// `/skills/planning.md` is the Markdown of `/skills/planning`. Accept
// negotiation redirects there, so it passes whenever the page does.
const pagePath = pathname.endsWith('.md') ? pathname.slice(0, -'.md'.length) : pathname
if (marketingPaths.has(pagePath)
|| marketingPrefixes.some(prefix => pathname.startsWith(prefix))) {
return { _tag: 'pass' }
}
Expand Down
40 changes: 14 additions & 26 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { dependencyPluginCompat } from './scripts/lib/dependency-plugin-compat.t
import { withBuildAssetMissFallthrough } from './scripts/lib/static-asset-fallthrough.ts'
import { externalCheckin } from './shared/checkin-external.ts'
import { iconifyCollections } from './shared/icon-collections.ts'
import { REGISTRY_SKILL } from './shared/llms-txt.ts'
import { SENTRY_DSN, sentryRelease, sentryReportingEnabled } from './shared/sentry.ts'
import { SESSION_NAME } from './shared/server/session-access.ts'
import { CLI_INSTALL_SCRIPTS } from './shared/skill-commands.ts'
Expand Down Expand Up @@ -53,22 +54,6 @@ export default defineNuxtConfig({
'/_nuxt/v2/',
)
},
// 2026-08-22 agent lane, where agents discover pages without Google's
// sitemap: list the agent-only sitemap in llms.txt. The module's `notes` config exists but never renders, so
// this pushes a link into the first section instead.
'ai-ready:llms-txt': (payload: { sections?: { links?: { title: string, href: string, description?: string }[] }[], notes: string[] }) => {
const section = payload.sections?.[0]
if (!section)
return
section.links ??= []
if (!section.links.some(l => l.href.includes('/ai-sitemap.xml'))) {
section.links.push({
title: 'AI sitemap',
href: 'https://skilld.dev/ai-sitemap.xml',
description: 'Every AI-ready page, including pages that are noindex for Google.',
})
}
},
},

nuxtDx: {
Expand Down Expand Up @@ -313,11 +298,15 @@ export default defineNuxtConfig({
},
cron: false,
runtimeSync: true,
// llms.txt is an index, not a dump: each entry links the page's .md.
// The 28.5 MB llms-full.txt inline dump is retired below (routeRules).
llmsTxt: {
markdownLinks: true,
},
// Content Signals for robots.txt: allow search, AI input, and AI training.
contentSignal: {
search: true,
aiInput: true,
aiTrain: true,
},
// `server/middleware/llms-txt.ts` serves /llms.txt and its nested files
// from the sitemaps, ahead of the module's route. The 28.5 MB
// llms-full.txt inline dump is retired below (routeRules).
// The module's negotiation also reads User-Agent, which a shared cache
// cannot key on. server/handlers/content-negotiation.ts decides from
// Accept and Sec-Fetch-Dest instead. Explicit `.md` URLs and the
Expand Down Expand Up @@ -349,8 +338,7 @@ export default defineNuxtConfig({
agentSkills: {
skills: [{
source: 'local',
name: 'skilld-registry',
description: 'Search skilld.dev and return verified commands to run or install a skill.',
...REGISTRY_SKILL,
file: './skills/skilld-registry/SKILL.md',
}],
},
Expand Down Expand Up @@ -654,9 +642,9 @@ export default defineNuxtConfig({
// empty sibling redirects instead of shipping next to it.
'/brand-kit/_GithubBadgePreview': { redirect: { to: '/brand-kit/github-badge', statusCode: 301 } } as any,
// 2026-08-22: llms-full.txt inlined every page's markdown into one 28.5 MB
// file; agents truncate or time out on it. llms.txt now links each page's
// .md (aiReady.llmsTxt.markdownLinks), so the dump redirects there. 302 so
// this reverses the moment per-section splitting is worth building.
// file; agents truncate or time out on it. llms.txt is now a root index
// with one nested file per sitemap, and nothing links llms-full.txt. The
// redirect keeps old bookmarks working.
'/llms-full.txt': { redirect: { to: '/llms.txt', statusCode: 302 } } as any,
// The printed native install commands. `CLI_INSTALL_SCRIPTS` says why each is a 302.
'/install.sh': { redirect: { to: CLI_INSTALL_SCRIPTS['/install.sh'], statusCode: 302 } } as any,
Expand Down
Loading
Loading