From 5de746fb6544ca8f2c7dfa4e6dcdc87cef47eb88 Mon Sep 17 00:00:00 2001 From: Rachel Yang Date: Tue, 23 Jun 2026 09:56:22 -0400 Subject: [PATCH 1/3] feat(skills): add catalog and session commands backed by Datadog API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds three new subcommands under `pup skills` that talk to the agentic-onboarding-api: - `pup skills catalog list [--tags ...]` — lists skills from the remote registry (GET /api/v2/skills), optionally filtered by tag (e.g. --tags apm) - `pup skills catalog get ` — fetches a single skill's full content by name (GET /api/v2/skills/) - `pup skills session` — records an onboarding session checkpoint or terminal event (POST /api/v2/onboarding/sessions) using the JSON:API envelope the service expects These complement the existing local `skills list/install/path` commands (which manage bundled skill files on disk) by giving Claude and other agents a way to discover domain skills from the registry and record session telemetry. Co-Authored-By: Claude Sonnet 4.6 --- src/commands/skills.rs | 46 +++++++++++++++++++++++++++ src/main.rs | 71 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+) diff --git a/src/commands/skills.rs b/src/commands/skills.rs index b01a820f..d35a0fc5 100644 --- a/src/commands/skills.rs +++ b/src/commands/skills.rs @@ -1,5 +1,8 @@ use anyhow::{bail, Result}; +use crate::client; +use crate::config::Config; +use crate::formatter; use crate::skills; /// Resolve the platform list from CLI input, validating each entry. @@ -300,6 +303,49 @@ pub fn path(platform: Option, project: bool) -> Result<()> { Ok(()) } +pub async fn catalog_list(cfg: &Config, tags: Vec) -> Result<()> { + let query = tags + .iter() + .map(|t| format!("tags={t}")) + .collect::>() + .join("&"); + let path = if query.is_empty() { + "/api/v2/skills".to_string() + } else { + format!("/api/v2/skills?{query}") + }; + let data = client::raw_get(cfg, &path, &[]).await?; + formatter::output(cfg, &data) +} + +pub async fn catalog_get(cfg: &Config, name: String) -> Result<()> { + let path = format!("/api/v2/skills/{name}"); + let data = client::raw_get(cfg, &path, &[]).await?; + formatter::output(cfg, &data) +} + +pub async fn session_record( + cfg: &Config, + session_id: String, + skill_ids: Vec, + summary: String, + status: String, +) -> Result<()> { + let body = serde_json::json!({ + "data": { + "type": "onboarding_session", + "id": session_id, + "attributes": { + "skill_ids": skill_ids, + "summary": summary, + "status": status, + } + } + }); + let data = client::raw_post(cfg, "/api/v2/onboarding/sessions", body).await?; + formatter::output(cfg, &data) +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/main.rs b/src/main.rs index 9fd1a9e1..f5f10347 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9686,6 +9686,56 @@ enum SkillsActions { #[arg(long)] project: bool, }, + /// Browse the remote Datadog skill catalog + /// + /// COMMANDS: + /// list List skills from the remote catalog (optionally filtered by tag) + /// get Fetch a single skill's full content by name + /// + /// EXAMPLES: + /// pup skills catalog list + /// pup skills catalog list --tags apm + /// pup skills catalog list --tags apm --tags observability + /// pup skills catalog get kubernetes-agent-install + Catalog { + #[command(subcommand)] + action: SkillsCatalogActions, + }, + /// Record an onboarding session checkpoint or terminal event + /// + /// EXAMPLES: + /// pup skills session --session-id --skill-ids kubernetes-agent-install --summary "installed agent" --status completed + /// pup skills session --session-id --skill-ids apm-setup --skill-ids missing-traces --summary "in progress" --status in_progress + Session { + /// Session UUID + #[arg(long)] + session_id: String, + /// Skill IDs used in this session (repeatable) + #[arg(long = "skill-ids", name = "skill-ids")] + skill_ids: Vec, + /// Human-readable summary of what happened + #[arg(long)] + summary: String, + /// Session status: in_progress, completed, failed, abandoned + #[arg(long)] + status: String, + }, +} + +#[cfg(not(target_arch = "wasm32"))] +#[derive(Subcommand)] +enum SkillsCatalogActions { + /// List skills from the remote Datadog skill catalog + List { + /// Filter by tag (repeatable, e.g. --tags apm --tags observability) + #[arg(long)] + tags: Vec, + }, + /// Fetch a single skill's full content by name + Get { + /// Skill name (e.g. kubernetes-agent-install) + name: String, + }, } // ---- Product Analytics ---- @@ -15113,6 +15163,27 @@ async fn main_inner() -> anyhow::Result<()> { SkillsActions::Path { platform, project } => { commands::skills::path(platform.map(|p| p.as_canonical().to_string()), project)? } + SkillsActions::Catalog { action } => { + cfg.validate_auth()?; + match action { + SkillsCatalogActions::List { tags } => { + commands::skills::catalog_list(&cfg, tags).await?; + } + SkillsCatalogActions::Get { name } => { + commands::skills::catalog_get(&cfg, name).await?; + } + } + } + SkillsActions::Session { + session_id, + skill_ids, + summary, + status, + } => { + cfg.validate_auth()?; + commands::skills::session_record(&cfg, session_id, skill_ids, summary, status) + .await?; + } }, // --- Product Analytics --- Commands::ProductAnalytics { action } => { From 4f8e7830b0f141fe64486815f2267a7c16774207 Mon Sep 17 00:00:00 2001 From: Rachel Yang Date: Tue, 23 Jun 2026 11:18:38 -0400 Subject: [PATCH 2/3] feat(skills): add catalog publish and update commands Adds two more subcommands under `pup skills catalog`: - `publish --name [--description ] [--tags ...]` Creates a new skill in the remote catalog via POST /api/v2/skills - `update --name [--description ] [--tags ...]` Updates an existing skill via PUT /api/v2/skills/{name} Both read the skill markdown from a local file and upload it as the skill content. This makes it easy to publish multi-file skill trees like dd-apm to the registry with a simple shell loop. Co-Authored-By: Claude Sonnet 4.6 --- src/commands/skills.rs | 53 ++++++++++++++++++++++++++++++++++++++++ src/main.rs | 55 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/src/commands/skills.rs b/src/commands/skills.rs index d35a0fc5..b3d7cd5c 100644 --- a/src/commands/skills.rs +++ b/src/commands/skills.rs @@ -4,6 +4,7 @@ use crate::client; use crate::config::Config; use crate::formatter; use crate::skills; +use std::path::Path; /// Resolve the platform list from CLI input, validating each entry. /// @@ -324,6 +325,58 @@ pub async fn catalog_get(cfg: &Config, name: String) -> Result<()> { formatter::output(cfg, &data) } +pub async fn catalog_publish( + cfg: &Config, + file: String, + name: String, + description: Option, + tags: Vec, +) -> Result<()> { + let content = std::fs::read_to_string(Path::new(&file)) + .map_err(|e| anyhow::anyhow!("failed to read {file}: {e}"))?; + let desc = description.unwrap_or_default(); + let body = serde_json::json!({ + "data": { + "type": "registry_skill", + "attributes": { + "name": name, + "description": desc, + "content": content, + "tags": tags, + } + } + }); + let data = client::raw_post(cfg, "/api/v2/skills", body).await?; + formatter::output(cfg, &data) +} + +pub async fn catalog_update( + cfg: &Config, + file: String, + name: String, + description: Option, + tags: Vec, +) -> Result<()> { + let content = std::fs::read_to_string(Path::new(&file)) + .map_err(|e| anyhow::anyhow!("failed to read {file}: {e}"))?; + let mut attrs = serde_json::json!({ + "content": content, + "tags": tags, + }); + if let Some(desc) = description { + attrs["description"] = serde_json::json!(desc); + } + let body = serde_json::json!({ + "data": { + "type": "registry_skill", + "attributes": attrs, + } + }); + let path = format!("/api/v2/skills/{}", name.replace('/', "%2F")); + let data = client::raw_put(cfg, &path, body).await?; + formatter::output(cfg, &data) +} + pub async fn session_record( cfg: &Config, session_id: String, diff --git a/src/main.rs b/src/main.rs index f5f10347..fef79f58 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9733,9 +9733,44 @@ enum SkillsCatalogActions { }, /// Fetch a single skill's full content by name Get { - /// Skill name (e.g. kubernetes-agent-install) + /// Skill name (e.g. apm-k8s-ssi-agent-install) name: String, }, + /// Publish a skill file to the remote catalog (creates a new skill) + /// + /// EXAMPLES: + /// pup skills catalog publish SKILL.md --name apm-k8s-ssi-agent-install --tags apm + /// pup skills catalog publish SKILL.md --name dd-apm --description "APM router" --tags apm --tags onboarding + Publish { + /// Path to the skill markdown file to publish + file: String, + /// Name to register the skill under in the catalog + #[arg(long)] + name: String, + /// Human-readable description of the skill + #[arg(long)] + description: Option, + /// Tags for filtering (repeatable) + #[arg(long)] + tags: Vec, + }, + /// Update an existing skill in the remote catalog + /// + /// EXAMPLES: + /// pup skills catalog update SKILL.md --name apm-k8s-ssi-agent-install + Update { + /// Path to the updated skill markdown file + file: String, + /// Name of the skill to update + #[arg(long)] + name: String, + /// Updated human-readable description + #[arg(long)] + description: Option, + /// Updated tags (replaces existing tags) + #[arg(long)] + tags: Vec, + }, } // ---- Product Analytics ---- @@ -15172,6 +15207,24 @@ async fn main_inner() -> anyhow::Result<()> { SkillsCatalogActions::Get { name } => { commands::skills::catalog_get(&cfg, name).await?; } + SkillsCatalogActions::Publish { + file, + name, + description, + tags, + } => { + commands::skills::catalog_publish(&cfg, file, name, description, tags) + .await?; + } + SkillsCatalogActions::Update { + file, + name, + description, + tags, + } => { + commands::skills::catalog_update(&cfg, file, name, description, tags) + .await?; + } } } SkillsActions::Session { From 9b639a3b66a2c174fb47cfab335f8c37957d3286 Mon Sep 17 00:00:00 2001 From: Rachel Yang Date: Tue, 23 Jun 2026 14:07:28 -0400 Subject: [PATCH 3/3] Add dd-onboarding bootstrap skill Thin skill that tells Claude to fetch the orchestrator from the agentic-onboarding-api before any Datadog setup or install task. Closes the delivery gap: customers running `pup skills install` now get the entry point for the live skill chain in dd-source. Co-Authored-By: Claude Sonnet 4.6 --- SKILL.md | 1 + skills/dd-onboarding/SKILL.md | 38 +++++++++++++++++++++++++++++++++++ src/skills.rs | 8 ++++++++ 3 files changed, 47 insertions(+) create mode 100644 skills/dd-onboarding/SKILL.md diff --git a/SKILL.md b/SKILL.md index ba224836..d8b246df 100644 --- a/SKILL.md +++ b/SKILL.md @@ -42,6 +42,7 @@ pup skills list | Skill | Description | |-------|-------------| +| **dd-onboarding** | Agent install, APM, logs, integrations — AI-driven setup via live skill API | | **dd-pup** | Primary CLI - all pup commands, auth, site config | | **dd-monitors** | Create, manage, mute monitors and alerts | | **dd-logs** | Search logs, pipelines, archives | diff --git a/skills/dd-onboarding/SKILL.md b/skills/dd-onboarding/SKILL.md new file mode 100644 index 00000000..4ca834a6 --- /dev/null +++ b/skills/dd-onboarding/SKILL.md @@ -0,0 +1,38 @@ +--- +name: dd-onboarding +description: Datadog Agentic Onboarding. Guides installation and setup of the Datadog Agent, APM, logs, integrations, and cloud environments via an AI-driven workflow. +metadata: + version: "1.0.0" + author: datadog-labs + repository: https://github.com/DataDog/dd-source + tags: datadog,onboarding,setup,install,apm,agent,kubernetes,docker,lambda,ecs + alwaysApply: "false" +--- + +# Datadog Agentic Onboarding + +For any Datadog setup, installation, configuration, or troubleshooting task, fetch the onboarding +orchestrator first and follow its instructions: + +```bash +curl -s -X GET "https://api.${DD_SITE:-datadoghq.com}/api/v2/onboarding/skills/orchestrator" \ + -H "DD-API-KEY: ${DD_API_KEY}" \ + -H "DD-APPLICATION-KEY: ${DD_APP_KEY}" \ + | jq -r '.data.attributes.content' +``` + +The orchestrator will guide credential validation, environment discovery, skill selection, and +step-by-step setup. Follow it exactly — do not skip steps or proceed without the orchestrator +content loaded. + +## Prerequisites + +`DD_API_KEY`, `DD_APP_KEY`, and `DD_SITE` must be set before fetching the orchestrator. If any are +missing, ask the user to provide them. Default `DD_SITE` to `datadoghq.com` if unset. + +## When this skill applies + +- Installing the Datadog Agent on any platform (Kubernetes, Linux, Docker, ECS, Lambda, Azure, GCP) +- Setting up APM, logs, RUM, or cloud integrations +- Troubleshooting an existing Datadog Agent or APM installation +- Any question that starts with "how do I monitor…", "set up Datadog on…", or "install the agent on…" diff --git a/src/skills.rs b/src/skills.rs index a3049e7a..9dd70090 100644 --- a/src/skills.rs +++ b/src/skills.rs @@ -36,6 +36,14 @@ static DD_PUP_PI_FILES: &[(&str, &str)] = &[ pub static SKILLS: &[SkillEntry] = &[ // --- Skills (from agent-skills + claude-plugin) --- + SkillEntry { + name: "dd-onboarding", + description: "Datadog Agentic Onboarding. Guides installation and setup of the Datadog Agent, APM, logs, integrations, and cloud environments via an AI-driven workflow.", + entry_type: "skill", + content: include_str!("../skills/dd-onboarding/SKILL.md"), + platform: "", + files: &[], + }, SkillEntry { name: "dd-pup", description: "Datadog CLI (pup). OAuth2 auth with token refresh.",