Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/llms-txt-hub / packages/cli/src/lib/storage.ts |
| Date | Not specified in source |
| Repository | Janady13/llms-txt-hub |
| Revision | e0f4bf978d4df168369c7601d9c5f5ea89e84377 |
| Branch | main |
| Path or archive member | packages/cli/src/lib/storage.ts |
| Original SHA-256 | 74b2bc374422976811d188fe0d9180d13f203a6ca9720fc53b1a6dcc3a7921e3 |
| Git blob SHA | aa0b295129ab0a00374b7730084ad76c5ed67d02 |
| Reading-copy SHA-256 | 74b2bc374422976811d188fe0d9180d13f203a6ca9720fc53b1a6dcc3a7921e3 |
Recovered source transcript
Download complete source text ↓185 lines · 5.1 KB
import { createHash } from 'node:crypto'
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'
import type { RegistryEntry } from '../types/index.js'
import {
type AgentConfig,
assertPathContainment,
CANONICAL_DIR,
createAgentSymlink,
detectInstalledAgents,
removeAgentSkill,
sanitizeSlug
} from './agents.js'
const LLMS_DIR = '.llms'
const LARGE_FILE_THRESHOLD = 500 // lines
// ── Canonical storage (.agents/skills/) ─────────────────────────
/**
* Generate SKILL.md content and optional reference.md for large files.
*/
function generateSkillMd(
entry: RegistryEntry,
content: string,
format: 'llms.txt' | 'llms-full.txt'
): { skillMd: string; referenceMd?: string } {
const lineCount = content.split('\n').length
const isLarge = lineCount > LARGE_FILE_THRESHOLD
const formatLabel = format === 'llms-full.txt' ? 'full ' : ''
const frontmatter = [
'---',
`name: ${entry.slug}-docs`,
`description: Official ${entry.name} ${formatLabel}documentation. Reference when working with ${entry.name}.`,
'user-invocable: false',
'---'
].join('\n')
const sourceUrl =
format === 'llms-full.txt' ? (entry.llmsFullTxtUrl ?? entry.llmsTxtUrl) : entry.llmsTxtUrl
if (isLarge) {
const skillMd = [
frontmatter,
'',
`# ${entry.name} Documentation`,
'',
`${entry.description}`,
'',
`Source: ${sourceUrl}`,
'',
'For complete documentation, see [reference.md](reference.md).'
].join('\n')
return { skillMd, referenceMd: content }
}
const skillMd = [
frontmatter,
'',
`# ${entry.name} Documentation`,
'',
`${entry.description}`,
'',
`Source: ${sourceUrl}`,
'',
'---',
'',
content
].join('\n')
return { skillMd }
}
// ── Multi-target write ──────────────────────────────────────────
export interface InstallResult {
checksum: string
size: number
agents: string[]
}
export interface InstallToAgentsInput {
projectDir: string
slug: string
entry: RegistryEntry
content: string
format: 'llms.txt' | 'llms-full.txt'
targetAgents?: AgentConfig[]
}
/**
* Install a skill to the canonical directory and selected agents.
* If targetAgents is provided, only those agents get symlinks.
* Otherwise falls back to all detected agents.
*/
export function installToAgents({
projectDir,
slug,
entry,
content,
format,
targetAgents
}: InstallToAgentsInput): InstallResult {
const checksum = createHash('sha256').update(content).digest('hex')
const size = Buffer.byteLength(content, 'utf-8')
const installedAgents: string[] = []
// 1. Write canonical copy to .agents/skills/<slug>/
sanitizeSlug(slug)
const canonicalDir = join(projectDir, CANONICAL_DIR, slug)
assertPathContainment(canonicalDir, join(projectDir, CANONICAL_DIR))
mkdirSync(canonicalDir, { recursive: true })
const { skillMd, referenceMd } = generateSkillMd(entry, content, format)
writeFileSync(join(canonicalDir, 'SKILL.md'), skillMd, 'utf-8')
if (referenceMd) {
writeFileSync(join(canonicalDir, 'reference.md'), referenceMd, 'utf-8')
}
installedAgents.push('universal')
// 2. For each target agent, create symlink to canonical
const agentsToLink = targetAgents ?? detectInstalledAgents()
for (const agent of agentsToLink) {
if (agent.isUniversal) {
if (!installedAgents.includes(agent.name)) {
installedAgents.push(agent.name)
}
continue
}
const linked = createAgentSymlink({ projectDir, slug, agent })
if (linked) {
installedAgents.push(agent.name)
}
}
// 3. Keep lockfile metadata dir
mkdirSync(join(projectDir, LLMS_DIR), { recursive: true })
return { checksum, size, agents: installedAgents }
}
// ── Multi-target remove ─────────────────────────────────────────
export interface RemoveFromAgentsInput {
projectDir: string
slug: string
}
/**
* Remove a skill from the canonical directory and all agent directories.
*/
export function removeFromAgents({ projectDir, slug }: RemoveFromAgentsInput): void {
sanitizeSlug(slug)
// Remove canonical
const canonicalDir = join(projectDir, CANONICAL_DIR, slug)
assertPathContainment(canonicalDir, join(projectDir, CANONICAL_DIR))
if (existsSync(canonicalDir)) {
rmSync(canonicalDir, { recursive: true, force: true })
}
// Remove from all agents
for (const agent of detectInstalledAgents()) {
removeAgentSkill({ projectDir, slug, agent })
}
}
// ── Status checks ───────────────────────────────────────────────
export interface IsInstalledInput {
projectDir: string
slug: string
}
/**
* Check whether a skill is installed in the canonical location.
*/
export function isInstalled({ projectDir, slug }: IsInstalledInput): boolean {
sanitizeSlug(slug)
return existsSync(join(projectDir, CANONICAL_DIR, slug, 'SKILL.md'))
}