Files
pqdongandmomothemage 5826001795 feat: Add support for Chinese Japanese and Korean(CJK) skills search (#1596)
Merged via squash.

Prepared head SHA: ab58f01be7
Co-authored-by: pq-dong <40668796+pq-dong@users.noreply.github.com>
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
Reviewed-by: @momothemage
2026-04-10 12:20:40 +08:00

558 lines
15 KiB
TypeScript

/**
* Extra seed skills for pagination testing.
*
* This file contains 50 placeholder skills to test pagination behavior.
* Run with: bunx convex run internal.devSeedExtra.seedExtraSkillsInternal
* Or with reset: bunx convex run internal.devSeedExtra.seedExtraSkillsInternal '{"reset": true}'
*/
import { v } from "convex/values";
import { internal } from "./_generated/api";
import type { Id } from "./_generated/dataModel";
import type { ActionCtx } from "./_generated/server";
import { internalAction, internalMutation } from "./functions";
import { parseClawdisMetadata, parseFrontmatter } from "./lib/skills";
type SeedSkillSpec = {
slug: string;
displayName: string;
summary: string;
version: string;
metadata: Record<string, unknown>;
rawSkillMd: string;
};
function makeSkill(
slug: string,
displayName: string,
summary: string,
envVars: string[] = [],
commands: string[] = ["help", "status", "run"],
): SeedSkillSpec {
const cliHelp = `${slug} - ${summary}
Usage:
${slug} [command]
Commands:
${commands.map((cmd) => ` ${cmd.padEnd(12)} Run ${cmd} operation`).join("\n")}
Flags:
-h, --help Show help
--json Output as JSON
`;
const rawSkillMd = `---
name: ${slug}
description: ${summary}
---
# ${displayName}
## CLI
\`\`\`bash
${commands.map((cmd) => `${slug} ${cmd}`).join("\n")}
\`\`\`
## Usage
Use this skill to ${summary.toLowerCase()}.
`;
return {
slug,
displayName,
summary,
version: "0.1.0",
metadata: {
clawdbot: {
nix: {
plugin: `github:example/${slug}`,
systems: ["aarch64-darwin", "x86_64-linux"],
},
config: {
requiredEnv: envVars,
},
cliHelp,
},
},
rawSkillMd,
};
}
// 50 placeholder skills for pagination testing
const EXTRA_SEED_SKILLS: SeedSkillSpec[] = [
// DevOps & Infrastructure (10)
makeSkill(
"kubectl-helper",
"Kubectl Helper",
"Simplified kubectl commands for common Kubernetes operations.",
["KUBECONFIG"],
["pods", "logs", "exec", "describe", "apply"],
),
makeSkill(
"terraform-runner",
"Terraform Runner",
"Execute Terraform plans and applies with safety checks.",
["TF_VAR_region", "AWS_PROFILE"],
["plan", "apply", "destroy", "output", "state"],
),
makeSkill(
"ansible-exec",
"Ansible Exec",
"Run Ansible playbooks and ad-hoc commands.",
["ANSIBLE_INVENTORY"],
["playbook", "adhoc", "inventory", "facts", "vault"],
),
makeSkill(
"docker-compose-mgr",
"Docker Compose Manager",
"Manage Docker Compose stacks and services.",
["DOCKER_HOST"],
["up", "down", "logs", "ps", "restart"],
),
makeSkill(
"k9s-wrapper",
"K9s Wrapper",
"Interactive Kubernetes cluster management via K9s.",
["KUBECONFIG"],
["launch", "contexts", "namespaces", "pods", "logs"],
),
makeSkill(
"helm-charts",
"Helm Charts",
"Manage Helm chart deployments and releases.",
["KUBECONFIG", "HELM_REPO"],
["install", "upgrade", "rollback", "list", "search"],
),
makeSkill(
"prometheus-alerts",
"Prometheus Alerts",
"Query Prometheus metrics and manage alerting rules.",
["PROMETHEUS_URL"],
["query", "alerts", "rules", "targets", "status"],
),
makeSkill(
"grafana-dash",
"Grafana Dashboards",
"Create and manage Grafana dashboards programmatically.",
["GRAFANA_URL", "GRAFANA_API_KEY"],
["list", "export", "import", "create", "delete"],
),
makeSkill(
"nginx-config",
"Nginx Config",
"Generate and validate Nginx configuration files.",
["NGINX_CONF_DIR"],
["generate", "validate", "reload", "test", "sites"],
),
makeSkill(
"jenkins-jobs",
"Jenkins Jobs",
"Manage Jenkins jobs and pipelines.",
["JENKINS_URL", "JENKINS_TOKEN"],
["list", "build", "status", "logs", "config"],
),
// Productivity (8)
makeSkill(
"todoist-sync",
"Todoist Sync",
"Sync and manage Todoist tasks from the command line.",
["TODOIST_API_TOKEN"],
["list", "add", "complete", "projects", "labels"],
),
makeSkill(
"notion-backup",
"Notion Backup",
"Export and backup Notion workspaces.",
["NOTION_TOKEN"],
["export", "backup", "restore", "pages", "databases"],
),
makeSkill(
"gcal-manager",
"Google Calendar Manager",
"Manage Google Calendar events and schedules.",
["GOOGLE_CREDENTIALS_FILE"],
["events", "create", "delete", "calendars", "reminders"],
),
makeSkill(
"time-tracker",
"Time Tracker",
"Track time spent on projects and tasks.",
["TIMETRACK_DB"],
["start", "stop", "status", "report", "projects"],
),
makeSkill(
"email-digest",
"Email Digest",
"Generate email digests and summaries.",
["IMAP_SERVER", "IMAP_USER"],
["fetch", "digest", "search", "folders", "unread"],
),
makeSkill(
"habit-tracker",
"Habit Tracker",
"Track daily habits and streaks.",
["HABITS_DB"],
["log", "streak", "stats", "habits", "remind"],
),
makeSkill(
"bookmark-sync",
"Bookmark Sync",
"Sync bookmarks across browsers and devices.",
["BOOKMARKS_DIR"],
["sync", "export", "import", "search", "tags"],
),
makeSkill(
"notes-export",
"Notes Export",
"Export notes to various formats.",
["NOTES_DIR"],
["export", "convert", "search", "list", "tags"],
),
// Media & Entertainment (6)
makeSkill(
"spotify-ctl",
"Spotify Control",
"Control Spotify playback from the terminal.",
["SPOTIFY_CLIENT_ID", "SPOTIFY_CLIENT_SECRET"],
["play", "pause", "next", "prev", "search"],
),
makeSkill(
"plex-manager",
"Plex Manager",
"Manage Plex media libraries and playback.",
["PLEX_URL", "PLEX_TOKEN"],
["libraries", "scan", "search", "play", "sessions"],
),
makeSkill(
"ytdl-wrapper",
"YouTube Downloader",
"Download videos from YouTube and other platforms.",
["YTDL_OUTPUT_DIR"],
["download", "info", "playlist", "audio", "formats"],
),
makeSkill(
"podcast-dl",
"Podcast Downloader",
"Download and manage podcast episodes.",
["PODCAST_DIR"],
["subscribe", "download", "list", "play", "search"],
),
makeSkill(
"audiobook-player",
"Audiobook Player",
"Manage and play audiobook collections.",
["AUDIOBOOK_DIR"],
["play", "pause", "bookmark", "list", "progress"],
),
makeSkill(
"music-lib",
"Music Library",
"Organize and query local music libraries.",
["MUSIC_DIR"],
["scan", "search", "play", "playlist", "stats"],
),
// Smart Home (8)
makeSkill(
"hass-control",
"Home Assistant Control",
"Control Home Assistant entities and automations.",
["HASS_URL", "HASS_TOKEN"],
["entities", "services", "automations", "scenes", "history"],
),
makeSkill(
"zigbee-mqtt",
"Zigbee2MQTT",
"Manage Zigbee devices via MQTT.",
["MQTT_BROKER", "ZIGBEE_TOPIC"],
["devices", "pair", "remove", "rename", "groups"],
),
makeSkill(
"tasmota-ctl",
"Tasmota Control",
"Control Tasmota-flashed devices.",
["TASMOTA_HOSTS"],
["status", "power", "config", "update", "backup"],
),
makeSkill(
"esphome-mgr",
"ESPHome Manager",
"Manage ESPHome device configurations.",
["ESPHOME_DIR"],
["compile", "upload", "logs", "dashboard", "config"],
),
makeSkill(
"mqtt-broker",
"MQTT Broker",
"Interact with MQTT brokers for IoT messaging.",
["MQTT_BROKER", "MQTT_USER"],
["pub", "sub", "topics", "clients", "stats"],
),
makeSkill(
"hue-lights",
"Philips Hue",
"Control Philips Hue lights and scenes.",
["HUE_BRIDGE_IP", "HUE_API_KEY"],
["lights", "scenes", "groups", "schedules", "sensors"],
),
makeSkill(
"smart-thermo",
"Smart Thermostat",
"Control smart thermostats and HVAC systems.",
["THERMOSTAT_API_KEY"],
["status", "set", "schedule", "history", "zones"],
),
makeSkill(
"cam-viewer",
"Camera Viewer",
"View and manage security camera feeds.",
["CAMERA_URLS"],
["list", "snapshot", "stream", "record", "events"],
),
// Finance (5)
makeSkill(
"budget-track",
"Budget Tracker",
"Track budgets and spending across categories.",
["BUDGET_DB"],
["summary", "add", "categories", "report", "goals"],
),
makeSkill(
"crypto-watch",
"Crypto Watcher",
"Monitor cryptocurrency prices and portfolios.",
["CRYPTO_API_KEY"],
["prices", "portfolio", "alerts", "history", "convert"],
),
makeSkill(
"stock-alerts",
"Stock Alerts",
"Set up stock price alerts and notifications.",
["STOCK_API_KEY"],
["quote", "watch", "alerts", "portfolio", "news"],
),
makeSkill(
"expense-cat",
"Expense Categorizer",
"Automatically categorize expenses.",
["EXPENSE_DB"],
["import", "categorize", "report", "rules", "export"],
),
makeSkill(
"invoice-gen",
"Invoice Generator",
"Generate and manage invoices.",
["INVOICE_DIR", "COMPANY_INFO"],
["create", "list", "send", "paid", "overdue"],
),
// Communication (5)
makeSkill(
"slack-bot",
"Slack Bot",
"Interact with Slack channels and messages.",
["SLACK_TOKEN"],
["send", "channels", "users", "search", "files"],
),
makeSkill(
"discord-mgr",
"Discord Manager",
"Manage Discord servers and messages.",
["DISCORD_TOKEN"],
["send", "servers", "channels", "members", "roles"],
),
makeSkill(
"telegram-bot",
"Telegram Bot",
"Send and receive Telegram messages.",
["TELEGRAM_BOT_TOKEN"],
["send", "receive", "chats", "files", "inline"],
),
makeSkill(
"matrix-cli",
"Matrix CLI",
"Interact with Matrix chat rooms.",
["MATRIX_HOMESERVER", "MATRIX_TOKEN"],
["send", "rooms", "join", "leave", "sync"],
),
makeSkill(
"irc-bridge",
"IRC Bridge",
"Bridge IRC channels to other platforms.",
["IRC_SERVER", "IRC_NICK"],
["connect", "join", "send", "channels", "users"],
),
// Data & Analytics (5)
makeSkill(
"pg-queries",
"PostgreSQL Queries",
"Execute PostgreSQL queries and manage databases.",
["DATABASE_URL"],
["query", "tables", "schema", "backup", "restore"],
),
makeSkill(
"clickhouse-ql",
"ClickHouse Queries",
"Run ClickHouse analytics queries.",
["CLICKHOUSE_URL"],
["query", "tables", "insert", "system", "optimize"],
),
makeSkill(
"redis-cli",
"Redis CLI",
"Interact with Redis cache and data structures.",
["REDIS_URL"],
["get", "set", "keys", "info", "flush"],
),
makeSkill(
"elastic-search",
"Elasticsearch",
"Search and manage Elasticsearch indices.",
["ELASTICSEARCH_URL"],
["search", "index", "mapping", "cluster", "aliases"],
),
makeSkill(
"mongo-shell",
"MongoDB Shell",
"Query and manage MongoDB collections.",
["MONGODB_URI"],
["find", "insert", "update", "delete", "aggregate"],
),
// Security (3)
makeSkill(
"vault-secrets",
"Vault Secrets",
"Manage secrets in HashiCorp Vault.",
["VAULT_ADDR", "VAULT_TOKEN"],
["read", "write", "list", "delete", "seal"],
),
makeSkill(
"gpg-keys",
"GPG Keys",
"Manage GPG keys and encryption.",
["GNUPGHOME"],
["list", "generate", "export", "import", "encrypt"],
),
makeSkill(
"ssh-rotate",
"SSH Key Rotator",
"Rotate and manage SSH keys.",
["SSH_KEY_DIR"],
["generate", "rotate", "deploy", "list", "revoke"],
),
// CJK Language Support (2)
makeSkill(
"nihongo-check",
"日本語チェッカー",
"日本語文章の文法チェックと翻訳支援ツール。Japanese grammar checker and translation assistant.",
["NIHONGO_API_KEY"],
["check", "translate", "kanji", "grammar", "vocabulary"],
),
makeSkill(
"hangukgeo-helper",
"한국어 도우미",
"한국어 학습 보조 도구입니다. Korean language learning assistant with vocabulary and grammar support.",
["HANGUL_API_KEY"],
["learn", "quiz", "vocabulary", "grammar", "pronunciation"],
),
];
function injectMetadata(rawSkillMd: string, metadata: Record<string, unknown>) {
const frontmatterEnd = rawSkillMd.indexOf("\n---", 3);
if (frontmatterEnd === -1) return rawSkillMd;
return `${rawSkillMd.slice(0, frontmatterEnd)}\nmetadata: ${JSON.stringify(
metadata,
)}${rawSkillMd.slice(frontmatterEnd)}`;
}
function randomStats() {
return {
downloads: Math.floor(Math.random() * 5000),
stars: Math.floor(Math.random() * 500),
installsCurrent: Math.floor(Math.random() * 200),
installsAllTime: Math.floor(Math.random() * 1000),
};
}
export const applyRandomStats = internalMutation({
args: {
skillId: v.id("skills"),
stats: v.object({
downloads: v.number(),
stars: v.number(),
installsCurrent: v.number(),
installsAllTime: v.number(),
}),
},
handler: async (ctx, args) => {
await ctx.db.patch(args.skillId, {
statsDownloads: args.stats.downloads,
statsStars: args.stats.stars,
statsInstallsCurrent: args.stats.installsCurrent,
statsInstallsAllTime: args.stats.installsAllTime,
stats: {
downloads: args.stats.downloads,
stars: args.stats.stars,
installsCurrent: args.stats.installsCurrent,
installsAllTime: args.stats.installsAllTime,
versions: 1,
comments: 0,
},
});
},
});
export const seedExtraSkillsInternal = internalAction({
args: {
reset: v.optional(v.boolean()),
},
handler: async (ctx: ActionCtx, args) => {
const results: Array<{ slug: string; ok: boolean; skipped?: boolean }> = [];
for (const spec of EXTRA_SEED_SKILLS) {
const skillMd = injectMetadata(spec.rawSkillMd, spec.metadata);
const frontmatter = parseFrontmatter(skillMd);
const clawdis = parseClawdisMetadata(frontmatter);
const storageId = await ctx.storage.store(new Blob([skillMd], { type: "text/markdown" }));
const result = (await ctx.runMutation(internal.devSeed.seedSkillMutation, {
reset: args.reset,
storageId,
metadata: spec.metadata,
frontmatter,
clawdis,
skillMd,
slug: spec.slug,
displayName: spec.displayName,
summary: spec.summary,
version: spec.version,
})) as { ok: boolean; skipped?: boolean; skillId?: string };
// Apply random stats after creation (only if not skipped)
if (result.skillId && !result.skipped) {
const stats = randomStats();
await ctx.runMutation(internal.devSeedExtra.applyRandomStats, {
skillId: result.skillId as Id<"skills">,
stats,
});
}
results.push({ slug: spec.slug, ok: result.ok, skipped: result.skipped });
}
const created = results.filter((r) => !r.skipped).length;
const skipped = results.filter((r) => r.skipped).length;
return { ok: true, total: results.length, created, skipped };
},
});