plugins/aem/project-management/skills/admin/SKILL.md
Generate comprehensive admin documentation for AEM Edge Delivery Services project handover. Creates admin guide covering Config Service setup, permissions, access control, Admin API operations, cache management, and code sync. Use for "admin guide", "admin documentation", "admin handover".
npx skillsauth add adobe/skills adminInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Generate comprehensive documentation for administrators taking over an AEM Edge Delivery Services project. Produces a complete admin guide with Config Service setup, permissions, Admin API operations, and troubleshooting.
Skip this step if allGuides flag is set (orchestrator already validated and navigated).
CRITICAL: If NOT skipped, you MUST execute the cd command. Do NOT use absolute paths — actually change directory.
ALL_GUIDES=$(cat .claude-plugin/project-config.json 2>/dev/null | node -e "
const d = require('fs').readFileSync(0,'utf8');
try { console.log(JSON.parse(d).allGuides ? 'true' : ''); } catch(e) { console.log(''); }
")
if [ -z "$ALL_GUIDES" ]; then
# Navigate to git project root (works from any subdirectory)
cd "$(git rev-parse --show-toplevel)"
# Verify it's an Edge Delivery Services project
ls scripts/aem.js
fi
IMPORTANT:
cd command above using the Bash toolproject-root/project-guides/If NOT skipped AND scripts/aem.js does NOT exist, respond:
"This skill is designed for AEM Edge Delivery Services projects. The current directory does not appear to be an Edge Delivery Services project (
scripts/aem.jsnot found).Please navigate to an Edge Delivery Services project and try again."
STOP if check fails. Otherwise proceed — you are now at project root.
- [ ] Phase 1: Gather Project Context
- [ ] Phase 2: Generate Admin Guide Content
- [ ] Phase 3: Customize for Project
- [ ] Phase 4: Generate Professional PDF
YOU MUST SAVE THE FILE TO THIS EXACT PATH:
project-guides/ADMIN-GUIDE.md
BEFORE WRITING ANY FILE:
mkdir -p project-guidesproject-guides/ADMIN-GUIDE.mdWHY THIS MATTERS: Files must be in project-guides/ for proper organization and PDF conversion.
❌ WRONG: ADMIN-GUIDE.md (root)
❌ WRONG: docs/ADMIN-GUIDE.md
❌ WRONG: /workspace/ADMIN-GUIDE.md
✅ CORRECT: project-guides/ADMIN-GUIDE.md
MANDATORY OUTPUT: project-guides/ADMIN-GUIDE.pdf
STRICTLY FORBIDDEN:
fstab.yaml — it does NOT exist in most projects and does NOT show all sites.plain.html filesconvert_markdown_to_html tool — this converts the FULL guide to HTML with raw frontmatter visible, which is NOT what we wantproject-guides/The HTML output must be a SHORT summary page (created with Write tool) containing:
REQUIRED WORKFLOW:
mkdir -p project-guides to ensure directory existsproject-guides/ADMIN-GUIDE.md (EXACT PATH - no exceptions)project-guides/ADMIN-GUIDE.pdfWhenever this skill runs — whether the user triggered it directly (e.g. "generate admin guide") or via the handover flow — you must have the Config Service organization name before doing anything else. Do not skip this phase.
# Check if org name is already saved
cat .claude-plugin/project-config.json 2>/dev/null | node -e "
const d = require('fs').readFileSync(0,'utf8');
try { const o = JSON.parse(d).org; if(o) console.log('org: ' + o); } catch(e) {}
"
If no org name is saved, you MUST pause and ask the user directly:
"What is your Config Service organization name? This is the
{org}part of your Edge Delivery Services URLs (e.g.,https://main--site--{org}.aem.page). The org name may differ from your GitHub organization."
IMPORTANT RULES:
AskUserQuestion with predefined options — ask as a plain text questionOnce you have the org name (either from saved config or user input), save it for future use:
# Create config directory if needed
mkdir -p .claude-plugin
# Ensure .claude-plugin is in .gitignore (contains project config)
grep -qxF '.claude-plugin/' .gitignore 2>/dev/null || echo '.claude-plugin/' >> .gitignore
# Save org name to config file (create or update)
if [ -f .claude-plugin/project-config.json ]; then
cat .claude-plugin/project-config.json | sed 's/"org"[[:space:]]*:[[:space:]]*"[^"]*"/"org": "{ORG_NAME}"/' > /tmp/project-config.json && mv /tmp/project-config.json .claude-plugin/project-config.json
else
echo '{"org": "{ORG_NAME}"}' > .claude-plugin/project-config.json
fi
Replace {ORG_NAME} with the actual organization name provided by the user.
After getting the organization name, authenticate to obtain an IMS token.
IMS_TOKEN=$(node -e "
const fs = require('fs');
try {
const t = JSON.parse(fs.readFileSync(process.env.HOME + '/.aem/ims-token.json', 'utf8'));
if (t.imsToken && t.imsTokenExpiry > Math.floor(Date.now()/1000) + 60) {
process.stdout.write(t.imsToken);
}
} catch (e) {}
")
IMS_EXPIRY=$(node -e "
const fs = require('fs');
try {
const t = JSON.parse(fs.readFileSync(process.env.HOME + '/.aem/ims-token.json', 'utf8'));
process.stdout.write(String(t.imsTokenExpiry || 0));
} catch (e) { process.stdout.write('0'); }
")
NOW=$(date +%s)
if [ -n "$IMS_TOKEN" ] && [ "$IMS_EXPIRY" -gt "$((NOW + 60))" ]; then
echo "Token valid (expires in $((IMS_EXPIRY - NOW)) seconds)"
else
echo "Token missing or expired. Need to authenticate."
fi
If no valid token exists, invoke the auth skill:
Skill({ skill: "project-management:auth" })
This will:
~/.aem/ims-token.json (user-level, shared across projects)After auth skill completes, verify token works:
IMS_TOKEN=$(node -e "
const fs = require('fs');
try {
const t = JSON.parse(fs.readFileSync(process.env.HOME + '/.aem/ims-token.json', 'utf8'));
process.stdout.write(t.imsToken || '');
} catch (e) {}
")
ORG=$(cat .claude-plugin/project-config.json | node -e "
const d = require('fs').readFileSync(0,'utf8');
console.log(JSON.parse(d).org || '');
")
# Test with authenticated endpoint
curl -s -w "%{http_code}" -o /dev/null -H "Authorization: Bearer ${IMS_TOKEN}" \
"https://admin.hlx.page/config/${ORG}/sites.json"
If returns 200, authentication is successful. If 401, re-run auth skill.
⚠️ MANDATORY DATA SOURCE — NO ALTERNATIVES ALLOWED
You MUST call the Config Service API. This is the ONLY acceptable source for site information.
❌ PROHIBITED APPROACHES (will produce incorrect results):
fstab.yaml — does NOT show all sites in repoless setupsREADME.md — may be outdated or incomplete✅ REQUIRED: Execute and save response:
ORG=$(cat .claude-plugin/project-config.json | node -e "
const d = require('fs').readFileSync(0,'utf8');
console.log(JSON.parse(d).org || '');
")
# Save response to file - Step 1.2 depends on this file
curl -s -H "Accept: application/json" "https://admin.hlx.page/config/${ORG}/sites.json" > .claude-plugin/sites-config.json
📁 REQUIRED ARTIFACT: .claude-plugin/sites-config.json
API Reference: https://www.aem.live/docs/admin.html#tag/siteConfig/operation/getConfigSites
The response is a JSON object with a sites array (each entry has a name field). Extract site names and construct per-site URLs:
https://main--{site-name}--{org}.aem.page/https://main--{site-name}--{org}.aem.live/Multiple sites = repoless setup. Single site = standard setup.
Then fetch individual site config for code and content details:
IMS_TOKEN=$(node -e "
const fs = require('fs');
try {
const t = JSON.parse(fs.readFileSync(process.env.HOME + '/.aem/ims-token.json', 'utf8'));
process.stdout.write(t.imsToken || '');
} catch (e) {}
")
curl -s -H "Authorization: Bearer ${IMS_TOKEN}" "https://admin.hlx.page/config/${ORG}/sites/{site-name}.json"
Example response:
{
"code": {
"owner": "github-owner",
"repo": "repo-name",
"source": { "type": "github", "url": "https://github.com/owner/repo" }
},
"content": {
"source": {
"url": "https://content.da.live/org-name/site-name/",
"type": "markup"
}
}
}
Extract from response:
code.owner / code.repo — GitHub repositorycontent.source.url — Content mountpath (e.g., https://content.da.live/org/site/)content.source.type — Content source type (markup, onedrive, google)⚠️ Do NOT use fstab.yaml — use Config Service API instead.
Read from artifact created in 1.1:
cat .claude-plugin/sites-config.json
Parse the JSON to extract site names, then fetch each site's config for code repo:
curl -s -H "Accept: application/json" "https://admin.hlx.page/config/${ORG}/sites/{site-name}.json"
Use these API responses to build context: site names, code repo (owner/repo), preview/live URLs. If site config includes a content source (e.g. content.da.live path), record it for the Sites table.
Admin Context:
├── Organization: {org from saved config}
├── Site(s): {site1}, {site2}, ... (from Config Service API response)
├── Setup Type: {repoless | standard} (from Config Service API)
├── Code Repo: {code.owner}/{code.repo} (from Config Service — GitHub or Cloud Manager)
├── Preview: https://main--{site}--{org}.aem.page/
├── Live: https://main--{site}--{org}.aem.live/
├── Login URL: https://admin.hlx.page/login/{org}/{site}
└── Config Service: https://admin.hlx.page/config/{org}/
project-guides/ADMIN-GUIDE.mdGenerate the admin handover document with the following structure:
# [Project Name] - Admin Guide
Complete administration guide for managing this Edge Delivery Services project.
## Quick Reference
### URLs
| Purpose | URL |
|---------|-----|
| **Login** | https://admin.hlx.page/login/{org}/{site} |
| **Config Service** | https://admin.hlx.page/config/{org}/ |
| **Preview** | https://main--{site}--{org}.aem.page/ |
| **Live** | https://main--{site}--{org}.aem.live/ |
### Sites (if multi-site/repoless)
| Site | Content Source (DA) | Preview | Live |
|------|---------------------|---------|------|
| {site1} | [from site config if present] | https://main--{site1}--{org}.aem.page/ | https://main--{site1}--{org}.aem.live/ |
| {site2} | [from site config if present] | https://main--{site2}--{org}.aem.page/ | https://main--{site2}--{org}.aem.live/ |
## Authentication
### Login
1. Open: https://admin.hlx.page/login/{org}/{site}
2. Sign in with your Adobe ID
3. Copy auth token for API operations
### Logout
```bash
curl -X POST \
-H "Authorization: Bearer $IMS_TOKEN" \
"https://admin.hlx.page/logout/{org}/{site}/main"
curl -H "Authorization: Bearer $IMS_TOKEN" \
"https://admin.hlx.page/config/{org}/sites/{site}/access.json"
| Role | Command |
|------|---------|
| Admin | POST /config/{org}/sites/{site}/access/admin.json with {"users": ["email"]} |
| Author | POST /config/{org}/sites/{site}/access/author.json with {"users": ["email"]} |
curl -X DELETE \
-H "Authorization: Bearer $IMS_TOKEN" \
"https://admin.hlx.page/config/{org}/sites/{site}/access/admin/{email}.json"
| Operation | Endpoint |
|-----------|----------|
| Single page | POST /preview/{org}/{site}/main/{path} |
| Bulk preview | POST /preview/{org}/{site}/main/* |
| Operation | Endpoint |
|-----------|----------|
| Single page | POST /live/{org}/{site}/main/{path} |
| Bulk publish | POST /live/{org}/{site}/main/* |
| Unpublish | DELETE /live/{org}/{site}/main/{path} |
| Operation | Endpoint |
|-----------|----------|
| Purge path | POST /cache/{org}/{site}/main/{path} |
| Purge all | POST /cache/{org}/{site}/main/* |
curl -X POST \
-H "Authorization: Bearer $IMS_TOKEN" \
"https://admin.hlx.page/code/{owner}/{repo}/main"
| Task | Steps |
|------|-------|
| Add new admin | POST to /config/{org}/sites/{site}/access/admin.json |
| Republish site | POST to /preview/{org}/{site}/main/* then /live/{org}/{site}/main/* |
| Clear all cache | POST to /cache/{org}/{site}/main/* |
| Deploy code changes | POST to /code/{owner}/{repo}/main |
| Issue | Solution |
|-------|----------|
| 401 Unauthorized | Token expired - login again |
| 403 Forbidden | Insufficient permissions - check role |
| 404 Not Found | Check org/site/path spelling |
| 429 Rate Limited | Wait and retry |
| Cache not clearing | Try with forceUpdate: true |
| Code not syncing | Manual sync: POST to /code/{owner}/{repo}/main |
| Resource | URL | |----------|-----| | Admin API Docs | https://www.aem.live/docs/admin.html | | Config Service | https://www.aem.live/docs/config-service-setup |
## Phase 3: Customize for Project
### 3.1 Fill in Project-Specific Values
Replace all placeholders:
- `{org}` → actual organization from Config Service API
- `{site}` → actual site name(s)
- `{owner}` → code owner from Config Service (GitHub org or Cloud Manager program)
- `{repo}` → code repo from Config Service
### 3.2 Add Multi-Site Details (if repoless)
If project has multiple sites, add a section listing all sites with their:
- Site name
- Preview URL
- Live URL
- Content source
### 3.3 Document Project-Specific Configurations
Check for and document:
- Custom headers (`/config/{org}/sites/{site}/headers.json`)
- CDN configuration
- Any project-specific admin procedures
---
## Phase 4: Convert to Professional PDF
### 4.1 Generate PDF (MANDATORY)
**THIS STEP IS NOT OPTIONAL. YOU MUST GENERATE THE PDF NOW.**
1. Save markdown to: `project-guides/ADMIN-GUIDE.md`
- File MUST start with YAML frontmatter:
```yaml
---
title: "[Project Name] - Admin Guide"
date: "[Full Date - e.g., February 17, 2026]"
---
```
- **Date format**: Always use full date with day, month, and year (e.g., "February 17, 2026"), NOT just month and year
2. **IMMEDIATELY after saving the markdown**, invoke the PDF conversion skill:
Skill({ skill: "project-management:whitepaper", args: "project-guides/ADMIN-GUIDE.md project-guides/ADMIN-GUIDE.pdf" })
3. Wait for PDF generation to complete (whitepaper skill auto-cleans source files)
**DO NOT:**
- Skip the PDF conversion step
- Tell user "PDF will be generated later" — generate it NOW
### 4.2 Deliver to User
After PDF is generated, inform the user:
"✓ Admin guide complete: project-guides/ADMIN-GUIDE.pdf"
---
## Output
**FINAL OUTPUT:** `project-guides/ADMIN-GUIDE.pdf`
All source files (.md, .html, .plain.html) are deleted after PDF generation. Only the PDF remains.
**Location:** `project-guides/` folder
---
## Success Criteria
**Data Source Validation (CRITICAL):**
- [ ] Config Service API was called (`https://admin.hlx.page/config/{ORG}/sites.json`)
- [ ] Site list came from API response, NOT from fstab.yaml or codebase analysis
- [ ] Code repo info came from site config API, NOT from git remote
**Content Validation:**
- [ ] All org/site values filled from Config Service API
- [ ] Login URL correct
- [ ] All API endpoints have correct org/site
- [ ] Multi-site documented (if applicable)
- [ ] Common tasks listed with correct paths
**Output Validation:**
- [ ] PDF generated successfully
- [ ] All source files cleaned up (only PDF remains)
---
## Tips
1. **Always use Config Service API** - org/site/code repo may differ from what's in the local git remote
2. **Test login URL** - Verify the login URL works before documenting
3. **List all sites** - For repoless setups, document every site
4. **Include examples** - Show actual paths from the project
tools
Use the run-workflow MCP to discover, compose, execute, publish, and save Adobe Firefly workflows. TRIGGER when: user asks what actions are available, what the MCP can do, how to process images/video/3D via workflow, wants to build/run/save/publish a workflow, OR pastes any workflow/batch/execution ID. BARE ID (UUID/workflowId/batchId) = INSPECT ONLY — call inspect_run, NEVER run_workflow_submit. ALWAYS call list_actions first for capability/discovery questions. DO NOT TRIGGER for direct Firefly API calls without MCP (use firefly-api-specs).
tools
Run predefined featured workflows via run-workflow MCP. TRIGGER when user names a featured workflow (retargeting, banners at scale, localization, packaging, banner advertising, etc.) or asks to run a known marketing/production workflow. Requires run-workflow MCP. ALWAYS call get_featured_workflow before compose_workflow. DO NOT TRIGGER for custom one-off workflows with no named template — use run-workflow skill.
tools
Migrate an Adobe Commerce App Builder project from the Integration Starter Kit or Checkout Starter Kit to the new App Management approach. Run from the root of the App Builder project to be migrated. Pass --auto to skip confirmation prompts (suitable for CI or batch use) — auto mode prints a summary of all Q&A questions answered with their defaults. Pass --doc-scan-only to scan README.md and env.dist for outdated content without modifying any files. Use when the user wants to migrate an App Builder project from the Integration Starter Kit or Checkout Starter Kit to the App Management approach, or mentions upgrading their Adobe Commerce extension architecture.
development
Add or modify webhook interceptors in an Adobe Commerce app. Use when the user wants to intercept Commerce operations to validate input, append data, or modify behavior — before or after execution. Requires a base app initialized with commerce-app-init.