Astroサイトにllms.txtを追加しました

Tadashi Shigeoka ·  Tue, April 29, 2025

Astroで構築された本サイトに llms.txt を追加したので、そのコードを紹介します。

Astroプラグインの探索

まずは、Astro Integration(プラグイン)で llms.txt を簡単に追加できるものがないか探してみました。llms.txt に対応しているものは、現時点では見当たりませんでした。

AIによる実装支援

プラグインが無く、今回は自分で作ったほうが早いということで、Cursor (claude-3.7-sonnet) のAgentモードで実装を依頼しました。

プロンプトに記載した仕様の概要は以下の通りです。

全てのページを以下のフォーマットでレスポンスを返すように実装してください
 
- [title](url): description

llms.txt の実装コード

以下が、CursorでのVibe Codingにより作成したコードです。

(プロジェクト構成や具体的な要件に合わせて適宜修正してください。)

src/pages/llms.txt.js

/**
 * Generate a complete site map with formatted Markdown links
 */
export async function GET(context) {
  const baseUrl = "https://codenote.net";
 
  // Get data
  const posts = await fetchAllPosts();
  const tags = extractAllTags(posts);
 
  // Generate formatted content
  const formattedEntries = [
    ...generateHeader(),
    ...generateStaticPages(baseUrl),
    ...generatePostsSection(posts, baseUrl),
    ...generateTagsSection(tags, baseUrl)
  ];
 
  // Return response as a text file
  return new Response(formattedEntries.join('\n'), {
    headers: {
      "Content-Type": "text/plain; charset=utf-8",
    },
  });
}
 
/**
 * Fetch all posts from the posts directory
 */
async function fetchAllPosts() {
  const postFiles = await import.meta.glob('./posts/*.md', { eager: true });
  return Object.values(postFiles);
}
 
/**
 * Extract all unique tags from posts
 */
function extractAllTags(posts) {
  const tagSet = new Set();
  posts.forEach(post => {
    if (post.frontmatter?.tags && Array.isArray(post.frontmatter.tags)) {
      post.frontmatter.tags.forEach(tag => tagSet.add(tag));
    }
  });
  return Array.from(tagSet);
}
 
/**
 * Generate the document header
 */
function generateHeader() {
  return [
    `# CodeNote.net`,
    ``,
  ];
}
 
/**
 * Generate links for static pages
 */
function generateStaticPages(baseUrl) {
  return [
    `- [Home](${baseUrl}/): Top page of CodeNote`,
    ``,
    `## Second directories`,
    ``,
    `- [About](${baseUrl}/about): About page and profile information`,
    `- [Posts](${baseUrl}/posts): All blog posts`,
    `- [Tags](${baseUrl}/tags): All available tags`,
    `- [RSS Feed](${baseUrl}/rss.xml): Subscribe to the blog updates`,
  ];
}
 
/**
 * Generate the posts section with links to all posts
 */
function generatePostsSection(posts, baseUrl) {
  const result = [
    ``,
    `## Posts`,
    ``
  ];
  posts.forEach(post => {
    const url = getPostUrl(post, baseUrl);
    if (url) {
      const title = post.frontmatter?.title || "Untitled";
      const description = post.frontmatter?.description || "";
      result.push(`- [${title}](${url}): ${description}`);
    }
  });
  return result;
}
 
/**
 * Get the URL for a post
 */
function getPostUrl(post, baseUrl) {
  if (post.url) {
    return `${baseUrl}${post.url}`;
  } else if (post.file) {
    const filename = post.file.split('/').pop()?.replace(/\.md$/, '');
    if (filename) {
      return `${baseUrl}/posts/${filename}`;
    }
  }
  return null;
}
 
/**
 * Generate the tags section with links to all tag pages
 */
function generateTagsSection(tags, baseUrl) {
  const result = [
    ``,
    `## Tags`,
    ``
  ];
  tags.forEach(tag => {
    result.push(`- [#${tag}](${baseUrl}/tags/${tag}): Posts tagged with #${tag}`);
  });
  return result;
}
 
export const prerender = true;

以上、llms.txtを設置した、現場からお送りしました。