| name | create-agent-catalog-source |
| description | Use this skill when the user asks to "create an agent catalog", "generate agent catalog source", "build agent catalog from repo", "catalog my agents", or wants to scan a repository containing agent templates and/or agent metadata to produce a custom agent catalog source YAML for the agents catalog. Trigger phrases include: "create agent catalog source", "generate agent catalog", "catalog agents from repo", "agent catalog from repository", "custom agent catalog".
|
| allowed-tools | Bash Read Grep Glob Write AskUserQuestion WebFetch |
Create Agent Catalog Source
Scan a repository for agent templates and metadata, then generate a custom agent catalog source
compatible with the agents catalog service.
Usage:
/create-agent-catalog-source — scan the current working directory
/create-agent-catalog-source /path/to/repo — scan a local repository
/create-agent-catalog-source https://github.com/org/repo — clone and scan a remote repository
Step 1: Resolve Input
Parse the argument passed by the user to determine the target repository path.
If the argument looks like a GitHub URL (starts with https://github.com/ or git@github.com:):
- Generate a temporary directory path:
TMPDIR=$(mktemp -d /tmp/agent-catalog-XXXXXX)
- Clone the repository (shallow):
git clone --depth 1 <url> "$TMPDIR"
- If the clone fails, report the error and suggest the user clone manually and pass the local path instead. Stop.
- Use
$TMPDIR as the scan target. Remember to clean up at the end.
If the argument is a local path:
- Verify the path exists:
test -d <path>
- If it does not exist, report the error and stop.
- Use the provided path as the scan target.
If no argument is provided:
- Use the current working directory as the scan target.
After resolving the target path, detect git metadata for repositoryUrl generation:
cd <target-path>
git remote get-url origin 2>/dev/null
git branch --show-current 2>/dev/null
If the directory is not a git repo, warn the user that repositoryUrl fields will be empty but continue.
Convert the git remote URL to an HTTPS base URL:
git@github.com:org/repo.git → https://github.com/org/repo
https://github.com/org/repo.git → https://github.com/org/repo
- Store the remote URL and branch name for later use when building
repositoryUrl per agent.
Check the output directory:
test -d <original-cwd>/.agent-catalog
If .agent-catalog/ already exists, ask the user whether to overwrite or choose a different output
path using AskUserQuestion. Store the chosen path as <output-dir> (default: <original-cwd>/.agent-catalog).
All subsequent steps use <output-dir> when writing output files.
Step 2: Discover Agents — Convention Scan
Search the target repository for directories containing an agent.yaml file. This is the
preferred agent metadata format. Inform the user:
"Scanning for agent.yaml files (preferred convention)..."
Find all agent.yaml files:
find <target-path> -name "agent.yaml" \
-not -path "*/.git/*" \
-not -path "*/.github/*" \
-not -path "*/node_modules/*" \
-not -path "*/vendor/*" \
-not -path "*/build/*" \
-not -path "*/dist/*" \
-not -path "*/__pycache__/*"
For each agent.yaml found:
- Read the file content.
- Extract known fields:
name (string, required)
displayName (string, optional)
framework (string, required)
description (string, required)
labels (string array, optional, default [])
logo (string, optional, default "")
env (object with required and optional string arrays, optional)
- Note any additional top-level fields — these will become
customProperties.
- Check for a companion README.md in the same directory:
test -f <agent-dir>/README.md
- If README.md exists, read its full content for the
readme field.
- Compute the agent's relative path from the repo root (for
repositoryUrl).
Collect all discovered agents into a list. Track for each:
- Relative path within the repo
- Parsed
agent.yaml fields
- Whether README.md was found
- Any extra fields for customProperties
- Discovery source:
"convention"
Validation per agent:
- If
name is missing, derive it from the directory name (kebab-case).
- If
framework is missing, flag it as needing user input.
- If
description is missing, try to extract the first paragraph from README.md if available.
Always proceed to Step 3 (Heuristic Scan) regardless of how many agents the convention
scan found. The heuristic scan may discover additional agents in non-standard formats
(e.g., YAML files not named agent.yaml, or agents documented only in Markdown).
Step 3: Discover Agents — Heuristic Scan
This step always runs after the convention scan to find additional agents that may use
non-standard file names or only have Markdown documentation.
Inform the user:
"Scanning for additional agents via YAML heuristic and Markdown analysis..."
3a: YAML Heuristic Scan
Search for YAML files that look like they describe an agent:
find <target-path> -type f \( -name "*.yaml" -o -name "*.yml" \) \
-not -path "*/.git/*" \
-not -path "*/node_modules/*" \
-not -path "*/vendor/*" \
-not -name "docker-compose*" \
-not -name "*lock*" \
-not -name ".pre-commit*"
For each YAML file, read it and check if it is "agent-like":
- It must contain a
name field (string)
- It must contain at least one of:
framework, description, or env
- Exclude files that look like CI configs, Dockerfiles, Kubernetes manifests (check for
apiVersion, kind, services, jobs, steps top-level keys — these are not agents)
For qualifying files:
- Extract the same known fields as the convention scan
- Set discovery source to
"yaml-heuristic"
- Note the file path
3b: Markdown Scan
Search for Markdown files that describe agents:
find <target-path> -type f -name "*.md" \
-not -path "*/.git/*" \
-not -path "*/node_modules/*" \
-not -name "CHANGELOG*" \
-not -name "LICENSE*" \
-not -name "CONTRIBUTING*"
For each Markdown file, scan for agent-relevant signals:
-
Framework mentions — search (case-insensitive) for:
langgraph, crewai, autogen, llamaindex, llama-index, haystack,
semantic-kernel, langflow, langchain, openai-agents, google-adk, a2a
-
Agent keywords — search for:
agent, tool-calling, ReAct, chain-of-thought, orchestrator, agentic,
function calling, tool use
-
Environment variable documentation — look for sections with headers like
"Environment Variables", "Configuration", "Setup", ".env" that list variable names
-
Description extraction — extract the first paragraph after the top-level heading
as a candidate description
A Markdown file is a candidate if it has at least one framework mention AND at least one
agent keyword.
For qualifying Markdown files, extract as many of the 7 required catalog fields as possible:
name — infer from the filename or first heading (kebab-cased)
displayName — infer from the first heading (original casing)
framework — from the framework mention found
description — from the first paragraph after the top-level heading
labels — default [] (user can add during confirmation)
logo — default "" (user can provide during confirmation)
env — extract from environment variable documentation: look for bullet lists or
tables listing variable names. Parse required/optional indicators from the text
(e.g., "required", "optional", "Yes", "No"). If a var is listed without an indicator,
default to required: true
- Set
readme to the full file content
- Set discovery source to
"markdown-inference"
- Flag all fields as draft — the user must confirm/edit
Merge Results
Combine convention-scan, YAML-heuristic, and markdown-inference results into a single list:
- Deduplicate: If a heuristic/markdown candidate is from the same directory as a
convention-scan agent, skip the heuristic candidate (convention already captured it).
- Merge within heuristic: If a YAML-heuristic candidate and a markdown-inference
candidate are from the same directory, merge them — prefer YAML fields, supplement
with Markdown-extracted description/readme.
Proceed to Step 3c (Generate agent.yaml) with the merged list.
If zero agents total after all scans, tell the user:
"No agents found in this repository. To use this skill, organize your agents with an
agent.yaml file per agent directory. Here is the expected format:"
Then print an example agent.yaml:
name: my-agent
displayName: My Agent
framework: langgraph
description: A custom agent that does X
labels:
- tool-calling
- rag
env:
required:
- API_KEY
- MODEL_ENDPOINT
optional:
- DEBUG
And stop.
3c: Generate agent.yaml for Heuristic-Discovered Agents
Every agent in the catalog must have an agent.yaml file. Convention-scanned agents already
have one. For each agent discovered via yaml-heuristic or markdown-inference, generate
an agent.yaml matching the format expected by the agents catalog.
Important: The generated agent.yaml is NOT written to the agent's source directory —
it is assembled in memory and included only in the catalog output (as the templates entry
in <output-dir>/catalog.yaml). The agent's source directory remains unchanged.
For each heuristic/markdown-discovered agent:
-
Assemble the agent.yaml content from the extracted metadata using this exact format:
name: <kebab-case-name>
displayName: "<Human Readable Display Name>"
framework: <framework>
description: "<description>"
labels: [<labels-as-inline-array-or-empty>]
logo: "<logo-string-or-empty>"
env:
required:
- VAR_NAME_1
- VAR_NAME_2
optional:
- VAR_NAME_3
Field mapping:
name: from extracted metadata, kebab-cased
displayName: from extracted metadata, or title-cased from name if not available
framework: from extracted metadata
description: from extracted metadata
labels: from extracted metadata, default []
logo: from extracted metadata, default ""
env.required / env.optional: from extracted environment variable documentation.
If no env vars were found, use required: [] and optional: []
-
Display the generated agent.yaml to the user:
"Generated agent.yaml for agent <name> (discovered via <source>):"
<full assembled content>
-
Use AskUserQuestion to ask the user to confirm:
Proceed to Step 4 with only the confirmed agents.
Step 4: User Confirmation
Present all discovered agents to the user in a summary table. Format:
Discovered N agents:
# Name Framework Source Path
1. langgraph-react-agent langgraph convention agents/react_agent/
2. crewai-rag-agent crewai convention agents/rag/
3. my-custom-agent autogen yaml-heuristic tools/agent.yaml
4. search-bot langgraph markdown bots/search/README.md
For markdown-inference sourced agents, also show the draft metadata:
Agent #4 (search-bot) — inferred from Markdown, please review:
name: search-bot
framework: langgraph (inferred from README mention)
description: "A search bot that uses LangGraph to orchestrate web search tools"
[Edit any field? y/N]
Use AskUserQuestion to ask:
-
"Include all discovered agents, or select which to include?"
- Options: "Include all (Recommended)", "Let me select", "Add agents manually"
- If "Let me select": ask which numbers to include (comma-separated)
- If "Add agents manually": ask for the path to an agent directory, read its files,
and add it to the list. Repeat until the user is done.
-
Required fields check. Every agent in the catalog MUST have all 7 of these fields
(even if some use default/empty values):
name (string, non-empty)
displayName (string, non-empty)
description (string, non-empty)
framework (string, non-empty)
labels (string array, may be [])
logo (string, may be "")
env (array of {name, required}, may be [])
For any agent missing a non-empty required field (name, displayName, description,
or framework), ask the user to provide the value. For labels, logo, and env,
use defaults ([], "", []) if not available, but show the user what defaults were
applied so they can override.
-
For markdown-inference agents, present each one's draft metadata for ALL 7 required
fields and ask if the user wants to edit any. Let them type corrections inline.
-
"What should the catalog source be named?"
- Default: the repository name (derived from git remote or directory name)
- The user can accept or type a custom name
-
"Would you like to add labels to this catalog source? (e.g., 'Custom', 'Internal')"
- Default: no labels (empty array)
- If yes, ask for comma-separated label strings
Store the confirmed agents list, source name, and source labels for output generation.
Step 5: Generate Catalog YAML
Create the output directory:
mkdir -p <output-dir>
Build the catalog YAML content. The output must match this exact schema (compatible with
the model-registry yamlAgentCatalog consumer):
source: <confirmed-source-name>
agents:
- name: <kebab-case-name>
displayName: <display-name>
description: <description>
readme: |
<full README.md content, or empty string>
repositoryUrl: <git-remote-https-url>/tree/<branch>/<agent-relative-path>
framework: <framework>
labels:
- <label1>
- <label2>
logo: "<logo-string-or-empty>"
env:
- name: <var-name>
required: true
- name: <var-name>
required: false
templates:
- name: agent.yaml
content: '<full-agent-yaml-as-json-string>'
customProperties:
<extra-field-name>:
metadataType: MetadataStringValue
string_value: "<value>"
Field assembly per agent:
| Field | How to build |
|---|
name | From confirmed metadata. Must be kebab-case and unique within the catalog. |
displayName | From confirmed metadata. If empty, title-case the name (replace hyphens with spaces, capitalize each word). |
description | From confirmed metadata. |
readme | Full content of the agent's README.md. Use YAML literal block scalar (` |
repositoryUrl | <git-remote-url>/tree/<branch>/<agent-relative-path>. Empty string if no git remote was detected. |
framework | From confirmed metadata. |
labels | From agent.yaml labels field. Default []. Always include this field. |
logo | From agent.yaml logo field. Default "". Always include this field. |
env | Transform from {required: [...], optional: [...]} to flat list: each required var gets {name: X, required: true}, each optional var gets {name: X, required: false}. For markdown-inferred agents, use env vars extracted from the README. Default []. Always include this field. |
templates | The templates content must always reflect the final confirmed metadata — including any edits the user made in Step 4. Convert the final metadata to a compact JSON string using ./bin/yq. The result becomes a single template entry: {name: "agent.yaml", content: "<json>"}. For all agents, assemble a YAML representation of the final confirmed fields, write it to a temporary file (TMPFILE=$(mktemp /tmp/agent-yaml-XXXXXX.yaml)), run ./bin/yq "$TMPFILE" -o json -I 0, capture stdout, then clean up (rm "$TMPFILE"). For convention-discovered agents where the user edited fields in Step 4: the on-disk agent.yaml is now out of sync — ask the user whether they want to update the original file in the source repository to match. |
IMPORTANT: The 7 required fields (name, displayName, description, framework,
labels, logo, env) must be present on EVERY agent entry in the catalog, regardless
of discovery source. Use empty defaults ([], "") where no value was found.
Write the catalog file to <output-dir>/catalog.yaml.
Ensure the file ends with a newline character.
Validate: Read back the written file and confirm:
- It is valid YAML
- It has a
source: key
- It has an
agents: array with the expected number of entries
- Each agent has ALL 7 required fields present:
name, displayName, description,
framework, labels, logo, env
Report to the user:
"Catalog written to <output-dir>/catalog.yaml with N agents."
Step 6: Generate Deployment Artifacts
6a: Sources Config Snippet
Derive the source ID from the confirmed source name:
- Lowercase the name
- Replace spaces with underscores
- Remove any characters that are not alphanumeric or underscores
Write <output-dir>/sources-snippet.yaml with the following content:
- name: "<confirmed-source-name>"
id: <derived-source-id>
type: yaml
enabled: true
properties:
yamlCatalogPath: /data/custom-agents/catalog.yaml
labels:
- <label1>
The yamlCatalogPath uses a placeholder — the user will update this to match the actual
mount path in their deployment.
If the user chose no labels, use: labels: []
6b: Deploy Script
Write <output-dir>/deploy.sh with the following content:
#!/bin/bash
set -euo pipefail
NAMESPACE="${NAMESPACE:-kubeflow}"
CONFIGMAP_NAME="<derived-source-id>-catalog"
echo "Creating ConfigMap '$CONFIGMAP_NAME' in namespace '$NAMESPACE'..."
kubectl create configmap "$CONFIGMAP_NAME" \
--from-file=catalog.yaml=<output-dir>/catalog.yaml \
-n "$NAMESPACE" --dry-run=client -o yaml | kubectl apply -f -
echo ""
echo "ConfigMap created successfully."
echo ""
echo "Next steps:"
echo " 1. Mount the ConfigMap into the model-registry catalog deployment."
echo " 2. Edit the existing catalog sources ConfigMap to add this agent source:"
echo " kubectl edit configmap <sources-configmap-name> -n $NAMESPACE"
echo ""
echo " Add the following under the 'agent_catalogs' section:"
echo ""
<output-dir>/sources-snippet.yaml
Make the deploy script executable:
chmod +x <output-dir>/deploy.sh
Replace <derived-source-id> in the script with the actual derived source ID value
before writing.
6c: Cleanup
If a temporary clone was created in Step 1:
rm -rf "$TMPDIR"
6d: Final Summary
Print to the user:
Agent catalog source generated successfully!
Output directory: <output-dir>/
catalog.yaml — Agent catalog data (N agents)
sources-snippet.yaml — Sources config snippet (paste into existing sources ConfigMap)
deploy.sh — Deployment script for Kubernetes
To deploy:
1. Run: <output-dir>/deploy.sh
2. Edit the existing sources ConfigMap to add the snippet from sources-snippet.yaml
3. The catalog service will hot-reload automatically — no restart needed.
Source name: <confirmed-source-name>
Source ID: <derived-source-id>
Note: Source IDs must be unique across all catalog types (models, MCP servers, agents)
in your model-registry deployment. If you have an existing source with ID
'<derived-source-id>', choose a different name.