// Name: Snippet Manager // Description: Create, search, and run Script Kit snippet scripts with keywords, tags, and expand triggers. // Author: tayiorbeii // GitHub: tayiorbeii import "@johnlindquist/kit" type SnippetType = "text" | "script" type SnippetRecord = { name: string description?: string type: SnippetType keywords?: string[] expand?: string group?: string filePath: string command: string // e.g., "snippets/my-snippet" } const SNIPPETS_ROOT = kenvPath("scripts", "snippets") await ensureDir(SNIPPETS_ROOT) // Utilities const slug = (s: string) => s .trim() .toLowerCase() .replace(/[^a-z0-9]+/g, "-") .replace(/(^-|-$)/g, "") const sanitizeGroupPath = (group?: string) => { if (!group) return "" return group .split("/") .map(p => slug(p)) .filter(Boolean) .join(path.sep) } const ensureGroupDir = async (group?: string) => { const subdir = sanitizeGroupPath(group) const dir = subdir ? path.join(SNIPPETS_ROOT, subdir) : SNIPPETS_ROOT await ensureDir(dir) return dir } const toArray = (s?: string) => (s || "") .split(",") .map(x => x.trim()) .filter(Boolean) const parseMetadataFromContent = (content: string) => { // Simple parser for top metadata comments // e.g. // Name: Foo const meta: Record<string, string> = {} const lines = content.split("\n").slice(0, 40) for (const line of lines) { const m = line.match(/^\s*\/\/\s*([A-Za-z-]+)\s*:\s*(.*)$/) if (m) { const key = m[1].trim().toLowerCase() const value = m[2].trim() meta[key] = value } } // Support custom: // Snippet-Type: text|script const type = (meta["snippet-type"] as SnippetType) || ("text" as SnippetType) return { name: meta["name"] || "Untitled", description: meta["description"] || "", keywords: toArray(meta["keyword"] || meta["keywords"]), expand: meta["expand"] || "", group: meta["group"] || "", type, } } const scanSnippets = async (): Promise<SnippetRecord[]> => { const patterns = [kenvPath("scripts", "snippets", "**/*.ts"), kenvPath("scripts", "snippets", "**/*.js")] const files = await globby(patterns) const results: SnippetRecord[] = [] for await (const file of files) { try { const content = await readFile(file, "utf8") const parsed = parseMetadataFromContent(content) const rel = path.relative(kenvPath("scripts"), file) const withoutExt = rel.replace(/\.(ts|js)$/i, "") results.push({ name: parsed.name, description: parsed.description, type: parsed.type, keywords: parsed.keywords, expand: parsed.expand, group: parsed.group, filePath: file, command: withoutExt, }) } catch { // ignore } } return results } // Snippet generators const makeTextSnippetSource = (opts: { name: string description?: string keywords?: string[] expand?: string group?: string content: string }) => { const { name, description = "", keywords = [], expand = "", group = "", content } = opts const author = "tayiorbeii" const groupMeta = group ? `\n// Group: Snippets/${group}` : `\n// Group: Snippets` const expandMeta = expand ? `\n// Expand: ${expand}` : "" const keywordMeta = keywords.length ? `\n// Keyword: ${keywords.join(", ")}` : "" // Always use template() so users can include placeholders like ${1:default} return `// Name: ${name} // Description: ${description} // Author: ${author}${groupMeta}${expandMeta}${keywordMeta} // Snippet-Type: text import "@johnlindquist/kit" const result = await template( ${JSON.stringify(content)}, { height: PROMPT.HEIGHT.XXS } ) await setSelectedText(result) ` } const makeScriptSnippetSource = (opts: { name: string description?: string keywords?: string[] expand?: string group?: string code: string }) => { const { name, description = "", keywords = [], expand = "", group = "", code } = opts const author = "tayiorbeii" const groupMeta = group ? `\n// Group: Snippets/${group}` : `\n// Group: Snippets` const expandMeta = expand ? `\n// Expand: ${expand}` : "" const keywordMeta = keywords.length ? `\n// Keyword: ${keywords.join(", ")}` : "" return `// Name: ${name} // Description: ${description} // Author: ${author}${groupMeta}${expandMeta}${keywordMeta} // Snippet-Type: script import "@johnlindquist/kit" // Safety: This snippet executes JavaScript when run. ${code.trim().startsWith("(async") || code.trim().startsWith("await") ? "" : "// You can use top-level await here.\n"} ${code} ` } // Create snippet flow const createSnippet = async () => { const type = await arg<SnippetType>("Snippet Type", [ { name: "Insertable Text (expands via setSelectedText)", value: "text" }, { name: "Executable Script (runs JavaScript)", value: "script" }, ]) const [name, description, keywordsStr, group, expand] = await fields([ { label: "Name", placeholder: "Quick Email Signature" }, { label: "Description", placeholder: "Brief description" }, { label: "Keywords (comma separated)", placeholder: "email, signature, work" }, { label: "Group (optional, can include / for subfolders)", placeholder: "Emails" }, { label: "Expand Trigger (optional, text expansion)", placeholder: "sig" }, ]) if (!name) return const destDir = await ensureGroupDir(group) const fileSlug = slug(name) || `snippet-${Date.now()}` const filePath = path.join(destDir, `${fileSlug}.ts`) if (type === "text") { const content = await editor({ value: `Dear \${1:Name}, Thank you for your message. Best regards, \${2:Your Name} \${3:Your Title} \${4:Company} `, language: "md", footer: "Use ${1} placeholders. Cmd+S to save.", }) const src = makeTextSnippetSource({ name, description, keywords: toArray(keywordsStr), expand, group, content, }) await writeFile(filePath, src) } else { const code = await editor({ value: `// Example executable snippet: // let who = await arg("Who?") // await setSelectedText(\`Hello, \${who}!\`) `, language: "typescript", footer: "Write Script Kit code. Cmd+S to save.", }) const src = makeScriptSnippetSource({ name, description, keywords: toArray(keywordsStr), expand, group, code, }) await writeFile(filePath, src) } toast(`Saved: ${filePath}`) await edit(filePath) } // Preview builder const buildPreview = async (rec: SnippetRecord) => { const content = await readFile(rec.filePath, "utf8") const body = content .split("\n") .filter(l => !l.trim().startsWith("//")) .join("\n") .trim() const badge = rec.type === "script" ? "⚠️ Executable (JS)" : "📝 Insertable (Text)" const tags = rec.keywords?.length ? rec.keywords.map(t => `\`${t}\``).join(", ") : "_none_" const group = rec.group ? `Snippets/${rec.group}` : "Snippets" return md(` ### ${rec.name} - Type: ${badge} - Group: ${group} - Keywords: ${tags} - Command: \`${rec.command}\` - Expand: ${rec.expand || "_none_"} --- ${rec.type === "script" ? "#### Code" : "#### Template"} \`\`\`${rec.type === "script" ? "ts" : "md"} ${body.slice(0, 800)} \`\`\` `) } // Run/Insert selection const runOrInsert = async (rec: SnippetRecord) => { // Use run() so snippets are standalone scripts await run(rec.command) } // Search and manage const searchAndRun = async () => { const records = await scanSnippets() const choice = await arg( { placeholder: "Search snippets by name, keyword, or group", enter: "Run / Insert", actions: [ { name: "Edit", shortcut: `${cmd}+o`, onAction: async (input, { focused }) => { if (!focused?.value) return await edit(focused.value.filePath) }, visible: true, }, { name: "Reveal in Finder", shortcut: `${cmd}+shift+f`, onAction: async (input, { focused }) => { if (!focused?.value) return await revealFile(focused.value.filePath) }, visible: true, }, { name: "Delete", shortcut: `${cmd}+backspace`, onAction: async (input, { focused }) => { if (!focused?.value) return const ok = await arg(`Type DELETE to confirm removing "${focused.value.name}"`) if (ok === "DELETE") { await trash(focused.value.filePath) submit({ deleted: true }) } }, visible: true, }, ], }, async input => { const query = (input || "").toLowerCase().trim() const filtered = !query ? records : records.filter(r => { const hay = [ r.name, r.description || "", (r.keywords || []).join(" "), r.group || "", r.expand || "", ] .join(" ") .toLowerCase() return hay.includes(query) }) return filtered.map(r => ({ name: `${r.name} ${r.type === "script" ? "⚠️" : "📝"}`, description: [r.description, r.keywords?.join(", ")].filter(Boolean).join(" — "), value: r, group: r.group ? `Snippets/${r.group}` : "Snippets", preview: () => buildPreview(r), enter: r.type === "script" ? "Run (Executable)" : "Insert (Paste)", })) } ) if ((choice as any)?.deleted) { // refresh after delete return await searchAndRun() } if (!choice) return await runOrInsert(choice as SnippetRecord) } // Example snippets to install const installExamples = async () => { // 1) Storing and searching snippets by keyword (text) { const name = "Email Signature (Template)" const group = "Examples/Emails" const fileDir = await ensureGroupDir(group) const file = path.join(fileDir, `${slug(name)}.ts`) if (!(await pathExists(file))) { const src = makeTextSnippetSource({ name, description: "Insert a templated email signature with placeholders", keywords: ["email", "signature", "template"], expand: "sig", group, content: `Best regards, \${1:Your Name} \${2:Your Title} \${3:Company} \${4:Phone} • \${5:Website}`, }) await writeFile(file, src) } } // 2) JS snippet that prompts for values and inserts a custom result (script) { const name = "Greeting Generator (JS)" const group = "Examples/Scripts" const fileDir = await ensureGroupDir(group) const file = path.join(fileDir, `${slug(name)}.ts`) if (!(await pathExists(file))) { const code = `let who = await arg("Recipient name") let topic = await arg("Topic") await setSelectedText(\`Hi \${who},\\n\\nThanks for reaching out about \${topic}. Happy to help!\\n\\n— Your Friendly Bot\`)` const src = makeScriptSnippetSource({ name, description: "Prompts for values, then inserts a message", keywords: ["greeting", "prompt", "insert"], expand: "", group, code, }) await writeFile(file, src) } } // 3) Selecting between plain text vs executable is covered by types above. // 4) Running another Script Kit script from within a snippet { const name = "Run Deploy Script (JS)" const group = "Examples/Automation" const fileDir = await ensureGroupDir(group) const file = path.join(fileDir, `${slug(name)}.ts`) if (!(await pathExists(file))) { const code = `// Example: call another Script Kit script in your kenv // Replace "deploy-script" with an actual script name you have. // This demonstrates launching another automation from a snippet. try { await run("deploy-script") } catch (err) { await div(md(\`Deployment script not found or failed.\\n\\n\${String(err)}\`)) }` const src = makeScriptSnippetSource({ name, description: 'Calls await run("deploy-script") to launch another script', keywords: ["deploy", "automation", "run"], expand: "deploy", group, code, }) await writeFile(file, src) } } toast("Example snippets installed") } // Main menu const mainChoice = await arg( { placeholder: "Snippet Manager", enter: "Select", }, [ { name: "Create Snippet", description: "Build an insertable or executable snippet", value: "create", }, { name: "Search / Run / Insert", description: "Find snippets by name/keywords/group, then run or insert", value: "search", }, { name: "Install Example Snippets", description: "Adds demo snippets showing templates, prompts, and run()", value: "examples", }, { name: "Open Snippets Folder", description: SNIPPETS_ROOT, value: "open", }, ] ) if (mainChoice === "create") { await createSnippet() } else if (mainChoice === "search") { await searchAndRun() } else if (mainChoice === "examples") { await installExamples() } else if (mainChoice === "open") { await revealFile(SNIPPETS_ROOT) }