UNPKG

@intlayer/chokidar

Version:

Uses chokidar to scan and build Intlayer declaration files into dictionaries based on Intlayer configuration.

299 lines (297 loc) 8.72 kB
import { readAsset } from "../_virtual/_utils_asset.mjs"; import path from "node:path"; import { v } from "@intlayer/config/logger"; import { promises } from "node:fs"; import { getMarkdownMetadata } from "@intlayer/core/markdown"; //#region src/installSkills/index.ts /** * Metadata for each available documentation skill. */ const SKILLS_METADATA = { Config: "Intlayer configuration documentation", Content: "Reference for all Intlayer content node types (t, enu, etc.)", Usage: "How to use Intlayer in your project", CLI: "Intlayer CLI commands and usage", Compiler: "Intlayer Compiler setup and usage for automatic content extraction without .content files", RemoteContent: "How to use Intlayer with Remote/CMS/Server-side content", NextJS: "Next.js-specific usage (Server & Client components)", React: "React-specific syntax and hooks usage", Vue: "Vue-specific composables and syntax", Svelte: "Svelte-specific stores and syntax", Angular: "Angular-specific syntax and Injectable Function usage", Preact: "Preact-specific syntax and hooks usage", Solid: "Integrates Intlayer internationalization with SolidJS components. Use when the user asks to \"setup SolidJS i18n\", use the \"useIntlayer\" hook in Solid, or manage locales in a SolidJS application.", Astro: "Astro-specific usage and getIntlayer" }; const SKILLS = Object.keys(SKILLS_METADATA); const getInitialSkills = (deps) => { const skills = [ "Usage", "Content", "Config", "CLI", "Compiler" ]; if (deps.next) skills.push("NextJS"); if (deps.react || !deps.next) skills.push("React"); if (deps.preact) skills.push("Preact"); if (deps["solid-js"]) skills.push("Solid"); if (deps.vue || deps.nuxt) skills.push("Vue"); if (deps.svelte || deps["@sveltejs/kit"]) skills.push("Svelte"); if (deps.astro) skills.push("Astro"); return skills; }; /** * Metadata and configuration for each supported platform. */ const PLATFORMS_METADATA = { Cursor: { label: "Cursor", dir: ".cursor/skills", check: () => process.env.CURSOR === "true" || process.env.TERM_PROGRAM === "cursor" }, Windsurf: { label: "Windsurf", dir: ".windsurf/skills", check: () => process.env.WINDSURF === "true" || process.env.TERM_PROGRAM === "windsurf" }, Trae: { label: "Trae", dir: ".trae/skills", check: () => process.env.TRAE === "true" || process.env.TERM_PROGRAM === "trae" }, TraeCN: { label: "Trae CN", dir: ".trae/skills", check: () => process.env.TRAE_CN === "true" }, VSCode: { label: "VS Code", dir: ".vscode/skills", check: () => process.env.VSCODE === "true" || process.env.TERM_PROGRAM === "vscode" }, OpenCode: { label: "OpenCode", dir: ".opencode/skills", check: () => process.env.OPENCODE === "true" }, Claude: { label: "Claude Code", dir: ".claude/skills", check: () => process.env.CLAUDE === "true" }, GitHub: { label: "GitHub Copilot Workspace", dir: ".github/skills", check: () => process.env.GITHUB_ACTIONS === "true" || !!process.env.GITHUB_WORKSPACE }, Antigravity: { label: "Antigravity", dir: ".agent/skills" }, Augment: { label: "Augment", dir: ".augment/skills" }, OpenClaw: { label: "OpenClaw", dir: "skills" }, Cline: { label: "Cline", dir: ".cline/skills" }, CodeBuddy: { label: "CodeBuddy", dir: ".codebuddy/skills" }, CommandCode: { label: "Command Code", dir: ".commandcode/skills" }, Continue: { label: "Continue", dir: ".continue/skills" }, Crush: { label: "Crush", dir: ".crush/skills" }, Droid: { label: "Droid", dir: ".factory/skills" }, Goose: { label: "Goose", dir: ".goose/skills" }, IFlow: { label: "iFlow CLI", dir: ".iflow/skills" }, Junie: { label: "Junie", dir: ".junie/skills" }, KiloCode: { label: "Kilo Code", dir: ".kilocode/skills" }, Kiro: { label: "Kiro CLI", dir: ".kiro/skills" }, Kode: { label: "Kode", dir: ".kode/skills" }, MCPJam: { label: "MCPJam", dir: ".mcpjam/skills" }, MistralVibe: { label: "Mistral Vibe", dir: ".vibe/skills" }, Mux: { label: "Mux", dir: ".mux/skills" }, OpenHands: { label: "OpenHands", dir: ".openhands/skills" }, Pi: { label: "Pi", dir: ".pi/skills" }, Qoder: { label: "Qoder", dir: ".qoder/skills" }, Qwen: { label: "Qwen Code", dir: ".qwen/skills" }, RooCode: { label: "Roo Code", dir: ".roo/skills" }, Zencoder: { label: "Zencoder", dir: ".zencoder/skills" }, Neovate: { label: "Neovate", dir: ".neovate/skills" }, Pochi: { label: "Pochi", dir: ".pochi/skills" }, Other: { label: "Other", dir: "skills" } }; const PLATFORMS = Object.keys(PLATFORMS_METADATA); /** * Maps specific skill keys to special filenames if they differ from standard snake_case. */ const SKILL_FILENAME_MAP = {}; /** * Helper to convert CamelCase to kebab-case for directory naming */ const camelToKebabCase = (str) => str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(); /** * Reads the raw markdown content for a specific skill from the assets folder. */ const getSkillContent = (skill) => { const fileName = `./skills/${SKILL_FILENAME_MAP[skill] ?? camelToKebabCase(skill)}.md`; try { return readAsset(fileName); } catch { console.warn(`Warning: Could not read asset for skill: ${skill} at ${fileName}`); return ""; } }; /** * Reads the licence content from the assets folder. */ const getLicenceContent = () => { try { return readAsset("./LICENCE.md"); } catch { console.warn("Warning: Could not read LICENCE.md asset"); return ""; } }; const ALLOWED_FETCH_HOSTS = new Set(["intlayer.org"]); /** * Fetches the content of a URL using native fetch. */ const fetchUrlContent = async (url) => { const parsed = new URL(url); if (parsed.protocol !== "https:" || !ALLOWED_FETCH_HOSTS.has(parsed.hostname)) throw new Error(`Blocked fetch to disallowed host: ${parsed.hostname}`); const response = await fetch(url, { redirect: "error" }); if (!response.ok) throw new Error(`Failed to fetch ${url}: ${response.statusText}`); return response.text(); }; /** * Installs skills using the "Agent Skills" directory standard. * Standard: <PROJECT_ROOT>/<CONFIG_DIR>/skills/<SKILL_NAME>/SKILL.md */ const installSkills = async (projectRoot, platform, skills) => { const relativeDir = PLATFORMS_METADATA[platform].dir ?? "skills"; const skillsBaseDir = path.join(projectRoot, relativeDir); await promises.mkdir(skillsBaseDir, { recursive: true }); const createdSkills = []; const licenceContent = getLicenceContent(); for (const skill of skills) { const skillName = `intlayer-${SKILL_FILENAME_MAP[skill] ?? camelToKebabCase(skill)}`; const skillContent = getSkillContent(skill); if (!skillContent) continue; const urls = Array.from(new Set(skillContent.match(/https:\/\/intlayer\.org\/[^\s)]+\.md/g) || [])); const skillDir = path.join(skillsBaseDir, skillName); const referenceDir = path.join(skillDir, "references"); await promises.mkdir(referenceDir, { recursive: true }); if (licenceContent) await promises.writeFile(path.join(skillDir, "LICENCE.md"), licenceContent, "utf-8"); let updatedSkillContent = skillContent; const downloadPromises = urls.map(async (url) => { try { const content = await fetchUrlContent(url); const metadata = getMarkdownMetadata(content); let fileName = ""; if (Array.isArray(metadata.slugs)) fileName = metadata.slugs.filter((slug) => slug !== "doc").join("_"); else fileName = new URL(url).pathname.split("/").filter((part) => part && part !== "doc").map((part) => part.replace(".md", "")).join("_"); fileName = fileName ? `${fileName}.md` : "index.md"; return { url, localRefPath: `references/${fileName}`, fileName, content, success: true }; } catch (error) { console.warn(`Warning: Failed to download ref ${url} for skill ${skill}`, error); return { url, success: false }; } }); const results = await Promise.all(downloadPromises); for (const res of results) if (res.success && res.fileName && res.content && res.localRefPath) { await promises.writeFile(path.join(referenceDir, res.fileName), res.content, "utf-8"); updatedSkillContent = updatedSkillContent.replaceAll(res.url, res.localRefPath); } const filePath = path.join(skillDir, "SKILL.md"); await promises.writeFile(filePath, updatedSkillContent, "utf-8"); createdSkills.push(`${skillName}/SKILL.md`); } if (createdSkills.length === 0) return `No skill files were created. Check your asset paths.`; return `${v} Created ${createdSkills.length} skills in ${skillsBaseDir}`; }; //#endregion export { PLATFORMS, PLATFORMS_METADATA, SKILLS, SKILLS_METADATA, getInitialSkills, installSkills }; //# sourceMappingURL=index.mjs.map