From f0660e522ebe4e5559f976e0184dd0060057a310 Mon Sep 17 00:00:00 2001 From: Ahmed Hesham Abdelkader <23265119+ahmedhesham6@users.noreply.github.com> Date: Mon, 19 Jan 2026 17:00:22 +0100 Subject: [PATCH] feat: add support for 7 new AI agents Sync agent configuration with vercel-labs/add-skill specification. New agents added: - Kilo Code (~/.kilocode/skills, .kilocode/skills) - Roo Code (~/.roo/skills, .roo/skills) - Gemini CLI (~/.gemini/skills, .gemini/skills) - Antigravity (~/.gemini/antigravity/skills, .agent/skills) - Clawdbot (~/.clawdbot/skills, skills) - Droid (~/.factory/skills, .factory/skills) - Windsurf (~/.codeium/windsurf/skills, .windsurf/skills) Fixes: - GitHub Copilot project dir changed from .copilot/skills to .github/skills Also includes: - 10 new tests for agent configuration - Updated UI install command dropdown with all 16 agents - Updated documentation with complete agent table - Updated SEO meta descriptions across web and docs --- apps/cli/src/commands/agent.rs | 7 + apps/cli/src/commands/core/config.rs | 394 +++++++++++++++++++- apps/docs/content/docs/agents.mdx | 29 +- apps/docs/src/routes/__root.tsx | 4 +- apps/web/src/components/features.tsx | 2 +- apps/web/src/components/install-command.tsx | 14 +- apps/web/src/routes/__root.tsx | 4 +- apps/web/src/routes/index.tsx | 2 +- apps/web/src/routes/search.tsx | 2 +- apps/web/src/routes/trending.tsx | 4 +- 10 files changed, 439 insertions(+), 23 deletions(-) diff --git a/apps/cli/src/commands/agent.rs b/apps/cli/src/commands/agent.rs index 795b1d4..d5abcad 100644 --- a/apps/cli/src/commands/agent.rs +++ b/apps/cli/src/commands/agent.rs @@ -110,6 +110,13 @@ pub async fn run(cmd: AgentCommand) -> Result<()> { "opencode", "amp", "codex", + "kilocode", + "roo", + "gemini", + "antigravity", + "clawdbot", + "droid", + "windsurf", ]; if builtins.contains(&name.as_str()) { bail!( diff --git a/apps/cli/src/commands/core/config.rs b/apps/cli/src/commands/core/config.rs index 03bcf11..db1b525 100644 --- a/apps/cli/src/commands/core/config.rs +++ b/apps/cli/src/commands/core/config.rs @@ -277,7 +277,7 @@ impl Config { skills_dir: dirs::home_dir() .map(|h| h.join(".copilot").join("skills")) .unwrap_or_else(|| PathBuf::from("~/.copilot/skills")), - project_skills_dir: Some(PathBuf::from(".copilot/skills")), + project_skills_dir: Some(PathBuf::from(".github/skills")), description: Some("GitHub Copilot CLI".to_string()), }, ); @@ -330,6 +330,90 @@ impl Config { }, ); + agents.insert( + "kilocode".to_string(), + AgentConfig { + name: "Kilo Code".to_string(), + skills_dir: dirs::home_dir() + .map(|h| h.join(".kilocode").join("skills")) + .unwrap_or_else(|| PathBuf::from("~/.kilocode/skills")), + project_skills_dir: Some(PathBuf::from(".kilocode/skills")), + description: Some("Kilo Code AI agent".to_string()), + }, + ); + + agents.insert( + "roo".to_string(), + AgentConfig { + name: "Roo Code".to_string(), + skills_dir: dirs::home_dir() + .map(|h| h.join(".roo").join("skills")) + .unwrap_or_else(|| PathBuf::from("~/.roo/skills")), + project_skills_dir: Some(PathBuf::from(".roo/skills")), + description: Some("Roo Code AI agent".to_string()), + }, + ); + + agents.insert( + "gemini".to_string(), + AgentConfig { + name: "Gemini CLI".to_string(), + skills_dir: dirs::home_dir() + .map(|h| h.join(".gemini").join("skills")) + .unwrap_or_else(|| PathBuf::from("~/.gemini/skills")), + project_skills_dir: Some(PathBuf::from(".gemini/skills")), + description: Some("Google's Gemini CLI agent".to_string()), + }, + ); + + agents.insert( + "antigravity".to_string(), + AgentConfig { + name: "Antigravity".to_string(), + skills_dir: dirs::home_dir() + .map(|h| h.join(".gemini").join("antigravity").join("skills")) + .unwrap_or_else(|| PathBuf::from("~/.gemini/antigravity/skills")), + project_skills_dir: Some(PathBuf::from(".agent/skills")), + description: Some("Google's Antigravity agent".to_string()), + }, + ); + + agents.insert( + "clawdbot".to_string(), + AgentConfig { + name: "Clawdbot".to_string(), + skills_dir: dirs::home_dir() + .map(|h| h.join(".clawdbot").join("skills")) + .unwrap_or_else(|| PathBuf::from("~/.clawdbot/skills")), + project_skills_dir: Some(PathBuf::from("skills")), + description: Some("Clawdbot AI agent".to_string()), + }, + ); + + agents.insert( + "droid".to_string(), + AgentConfig { + name: "Droid".to_string(), + skills_dir: dirs::home_dir() + .map(|h| h.join(".factory").join("skills")) + .unwrap_or_else(|| PathBuf::from("~/.factory/skills")), + project_skills_dir: Some(PathBuf::from(".factory/skills")), + description: Some("Droid AI agent".to_string()), + }, + ); + + agents.insert( + "windsurf".to_string(), + AgentConfig { + name: "Windsurf".to_string(), + skills_dir: dirs::home_dir() + .map(|h| h.join(".codeium").join("windsurf").join("skills")) + .unwrap_or_else(|| PathBuf::from("~/.codeium/windsurf/skills")), + project_skills_dir: Some(PathBuf::from(".windsurf/skills")), + description: Some("Codeium's Windsurf AI editor".to_string()), + }, + ); + agents } @@ -753,4 +837,312 @@ project_skills_dir = ".custom/skills" // but we can at least verify the function doesn't panic let _ = result; } + + #[test] + fn test_all_builtin_agents_exist() { + let config = Config::default_with_builtin_agents(); + + // All 16 builtin agents should exist + let expected_agents = [ + "stakpak", + "claude-code", + "cursor", + "vscode", + "copilot", + "goose", + "opencode", + "amp", + "codex", + "kilocode", + "roo", + "gemini", + "antigravity", + "clawdbot", + "droid", + "windsurf", + ]; + + assert_eq!( + config.agents.len(), + expected_agents.len(), + "Expected {} agents, found {}", + expected_agents.len(), + config.agents.len() + ); + + for agent_id in expected_agents { + assert!( + config.agents.contains_key(agent_id), + "Missing builtin agent: {}", + agent_id + ); + } + } + + #[test] + fn test_stakpak_is_first_agent() { + let config = Config::default_with_builtin_agents(); + + // stakpak should always be the first agent (insertion order preserved) + let first_agent = config.agents.keys().next(); + assert_eq!(first_agent, Some(&"stakpak".to_string())); + } + + #[test] + fn test_new_agents_project_skills_dir() { + let config = Config::default_with_builtin_agents(); + + // Kilo Code + let kilocode = config.get_agent("kilocode").unwrap(); + assert_eq!( + kilocode.project_skills_dir, + Some(PathBuf::from(".kilocode/skills")) + ); + + // Roo Code + let roo = config.get_agent("roo").unwrap(); + assert_eq!(roo.project_skills_dir, Some(PathBuf::from(".roo/skills"))); + + // Gemini CLI + let gemini = config.get_agent("gemini").unwrap(); + assert_eq!( + gemini.project_skills_dir, + Some(PathBuf::from(".gemini/skills")) + ); + + // Antigravity + let antigravity = config.get_agent("antigravity").unwrap(); + assert_eq!( + antigravity.project_skills_dir, + Some(PathBuf::from(".agent/skills")) + ); + + // Clawdbot + let clawdbot = config.get_agent("clawdbot").unwrap(); + assert_eq!(clawdbot.project_skills_dir, Some(PathBuf::from("skills"))); + + // Droid + let droid = config.get_agent("droid").unwrap(); + assert_eq!( + droid.project_skills_dir, + Some(PathBuf::from(".factory/skills")) + ); + + // Windsurf + let windsurf = config.get_agent("windsurf").unwrap(); + assert_eq!( + windsurf.project_skills_dir, + Some(PathBuf::from(".windsurf/skills")) + ); + } + + #[test] + fn test_new_agents_global_skills_dir() { + let config = Config::default_with_builtin_agents(); + + // Kilo Code - ~/.kilocode/skills + let kilocode = config.get_agent("kilocode").unwrap(); + assert!( + kilocode + .skills_dir + .to_string_lossy() + .ends_with(".kilocode/skills"), + "kilocode skills_dir should end with .kilocode/skills, got: {}", + kilocode.skills_dir.display() + ); + + // Roo Code - ~/.roo/skills + let roo = config.get_agent("roo").unwrap(); + assert!( + roo.skills_dir.to_string_lossy().ends_with(".roo/skills"), + "roo skills_dir should end with .roo/skills, got: {}", + roo.skills_dir.display() + ); + + // Gemini CLI - ~/.gemini/skills + let gemini = config.get_agent("gemini").unwrap(); + assert!( + gemini + .skills_dir + .to_string_lossy() + .ends_with(".gemini/skills"), + "gemini skills_dir should end with .gemini/skills, got: {}", + gemini.skills_dir.display() + ); + + // Antigravity - ~/.gemini/antigravity/skills + let antigravity = config.get_agent("antigravity").unwrap(); + assert!( + antigravity + .skills_dir + .to_string_lossy() + .ends_with(".gemini/antigravity/skills"), + "antigravity skills_dir should end with .gemini/antigravity/skills, got: {}", + antigravity.skills_dir.display() + ); + + // Clawdbot - ~/.clawdbot/skills + let clawdbot = config.get_agent("clawdbot").unwrap(); + assert!( + clawdbot + .skills_dir + .to_string_lossy() + .ends_with(".clawdbot/skills"), + "clawdbot skills_dir should end with .clawdbot/skills, got: {}", + clawdbot.skills_dir.display() + ); + + // Droid - ~/.factory/skills + let droid = config.get_agent("droid").unwrap(); + assert!( + droid + .skills_dir + .to_string_lossy() + .ends_with(".factory/skills"), + "droid skills_dir should end with .factory/skills, got: {}", + droid.skills_dir.display() + ); + + // Windsurf - ~/.codeium/windsurf/skills + let windsurf = config.get_agent("windsurf").unwrap(); + assert!( + windsurf + .skills_dir + .to_string_lossy() + .ends_with(".codeium/windsurf/skills"), + "windsurf skills_dir should end with .codeium/windsurf/skills, got: {}", + windsurf.skills_dir.display() + ); + } + + #[test] + fn test_github_copilot_uses_github_project_dir() { + let config = Config::default_with_builtin_agents(); + + // GitHub Copilot should use .github/skills for project dir (not .copilot/skills) + let copilot = config.get_agent("copilot").unwrap(); + assert_eq!( + copilot.project_skills_dir, + Some(PathBuf::from(".github/skills")), + "GitHub Copilot should use .github/skills for project_skills_dir" + ); + + // Global dir should still be ~/.copilot/skills + assert!( + copilot + .skills_dir + .to_string_lossy() + .ends_with(".copilot/skills"), + "copilot global skills_dir should end with .copilot/skills, got: {}", + copilot.skills_dir.display() + ); + } + + #[test] + fn test_opencode_uses_skill_not_skills() { + let config = Config::default_with_builtin_agents(); + + // OpenCode uses "skill" (singular) not "skills" + let opencode = config.get_agent("opencode").unwrap(); + assert_eq!( + opencode.project_skills_dir, + Some(PathBuf::from(".opencode/skill")), + "OpenCode should use .opencode/skill (singular)" + ); + + assert!( + opencode + .skills_dir + .to_string_lossy() + .contains("opencode/skill"), + "opencode global skills_dir should contain opencode/skill, got: {}", + opencode.skills_dir.display() + ); + } + + #[test] + fn test_all_agents_have_descriptions() { + let config = Config::default_with_builtin_agents(); + + for (id, agent) in &config.agents { + assert!( + agent.description.is_some(), + "Agent '{}' should have a description", + id + ); + assert!( + !agent.description.as_ref().unwrap().is_empty(), + "Agent '{}' description should not be empty", + id + ); + } + } + + #[test] + fn test_all_agents_have_display_names() { + let config = Config::default_with_builtin_agents(); + + for (id, agent) in &config.agents { + assert!(!agent.name.is_empty(), "Agent '{}' should have a name", id); + } + } + + #[test] + fn test_resolve_skills_dir_for_new_agents() { + let config = Config::default_with_builtin_agents(); + + // Test global scope resolution for new agents + let new_agents = [ + ("kilocode", ".kilocode/skills"), + ("roo", ".roo/skills"), + ("gemini", ".gemini/skills"), + ("windsurf", ".codeium/windsurf/skills"), + ]; + + for (agent_id, expected_suffix) in new_agents { + let result = config + .resolve_skills_dir(Scope::Global, Some(agent_id), None) + .unwrap(); + assert!( + result.to_string_lossy().ends_with(expected_suffix), + "Agent '{}' global dir should end with '{}', got: {}", + agent_id, + expected_suffix, + result.display() + ); + } + } + + #[test] + fn test_resolve_skills_dir_project_scope_for_new_agents() { + let config = Config::default_with_builtin_agents(); + + // Test project scope resolution for new agents + let new_agents = [ + ("kilocode", ".kilocode/skills"), + ("roo", ".roo/skills"), + ("gemini", ".gemini/skills"), + ("antigravity", ".agent/skills"), + ("clawdbot", "skills"), + ("droid", ".factory/skills"), + ("windsurf", ".windsurf/skills"), + ]; + + for (agent_id, expected_suffix) in new_agents { + let result = config.resolve_skills_dir(Scope::Project, Some(agent_id), None); + assert!( + result.is_ok(), + "Failed to resolve project dir for {}", + agent_id + ); + let path = result.unwrap(); + assert!( + path.to_string_lossy().ends_with(expected_suffix), + "Agent '{}' project dir should end with '{}', got: {}", + agent_id, + expected_suffix, + path.display() + ); + } + } } diff --git a/apps/docs/content/docs/agents.mdx b/apps/docs/content/docs/agents.mdx index 95087f8..2af7112 100644 --- a/apps/docs/content/docs/agents.mdx +++ b/apps/docs/content/docs/agents.mdx @@ -3,19 +3,28 @@ title: Agent Configuration description: Configure target AI agents for skill installation --- -Paks supports multiple AI coding agents out of the box. Each agent has a designated skills directory where skills are installed. +Paks supports multiple AI coding agents out of the box. Each agent has designated skills directories for both global and project-level installation. ## Built-in Agents -| Agent | Skills Directory | Description | -| ----- | ---------------- | ----------- | -| `stakpak` | `~/.stakpak/skills` | Stakpak AI agent (default) | -| `claude-code` | `~/.claude/skills` | Anthropic Claude Code | -| `cursor` | `~/.cursor/skills` | Cursor IDE | -| `vscode` | `~/.vscode/skills` | VS Code with AI extensions | -| `copilot` | `~/.copilot/skills` | GitHub Copilot | -| `goose` | `~/.goose/skills` | Goose AI agent | -| `opencode` | `~/.opencode/skills` | OpenCode agent | +| Agent | Global Directory | Project Directory | Description | +| ----- | ---------------- | ----------------- | ----------- | +| `stakpak` | `~/.stakpak/skills` | `.stakpak/skills` | Stakpak AI agent | +| `claude-code` | `~/.claude/skills` | `.claude/skills` | Anthropic Claude Code | +| `cursor` | `~/.cursor/skills` | `.cursor/skills` | Cursor IDE | +| `codex` | `~/.codex/skills` | `.codex/skills` | OpenAI Codex | +| `copilot` | `~/.copilot/skills` | `.github/skills` | GitHub Copilot | +| `opencode` | `~/.config/opencode/skill` | `.opencode/skill` | OpenCode agent | +| `amp` | `~/.config/agents/skills` | `.agents/skills` | Sourcegraph Amp | +| `goose` | `~/.config/goose/skills` | `.goose/skills` | Block's Goose agent | +| `gemini` | `~/.gemini/skills` | `.gemini/skills` | Google Gemini CLI | +| `windsurf` | `~/.codeium/windsurf/skills` | `.windsurf/skills` | Codeium Windsurf | +| `roo` | `~/.roo/skills` | `.roo/skills` | Roo Code | +| `kilocode` | `~/.kilocode/skills` | `.kilocode/skills` | Kilo Code | +| `antigravity` | `~/.gemini/antigravity/skills` | `.agent/skills` | Google Antigravity | +| `droid` | `~/.factory/skills` | `.factory/skills` | Droid | +| `clawdbot` | `~/.clawdbot/skills` | `skills` | Clawdbot | +| `vscode` | `~/.vscode/skills` | `.vscode/skills` | VS Code with AI extensions | ## Managing Agents diff --git a/apps/docs/src/routes/__root.tsx b/apps/docs/src/routes/__root.tsx index 537d7e4..54e88e6 100644 --- a/apps/docs/src/routes/__root.tsx +++ b/apps/docs/src/routes/__root.tsx @@ -6,7 +6,7 @@ import { RootProvider } from "fumadocs-ui/provider/tanstack"; const SITE_URL = "https://paks.stakpak.dev/docs"; const SITE_NAME = "Paks Docs"; const DEFAULT_TITLE = "Paks Documentation - AI Agent Skills Package Manager"; -const DEFAULT_DESCRIPTION = "Complete documentation for Paks CLI - create, install, publish, and manage skills for AI coding agents like Claude Code, Cursor, and GitHub Copilot."; +const DEFAULT_DESCRIPTION = "Complete documentation for Paks CLI - create, install, publish, and manage skills for AI coding agents like Claude Code, Cursor, Codex, Copilot, Windsurf, Gemini CLI, and more."; export const Route = createRootRoute({ head: () => ({ @@ -15,7 +15,7 @@ export const Route = createRootRoute({ { name: "viewport", content: "width=device-width, initial-scale=1" }, { title: DEFAULT_TITLE }, { name: "description", content: DEFAULT_DESCRIPTION }, - { name: "keywords", content: "paks, cli, ai agents, skills, package manager, claude code, cursor, copilot, documentation" }, + { name: "keywords", content: "paks, cli, ai agents, skills, package manager, claude code, cursor, codex, copilot, windsurf, gemini cli, amp, opencode, documentation" }, { name: "author", content: "Stakpak" }, { name: "robots", content: "index, follow" }, diff --git a/apps/web/src/components/features.tsx b/apps/web/src/components/features.tsx index 5011b06..c36599c 100644 --- a/apps/web/src/components/features.tsx +++ b/apps/web/src/components/features.tsx @@ -9,7 +9,7 @@ const features = [ { icon: Download, title: "Install Anywhere", - description: "Works with Claude Code, Cursor, VS Code, GitHub Copilot, Goose, and more AI agents.", + description: "Works with Claude Code, Cursor, Codex, Copilot, Windsurf, Gemini CLI, Amp, and 10+ more AI agents.", }, { icon: Search, diff --git a/apps/web/src/components/install-command.tsx b/apps/web/src/components/install-command.tsx index 2813eee..ac49c2a 100644 --- a/apps/web/src/components/install-command.tsx +++ b/apps/web/src/components/install-command.tsx @@ -8,12 +8,20 @@ interface InstallCommandProps { const agents = [ // { id: "stakpak", name: "Stakpak", flag: "stakpak" }, { id: "claude-code", name: "Claude Code", flag: "claude-code" }, - { id: "codex", name: "Codex", flag: "codex" }, { id: "cursor", name: "Cursor", flag: "cursor" }, + { id: "codex", name: "Codex", flag: "codex" }, { id: "copilot", name: "Copilot", flag: "copilot" }, - { id: "goose", name: "Goose", flag: "goose" }, { id: "opencode", name: "OpenCode", flag: "opencode" }, - { id: "amp", name: "AMP", flag: "amp" }, + { id: "amp", name: "Amp", flag: "amp" }, + { id: "goose", name: "Goose", flag: "goose" }, + { id: "gemini", name: "Gemini CLI", flag: "gemini" }, + { id: "windsurf", name: "Windsurf", flag: "windsurf" }, + { id: "roo", name: "Roo Code", flag: "roo" }, + { id: "kilocode", name: "Kilo Code", flag: "kilocode" }, + { id: "antigravity", name: "Antigravity", flag: "antigravity" }, + { id: "droid", name: "Droid", flag: "droid" }, + { id: "clawdbot", name: "Clawdbot", flag: "clawdbot" }, + { id: "vscode", name: "VS Code", flag: "vscode" }, ] as const; type AgentId = typeof agents[number]["id"]; diff --git a/apps/web/src/routes/__root.tsx b/apps/web/src/routes/__root.tsx index 349afed..ee2b19e 100644 --- a/apps/web/src/routes/__root.tsx +++ b/apps/web/src/routes/__root.tsx @@ -25,8 +25,8 @@ export interface RouterAppContext {} const SITE_URL = "https://paks.stakpak.dev"; const SITE_NAME = "Paks"; const DEFAULT_TITLE = "Paks - AI Agent Skills Package Manager"; -const DEFAULT_DESCRIPTION = "Create, install, publish, and share reusable skills for AI coding agents like Claude Code, Cursor, and GitHub Copilot. The npm for AI agent capabilities."; -const DEFAULT_KEYWORDS = "AI agents, coding agents, Claude Code, Cursor, GitHub Copilot, package manager, skills, rulebooks, AI tools, developer tools, automation"; +const DEFAULT_DESCRIPTION = "Create, install, publish, and share reusable skills for AI coding agents like Claude Code, Cursor, Codex, Copilot, Windsurf, Gemini CLI, and more. The npm for AI agent capabilities."; +const DEFAULT_KEYWORDS = "AI agents, coding agents, Claude Code, Cursor, Codex, GitHub Copilot, Windsurf, Gemini CLI, Amp, OpenCode, package manager, skills, rulebooks, AI tools, developer tools, automation"; export const Route = createRootRouteWithContext()({ head: () => ({ diff --git a/apps/web/src/routes/index.tsx b/apps/web/src/routes/index.tsx index f809628..2ecbfe3 100644 --- a/apps/web/src/routes/index.tsx +++ b/apps/web/src/routes/index.tsx @@ -14,7 +14,7 @@ export const Route = createFileRoute("/")({ head: () => ({ meta: [ { title: "Paks - AI Agent Skills Package Manager" }, - { name: "description", content: "Create, install, publish, and share reusable skills for AI coding agents like Claude Code, Cursor, and GitHub Copilot. The npm for AI agent capabilities." }, + { name: "description", content: "Create, install, publish, and share reusable skills for AI coding agents like Claude Code, Cursor, Codex, Copilot, Windsurf, Gemini CLI, and more. The npm for AI agent capabilities." }, { property: "og:image", content: "https://paks.stakpak.dev/meta-img.jpg" }, { name: "twitter:image", content: "https://paks.stakpak.dev/meta-img.jpg" }, ], diff --git a/apps/web/src/routes/search.tsx b/apps/web/src/routes/search.tsx index eb81b6d..358ead6 100644 --- a/apps/web/src/routes/search.tsx +++ b/apps/web/src/routes/search.tsx @@ -24,7 +24,7 @@ export const Route = createFileRoute("/search")({ head: () => { // Static SEO for search page - dynamic content handled client-side const title = "Search Packages - Paks"; - const description = "Search and discover AI agent skills and packages. Find the perfect tools for Claude Code, Cursor, GitHub Copilot and other coding agents."; + const description = "Search and discover AI agent skills and packages. Find the perfect tools for Claude Code, Cursor, Codex, Copilot, Windsurf, Gemini CLI, and other coding agents."; return { meta: [ diff --git a/apps/web/src/routes/trending.tsx b/apps/web/src/routes/trending.tsx index 545a164..07819cc 100644 --- a/apps/web/src/routes/trending.tsx +++ b/apps/web/src/routes/trending.tsx @@ -20,13 +20,13 @@ export const Route = createFileRoute("/trending")({ }, head: () => { const title = "Trending Paks - Most Popular AI Agent Skills"; - const description = "Discover the most popular AI agent skills and packages. Browse trending tools for Claude Code, Cursor, GitHub Copilot and other coding agents."; + const description = "Discover the most popular AI agent skills and packages. Browse trending tools for Claude Code, Cursor, Codex, Copilot, Windsurf, Gemini CLI, and other coding agents."; return { meta: [ { title }, { name: "description", content: description }, - { name: "keywords", content: "trending, popular, AI agents, coding agents, Claude Code, Cursor, GitHub Copilot, packages, skills" }, + { name: "keywords", content: "trending, popular, AI agents, coding agents, Claude Code, Cursor, Codex, Copilot, Windsurf, Gemini CLI, Amp, packages, skills" }, { property: "og:title", content: title }, { property: "og:description", content: description }, { property: "og:type", content: "website" },