UNPKG

akm-cli

Version:

akm (Agent Knowledge Manager) — a portable, local-first capability library for AI agents. Discover, load, share, and improve reusable skills, scripts, workflows, and knowledge across any shell-capable coding agent, including Claude Code, OpenCode, and Cur

101 lines (100 loc) • 3.54 kB
// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. import { GITHUB_API_BASE, githubHeaders } from "../../integrations/github.js"; import { htmlToMarkdown } from "./content-extract.js"; import { avoidReservedBasename } from "./fetcher-util.js"; import { fetchPinnedText } from "./host-guard.js"; const GITHUB_HOSTS = new Set(["github.com", "www.github.com"]); const RESERVED_GITHUB_ROOTS = new Set([ "apps", "collections", "codespaces", "customer-stories", "education", "enterprise", "events", "explore", "features", "issues", "login", "marketplace", "new", "notifications", "orgs", "organizations", "pricing", "pulls", "search", "security", "settings", "solutions", "sponsors", "topics", "trending", "users", ]); const GITHUB_README_BYTE_CAP = 5 * 1024 * 1024; const GITHUB_README_BODY_TIMEOUT_MS = 30_000; /** Match only repository roots; deeper GitHub pages stay generic web pages. */ export function extractGithubRepository(url) { if (!GITHUB_HOSTS.has(url.hostname.toLowerCase())) return null; const segments = url.pathname.split("/").filter(Boolean); if (segments.length !== 2) return null; let owner; let repo; try { owner = decodeURIComponent(segments[0] ?? ""); repo = decodeURIComponent(segments[1] ?? "").replace(/\.git$/i, ""); } catch { return null; } if (!/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})$/.test(owner)) return null; if (RESERVED_GITHUB_ROOTS.has(owner.toLowerCase())) return null; if (!/^[A-Za-z0-9._-]{1,100}$/.test(repo) || repo === "." || repo === "..") return null; return { owner, repo }; } const githubFetcher = { name: "github-repository", matches(url) { return extractGithubRepository(url) !== null; }, async fetch(url, context) { const repository = extractGithubRepository(url); if (!repository) return null; const apiUrl = `${GITHUB_API_BASE}/repos/${encodeURIComponent(repository.owner)}/${encodeURIComponent(repository.repo)}/readme`; const headers = new Headers(githubHeaders(apiUrl)); headers.set("Accept", "application/vnd.github.html+json"); headers.set("User-Agent", "akm-cli github snapshot fetcher"); const html = await fetchPinnedText(apiUrl, { headers, byteCap: GITHUB_README_BYTE_CAP, bodyTimeoutMs: GITHUB_README_BODY_TIMEOUT_MS, timeoutMs: context.timeoutMs, signal: context.signal, }); if (!html) return null; const canonicalUrl = `https://github.com/${repository.owner}/${repository.repo}`; const markdown = htmlToMarkdown(html, `${canonicalUrl}/`); if (!markdown) return null; const repoName = repository.repo.startsWith(".") ? `dot-${repository.repo.slice(1)}` : repository.repo; const preferredName = `${repository.owner.toLowerCase()}/${repoName.toLowerCase()}`; return { url: canonicalUrl, title: `${repository.owner}/${repository.repo}`, markdown, preferredName: avoidReservedBasename(preferredName), tags: ["github", "repository", repository.owner.toLowerCase()], }; }, }; export default githubFetcher;