repositories
loading repo index
repositories
loading repo index
repository
loading code, commits, and activity
public Clawd ADK gateway launch mirror
stars
latest
clone command
git clone gitlawb://did:key:z6Mkq5mY...iFZ5/my-project-publ...git clone gitlawb://did:key:z6Mkq5mY.../my-project-publ...2fa351d6docs: add automaton and perps launch sources16d ago| #1 | /** |
| #2 | * Skills Loader |
| #3 | * |
| #4 | * Discovers and loads SKILL.md files from ~/.automaton/skills/ |
| #5 | * Each skill is a directory containing a SKILL.md file with |
| #6 | * YAML frontmatter + Markdown instructions. |
| #7 | */ |
| #8 | import fs from "fs"; |
| #9 | import path from "path"; |
| #10 | import { parseSkillMd } from "./format.js"; |
| #11 | /** |
| #12 | * Scan the skills directory and load all valid SKILL.md files. |
| #13 | * Returns loaded skills and syncs them to the database. |
| #14 | */ |
| #15 | export function loadSkills(skillsDir, db) { |
| #16 | const resolvedDir = resolveHome(skillsDir); |
| #17 | if (!fs.existsSync(resolvedDir)) { |
| #18 | return db.getSkills(true); |
| #19 | } |
| #20 | const entries = fs.readdirSync(resolvedDir, { withFileTypes: true }); |
| #21 | const loaded = []; |
| #22 | for (const entry of entries) { |
| #23 | if (!entry.isDirectory()) |
| #24 | continue; |
| #25 | const skillMdPath = path.join(resolvedDir, entry.name, "SKILL.md"); |
| #26 | if (!fs.existsSync(skillMdPath)) |
| #27 | continue; |
| #28 | try { |
| #29 | const content = fs.readFileSync(skillMdPath, "utf-8"); |
| #30 | const skill = parseSkillMd(content, skillMdPath); |
| #31 | if (!skill) |
| #32 | continue; |
| #33 | // Check requirements |
| #34 | if (!checkRequirements(skill)) { |
| #35 | continue; |
| #36 | } |
| #37 | // Check if already in DB and preserve enabled state |
| #38 | const existing = db.getSkillByName(skill.name); |
| #39 | if (existing) { |
| #40 | skill.enabled = existing.enabled; |
| #41 | skill.installedAt = existing.installedAt; |
| #42 | } |
| #43 | db.upsertSkill(skill); |
| #44 | loaded.push(skill); |
| #45 | } |
| #46 | catch { |
| #47 | // Skip invalid skill files |
| #48 | } |
| #49 | } |
| #50 | // Return all enabled skills (includes DB-only skills not on disk) |
| #51 | return db.getSkills(true); |
| #52 | } |
| #53 | /** |
| #54 | * Check if a skill's requirements are met. |
| #55 | */ |
| #56 | function checkRequirements(skill) { |
| #57 | if (!skill.requires) |
| #58 | return true; |
| #59 | // Check required binaries |
| #60 | if (skill.requires.bins) { |
| #61 | for (const bin of skill.requires.bins) { |
| #62 | try { |
| #63 | const { execSync } = require("child_process"); |
| #64 | execSync(`which ${bin}`, { stdio: "ignore" }); |
| #65 | } |
| #66 | catch { |
| #67 | return false; |
| #68 | } |
| #69 | } |
| #70 | } |
| #71 | // Check required environment variables |
| #72 | if (skill.requires.env) { |
| #73 | for (const envVar of skill.requires.env) { |
| #74 | if (!process.env[envVar]) { |
| #75 | return false; |
| #76 | } |
| #77 | } |
| #78 | } |
| #79 | return true; |
| #80 | } |
| #81 | /** |
| #82 | * Get the active skill instructions to inject into the system prompt. |
| #83 | * Only returns instructions from auto-activate skills that are enabled. |
| #84 | */ |
| #85 | export function getActiveSkillInstructions(skills) { |
| #86 | const active = skills.filter((s) => s.enabled && s.autoActivate); |
| #87 | if (active.length === 0) |
| #88 | return ""; |
| #89 | const sections = active.map((s) => `--- SKILL: ${s.name} ---\n${s.description ? `${s.description}\n\n` : ""}${s.instructions}\n--- END SKILL: ${s.name} ---`); |
| #90 | return sections.join("\n\n"); |
| #91 | } |
| #92 | function resolveHome(p) { |
| #93 | if (p.startsWith("~")) { |
| #94 | return path.join(process.env.HOME || "/root", p.slice(1)); |
| #95 | } |
| #96 | return p; |
| #97 | } |
| #98 | //# sourceMappingURL=loader.js.map |