import { useSSRContext, defineComponent, ref, computed, watch, mergeProps } from 'vue'; import { marked } from 'marked'; import { ssrRenderAttrs, ssrRenderAttr, ssrInterpolate, ssrRenderList, ssrRenderClass, ssrIncludeBooleanAttr, ssrLooseContain, ssrLooseEqual } from 'vue/server-renderer'; import { _ as _export_sfc } from './_plugin-vue_export-helper_B1lnwsE2.mjs'; const _sfc_main = /* @__PURE__ */ defineComponent({ __name: "SkillEditor", props: { mode: {}, slug: {}, initialName: {}, initialDescription: {}, initialAllowedTools: {}, initialArgumentHint: {}, initialModel: {}, initialUserInvocable: { type: Boolean }, initialDisableModelInvocation: { type: Boolean }, initialContext: {}, initialAgent: {}, initialHooks: {}, initialBody: {}, availableTools: {}, availableModels: {} }, setup(__props, { expose: __expose }) { __expose(); const props = __props; const AVAILABLE_TOOLS = props.availableTools ?? [ "Bash", "Read", "Write", "Edit", "Glob", "Grep", "WebFetch", "WebSearch", "Task", "NotebookEdit" ]; const AVAILABLE_MODELS = props.availableModels ?? [ { id: "claude-opus-4-6", display_name: "Claude Opus 4.6" }, { id: "claude-sonnet-4-5-20250929", display_name: "Claude Sonnet 4.5" }, { id: "claude-haiku-4-5-20251001", display_name: "Claude Haiku 4.5" } ]; const name = ref(props.initialName || ""); const description = ref(props.initialDescription || ""); const argumentHint = ref(props.initialArgumentHint || ""); const model = ref(props.initialModel || ""); const userInvocable = ref(props.initialUserInvocable ?? true); const disableModelInvocation = ref(props.initialDisableModelInvocation ?? false); const context = ref(props.initialContext || ""); const agent = ref(props.initialAgent || ""); const hooksJson = ref(props.initialHooks || ""); const body = ref(props.initialBody || ""); const saving = ref(false); const error = ref(""); const selectedTools = ref(new Set( props.initialAllowedTools ? props.initialAllowedTools.split(",").map((t) => t.trim()).filter(Boolean) : [] )); function toggleTool(tool) { if (selectedTools.value.has(tool)) { selectedTools.value.delete(tool); } else { selectedTools.value.add(tool); } selectedTools.value = new Set(selectedTools.value); } const computedSlug = computed(() => { if (props.mode === "edit" && props.slug) return props.slug; return name.value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "my-skill"; }); let previewHtml = ref(""); let debounceTimer; watch(body, (val) => { clearTimeout(debounceTimer); debounceTimer = setTimeout(async () => { previewHtml.value = await marked(val || ""); }, 300); }, { immediate: true }); function buildContent() { const tools = [...selectedTools.value]; const lines = ["---"]; lines.push(`name: ${name.value}`); if (description.value) lines.push(`description: ${description.value}`); if (argumentHint.value) lines.push(`argument-hint: ${argumentHint.value}`); if (tools.length > 0) lines.push(`allowed-tools: ${tools.join(", ")}`); if (model.value) lines.push(`model: ${model.value}`); if (userInvocable.value === false) lines.push("user-invocable: false"); if (disableModelInvocation.value) lines.push("disable-model-invocation: true"); if (context.value) lines.push(`context: ${context.value}`); if (agent.value) lines.push(`agent: ${agent.value}`); if (hooksJson.value.trim()) { try { const parsed = JSON.parse(hooksJson.value.trim()); lines.push(`hooks: ${JSON.stringify(parsed)}`); } catch { } } lines.push("---"); return lines.join("\n") + "\n\n" + body.value.trim() + "\n"; } async function save() { saving.value = true; error.value = ""; try { const content = buildContent(); if (props.mode === "create") { const res = await fetch("/api/skills", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ slug: computedSlug.value, content }) }); if (!res.ok) { const data = await res.json(); throw new Error(data.error || "Failed to create skill"); } window.location.href = `/${computedSlug.value}`; } else { const res = await fetch(`/api/skills/${props.slug}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ content }) }); if (!res.ok) { const data = await res.json(); throw new Error(data.error || "Failed to update skill"); } window.location.href = `/${props.slug}`; } } catch (err) { error.value = err instanceof Error ? err.message : "Something went wrong"; } finally { saving.value = false; } } const __returned__ = { props, AVAILABLE_TOOLS, AVAILABLE_MODELS, name, description, argumentHint, model, userInvocable, disableModelInvocation, context, agent, hooksJson, body, saving, error, selectedTools, toggleTool, computedSlug, get previewHtml() { return previewHtml; }, set previewHtml(v) { previewHtml = v; }, get debounceTimer() { return debounceTimer; }, set debounceTimer(v) { debounceTimer = v; }, buildContent, save }; Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true }); return __returned__; } }); function _sfc_ssrRender(_ctx, _push, _parent, _attrs, $props, $setup, $data, $options) { _push(`

Slug: ${ssrInterpolate($setup.computedSlug)}

`); ssrRenderList($setup.AVAILABLE_TOOLS, (tool) => { _push(``); }); _push(`

Fork runs the skill in an isolated subagent context

Hooks (advanced)

JSON object. Leave empty to omit.

Preview

${$setup.previewHtml ?? ""}
Cancel`); if ($setup.error) { _push(`

${ssrInterpolate($setup.error)}

`); } else { _push(``); } _push(`
`); } const _sfc_setup = _sfc_main.setup; _sfc_main.setup = (props, ctx) => { const ssrContext = useSSRContext(); (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/components/SkillEditor.vue"); return _sfc_setup ? _sfc_setup(props, ctx) : void 0; }; const SkillEditor = /* @__PURE__ */ _export_sfc(_sfc_main, [["ssrRender", _sfc_ssrRender]]); const FALLBACK_TOOLS = [ "Bash", "Read", "Write", "Edit", "MultiEdit", "Glob", "Grep", "WebFetch", "WebSearch", "Task", "NotebookEdit", "NotebookRead", "TodoRead", "TodoWrite" ]; const GIST_URL = "https://gist.githubusercontent.com/wong2/e0f34aac66caf890a332f7b6f9e2ba8f/raw"; const IGNORED = /* @__PURE__ */ new Set(["LS", "exit_plan_mode", "Agent", "BashOutput", "KillShell", "SlashCommand", "ExitPlanMode"]); let cached$1 = null; let lastFetch$1 = 0; const CACHE_TTL$1 = 1e3 * 60 * 60; async function getAvailableTools() { if (cached$1 && Date.now() - lastFetch$1 < CACHE_TTL$1) { return cached$1; } try { const res = await fetch(GIST_URL, { signal: AbortSignal.timeout(5e3) }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const text = await res.text(); const matches = text.matchAll(/"name":\s*"([A-Z][a-zA-Z]+)"/g); const tools = /* @__PURE__ */ new Set(); for (const m of matches) { if (!IGNORED.has(m[1])) { tools.add(m[1]); } } if (tools.size >= 5) { cached$1 = [...tools].sort(); lastFetch$1 = Date.now(); return cached$1; } } catch { } cached$1 = FALLBACK_TOOLS; lastFetch$1 = Date.now(); return cached$1; } const FALLBACK_MODELS = [ { id: "claude-opus-4-6", display_name: "Claude Opus 4.6" }, { id: "claude-sonnet-4-5-20250929", display_name: "Claude Sonnet 4.5" }, { id: "claude-haiku-4-5-20251001", display_name: "Claude Haiku 4.5" } ]; const DOCS_URL = "https://platform.claude.com/docs/en/about-claude/models/overview"; const MODEL_ID_RE = /claude-(?:opus|sonnet|haiku|3-\d+-(?:opus|sonnet|haiku)|3-(?:opus|sonnet|haiku))-[\w-]*\d+(?:-\d{8})?/g; let cached = null; let lastFetch = 0; const CACHE_TTL = 1e3 * 60 * 60 * 24; function displayName(id) { const clean = id.replace(/-\d{8}$/, "").replace(/^claude-/, ""); const parts = clean.split("-"); const words = parts.map((p, i) => { if (/^\d+$/.test(p) && i === parts.length - 1 && parts.length > 1) { const prev = parts[i - 1]; if (/^\d+$/.test(prev)) return null; return p; } return p.charAt(0).toUpperCase() + p.slice(1); }).filter(Boolean); const result = []; for (const w of words) { if (/^\d+$/.test(w) && result.length > 0 && /^\d+$/.test(result[result.length - 1])) { result[result.length - 1] += "." + w; } else { result.push(w); } } return "Claude " + result.join(" "); } async function getAvailableModels() { if (cached && Date.now() - lastFetch < CACHE_TTL) { return cached; } const apiKey = process.env.ANTHROPIC_API_KEY; if (apiKey) { try { const res = await fetch("https://api.anthropic.com/v1/models?limit=100", { headers: { "x-api-key": apiKey, "anthropic-version": "2023-06-01" }, signal: AbortSignal.timeout(5e3) }); if (res.ok) { const body = await res.json(); const models = body.data.filter((m) => m.id.startsWith("claude-")).map((m) => ({ id: m.id, display_name: m.display_name })); if (models.length > 0) { cached = models; lastFetch = Date.now(); return cached; } } } catch { } } try { const res = await fetch(DOCS_URL, { signal: AbortSignal.timeout(5e3) }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const html = await res.text(); const seen = /* @__PURE__ */ new Set(); const models = []; for (const match of html.matchAll(MODEL_ID_RE)) { const id = match[0]; if (id.endsWith("-v1") || id.includes("-v1:")) continue; if (seen.has(id)) continue; seen.add(id); models.push({ id, display_name: displayName(id) }); } const deduped = /* @__PURE__ */ new Map(); for (const m of models) { const base = m.id.replace(/-\d{8}$/, "").replace(/-0$/, ""); if (!deduped.has(base) || m.id.length < deduped.get(base).id.length) { deduped.set(base, m); } } const result = [...deduped.values()]; if (result.length > 0) { cached = result; lastFetch = Date.now(); return cached; } } catch { } cached = FALLBACK_MODELS; lastFetch = Date.now(); return cached; } export { SkillEditor as S, getAvailableModels as a, getAvailableTools as g };