import type { ExtensionAPI } from "@jaeswift/jae-coding-agent"; import { existsSync, writeFileSync, mkdirSync, readFileSync, readdirSync } from "node:fs"; import { join } from "node:path"; import { Type } from "@sinclair/typebox"; const SKILLS_API = "https://skills.jaeswift.xyz/api/skills"; interface SkillInfo { name: string; description: string; version: string; author: string; } export default function (pi: ExtensionAPI) { async function fetchJSON(url: string): Promise { const result = await pi.exec("curl", ["-s", "-f", url]); if (result.code !== 0) { throw new Error(`HTTP request failed: ${result.stderr}`); } return JSON.parse(result.stdout); } function getSkillsDir(ctx: any): string { const dir = join(ctx.agentDir, "skills"); if (!existsSync(dir)) mkdirSync(dir, { recursive: true }); return dir; } function listLocalSkills(ctx: any): string[] { const dir = getSkillsDir(ctx); try { return readdirSync(dir).filter((f) => f.endsWith(".md") || existsSync(join(dir, f, "SKILL.md"))); } catch { return []; } } // /search-skills command pi.registerCommand("search-skills", { description: "Search the JAE skill marketplace: /search-skills ", handler: async (args, ctx) => { const query = args.trim(); if (!query) { ctx.ui.notify("Usage: /search-skills ", "warning"); return; } ctx.ui.notify(`\u{1F50D} Searching skills for: ${query}...`, "info"); try { const skills = await fetchJSON(`${SKILLS_API}/search?q=${encodeURIComponent(query)}`); if (!skills || skills.length === 0) { ctx.ui.notify(`No skills found matching '${query}'.`, "info"); return; } const list = skills .map((s: SkillInfo) => ` - ${s.name} (v${s.version}) by ${s.author}\n ${s.description}`) .join("\n"); ctx.ui.notify(`\u{1F3EA} Skills matching '${query}':\n${list}`, "info"); } catch (err: any) { ctx.ui.notify(`Failed to search skills: ${err.message}\nAPI: ${SKILLS_API}`, "error"); } }, }); // /install-skill command pi.registerCommand("install-skill", { description: "Install a skill from the marketplace: /install-skill ", handler: async (args, ctx) => { const name = args.trim(); if (!name) { ctx.ui.notify("Usage: /install-skill ", "warning"); return; } ctx.ui.notify(`\u{1F4E6} Installing skill: ${name}...`, "info"); try { const skill = await fetchJSON(`${SKILLS_API}/${encodeURIComponent(name)}`); if (!skill || !skill.content) { ctx.ui.notify(`Skill '${name}' not found or has no content.`, "error"); return; } const skillsDir = getSkillsDir(ctx); const skillDir = join(skillsDir, name); if (!existsSync(skillDir)) mkdirSync(skillDir, { recursive: true }); writeFileSync(join(skillDir, "SKILL.md"), skill.content, "utf-8"); // Write any additional files if (skill.files && typeof skill.files === "object") { for (const [filename, content] of Object.entries(skill.files)) { writeFileSync(join(skillDir, filename), content as string, "utf-8"); } } ctx.ui.notify( `\u2705 Skill '${name}' installed to ${skillDir}\nRestart JAE to activate.`, "info", ); } catch (err: any) { ctx.ui.notify(`Failed to install skill: ${err.message}`, "error"); } }, }); // /publish-skill command pi.registerCommand("publish-skill", { description: "Publish a local skill to the marketplace: /publish-skill ", handler: async (args, ctx) => { const name = args.trim(); if (!name) { ctx.ui.notify("Usage: /publish-skill ", "warning"); return; } const skillsDir = getSkillsDir(ctx); const skillDir = join(skillsDir, name); const skillMd = join(skillDir, "SKILL.md"); if (!existsSync(skillMd)) { ctx.ui.notify(`Skill '${name}' not found locally at ${skillDir}`, "error"); return; } const content = readFileSync(skillMd, "utf-8"); // Collect extra files const files: Record = {}; try { for (const f of readdirSync(skillDir)) { if (f !== "SKILL.md") { files[f] = readFileSync(join(skillDir, f), "utf-8"); } } } catch { /* ignore */ } ctx.ui.notify(`\u{1F4E4} Publishing skill: ${name}...`, "info"); try { const payload = JSON.stringify({ name, content, files }); const result = await pi.exec( "curl", ["-s", "-f", "-X", "POST", "-H", "Content-Type: application/json", "-d", payload, SKILLS_API], ); if (result.code === 0) { ctx.ui.notify(`\u2705 Skill '${name}' published to marketplace!`, "info"); } else { ctx.ui.notify(`Publish failed: ${result.stderr}`, "error"); } } catch (err: any) { ctx.ui.notify(`Publish error: ${err.message}`, "error"); } }, }); // /skills command - list local skills pi.registerCommand("skills", { description: "List locally installed skills", handler: async (_args, ctx) => { const local = listLocalSkills(ctx); if (local.length === 0) { ctx.ui.notify("No skills installed. Use /search-skills and /install-skill to get started.", "info"); return; } ctx.ui.notify(`\u{1F9E0} Installed Skills (${local.length}):\n${local.map((s) => ` - ${s}`).join("\n")}`, "info"); }, }); // LLM tool for skill search pi.registerTool({ name: "search_skills", label: "Search Skills", description: "Search the JAE skill marketplace for installable skills", parameters: Type.Object({ query: Type.String({ description: "Search query for skills" }), }), execute: async (args, _ctx) => { try { const skills = await fetchJSON(`${SKILLS_API}/search?q=${encodeURIComponent(args.query)}`); return { output: JSON.stringify(skills, null, 2) }; } catch (err: any) { return { error: `Skill search failed: ${err.message}` }; } }, }); }