feat: rewrite skills/agent docs

Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
This commit is contained in:
David Karlsson
2026-03-31 11:06:48 +02:00
parent 3c87de0c42
commit 2c4facb6d0
18 changed files with 1011 additions and 442 deletions

View File

@@ -0,0 +1,20 @@
#!/bin/bash
# PreToolUse hook for the Bash tool.
# Blocks dangerous git patterns that cause CI failures.
set -euo pipefail
input=$(cat)
command=$(echo "$input" | jq -r '.tool_input.command // empty')
if [ -z "$command" ]; then
exit 0
fi
# Block 'git add .' / 'git add -A' / 'git add --all'
# These stage package-lock.json and other generated files.
if echo "$command" | grep -qE 'git\s+add\s+(\.|--all|-A)(\s|$|;)'; then
echo "BLOCKED: do not use 'git add .' / 'git add -A' / 'git add --all'. Stage files explicitly by name." >&2
exit 2
fi
exit 0

View File

@@ -0,0 +1,25 @@
#!/bin/bash
# PreToolUse hook for Edit and Write tools.
# Blocks edits to vendored content that must be fixed upstream.
set -euo pipefail
input=$(cat)
file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
if [ -z "$file_path" ]; then
exit 0
fi
# Block edits to vendored Hugo modules.
if echo "$file_path" | grep -qE '/_vendor/'; then
echo "BLOCKED: _vendor/ is vendored from upstream Hugo modules. Fix in the source repo instead." >&2
exit 2
fi
# Block edits to vendored CLI reference data.
if echo "$file_path" | grep -qE '/data/cli/'; then
echo "BLOCKED: data/cli/ is generated from upstream repos (docker/cli, docker/buildx, etc.). Fix in the source repo instead." >&2
exit 2
fi
exit 0

26
.agents/settings.json Normal file
View File

@@ -0,0 +1,26 @@
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "bash .agents/hooks/enforce-vendored.sh"
}
],
"description": "Block edits to vendored content (_vendor/, data/cli/)"
},
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash .agents/hooks/enforce-git-hygiene.sh"
}
],
"description": "Block git add . and dangerous patterns"
}
]
}
}

View File

@@ -0,0 +1,90 @@
---
name: check-pr
description: >
Check a single PR's CI status, review comments, and requested changes.
Fix actionable failures and address feedback. "check PR 1234", "what's
the status of my PR", "address review comments on #500".
argument-hint: "<pr-number>"
context: fork
---
# Check PR
Do one pass over PR **$ARGUMENTS**: check CI, read reviews, fix what's
actionable, report status.
## 1. Gather PR state
```bash
# Overall state
gh pr view $ARGUMENTS --repo docker/docs --json state,title,url,headRefName
# CI checks
gh pr checks $ARGUMENTS --repo docker/docs --json name,state,detailsUrl
# Top-level reviews
gh pr view $ARGUMENTS --repo docker/docs --json reviews,reviewDecision
# Inline (line-level) comments — NOT included in the above
gh api repos/docker/docs/pulls/$ARGUMENTS/comments \
--jq '[.[] | {id: .id, author: .user.login, body: .body, path: .path, line: .line}]'
```
Always check both the reviews endpoint and the inline comments endpoint.
A review with an empty body may still have line-level comments requiring
action.
## 2. If merged
Report the final state. No further action needed.
## 3. If closed without merge
Read the closing context to understand why:
```bash
gh pr view $ARGUMENTS --repo docker/docs --json closedAt,comments \
--jq '{closedAt, lastComment: .comments[-1].body}'
```
Report the reason. Common causes: rejected by maintainers, superseded by
another PR, closed by automation.
## 4. If CI is failing
- Read the failure details (follow `detailsUrl` if needed)
- Determine if the failure is in the PR's changed files or pre-existing
- **Actionable:** check out the branch, fix, commit, push
```bash
git checkout <branch>
# fix the issue
git add <files>
git commit -m "fix: <description>"
git push
```
- **Pre-existing / upstream:** note it, do not block
## 5. If review comments or changes requested
- Read each unresolved comment
- Address feedback in a follow-up commit
- Push, then reply to each comment explaining what was done:
```bash
gh api repos/docker/docs/pulls/$ARGUMENTS/comments \
--method POST \
--field in_reply_to=<comment-id> \
--field body="<response>"
```
- End every comment with a `Generated by [Claude Code](https://claude.com/claude-code)` footer
- Re-request review if changes were requested
## 6. Report
```
## PR #$ARGUMENTS: <title>
**State:** <open|merged|closed>
**CI:** <passing|failing|pending>
**Review:** <approved|changes requested|pending>
**Action taken:** <what was done, or "none needed">
```

View File

@@ -0,0 +1,123 @@
---
name: create-pr
description: >
Push the current branch and create a pull request against docker/docs.
Use after changes are committed and reviewed. "create a PR", "submit the
fix", "open a pull request for this".
---
# Create PR
Push the branch and create a properly structured pull request.
## 1. Verify the branch
```bash
git log --oneline main..HEAD # confirm commits exist
git diff --quiet # confirm no unstaged changes
```
## 2. Push the branch
Confirm origin points to your fork, not upstream:
```bash
git remote get-url origin
```
Then push:
```bash
git push -u origin <branch-name>
```
## 3. Create the PR
Derive the fork owner dynamically:
```bash
FORK_OWNER=$(git remote get-url origin | sed -E 's|.*[:/]([^/]+)/[^/]+(\.git)?$|\1|')
```
```bash
gh pr create --repo docker/docs \
--head "${FORK_OWNER}:<branch-name>" \
--title "<concise summary under 70 chars>" \
--body "$(cat <<'EOF'
## Summary
<1-2 sentences: what was wrong and what was changed>
Closes #NNNN
Generated by [Claude Code](https://claude.com/claude-code)
EOF
)"
```
Keep the body short. Reviewers need to know what changed and why — nothing
else.
### Optional: Learnings section
If while working on this PR you discovered something non-obvious about the
repo — a convention not documented in AGENTS.md, a gotcha that tripped you
up, a pattern that should be codified — add a Learnings section to the PR
body:
```markdown
## Learnings
- <what you learned and why it matters>
```
Add this section between the Summary and the `Closes` line. Only include
learnings that would help future contributors avoid the same issue. Do not
include things already documented in AGENTS.md or STYLE.md.
The weekly PR learnings scanner reads these sections to surface recurring
patterns for the team to codify.
## 4. Apply labels and request review
Use the Issues API for labels — `gh pr edit --add-label` silently fails:
```bash
gh api repos/docker/docs/issues/<pr-number>/labels \
--method POST \
--field 'labels[]=status/review'
```
Request review:
```bash
gh pr edit <pr-number> --repo docker/docs --add-reviewer docker/docs-team
```
Verify the reviewer was assigned:
```bash
gh pr view <pr-number> --repo docker/docs --json reviewRequests \
--jq '.reviewRequests[].slug'
```
If the team doesn't appear, use the API directly:
```bash
gh api repos/docker/docs/pulls/<pr-number>/requested_reviewers \
--method POST --field 'team_reviewers[]=docs-team'
```
## 5. Report
Print the PR URL and current CI state:
```bash
gh pr view <pr-number> --repo docker/docs --json url,state
gh pr checks <pr-number> --repo docker/docs --json name,state
```
## Notes
- Always use `Closes #NNNN` (not "Fixes") for GitHub auto-close linkage
- One issue, one branch, one PR — never combine

View File

@@ -0,0 +1,83 @@
---
name: fix-issue
description: >
Fix a single GitHub issue end-to-end: triage, research, write the fix,
review, and create a PR. Use when asked to fix an issue: "fix issue 1234",
"resolve #500", "create a PR for issue 200".
argument-hint: "<issue-number>"
---
# Fix Issue
Given GitHub issue **$ARGUMENTS**, decide what to do with it and either
close it or fix it. This skill orchestrates the composable skills — it owns
the decision tree, not the individual steps.
## 1. Triage
Invoke `/triage-issue $ARGUMENTS` to understand the issue and decide what
to do. This runs in a forked subagent and returns a verdict.
## 2. Act on the triage result
If triage says **close it** — comment with the reason and close:
```bash
gh issue close $ARGUMENTS --repo docker/docs \
--comment "<one sentence explaining why>
Generated by [Claude Code](https://claude.com/claude-code)"
```
Done.
If triage says **escalate upstream** — comment noting the repo and stop:
```bash
gh issue comment $ARGUMENTS --repo docker/docs \
--body "This needs to be fixed in <upstream-repo>.
Generated by [Claude Code](https://claude.com/claude-code)"
```
Done.
If triage says **leave it open** — comment explaining what was checked and
what's unclear. Do not close.
Done.
End every issue comment with a `Generated by [Claude Code](https://claude.com/claude-code)` footer.
If triage says **fix it** — proceed to step 3.
## 3. Research
Invoke `/research` to locate affected files, verify facts, and identify
the fix. The issue context carries over from triage. This runs inline —
findings stay in conversation context for the write step.
If research reveals the issue is upstream or cannot be fixed (e.g.
unverifiable URLs), comment on the issue and stop.
## 4. Write
Invoke `/write` to create a branch, make the change, format, self-review,
and commit.
## 5. Review
Invoke `/review-changes` to check the diff for correctness, coherence, and
mechanical compliance. This runs in a forked subagent with fresh context.
If issues are found, fix them and re-review until clean.
## 6. Create PR
Invoke `/create-pr` to push the branch and open a pull request.
## 7. Return to main
```bash
git checkout main
```
## 8. Report
Summarize what happened: the issue number, what was done (closed, escalated,
fixed with a PR link), and why — in a sentence or two.

View File

@@ -0,0 +1,93 @@
---
name: research
description: >
Research a documentation topic — locate affected files, understand the
problem, identify what to change. Use when investigating an issue, a
question, or a topic before writing a fix. Triggers on: "research issue
1234", "investigate what needs changing for #500", "what files are
affected by #200", "where is X documented", "is our docs page about Y
accurate", "look into how we document Z".
---
# Research
Thoroughly investigate the topic at hand and produce a clear plan for
the fix. The goal is to identify exact files, named targets within those
files, and the verified content needed for the fix.
## 1. Gather context
If the input is a GitHub issue number, fetch it:
```bash
gh issue view <number> --repo docker/docs \
--json number,title,body,labels,comments
```
Otherwise, work from what was provided — a description, a URL, a question,
or prior conversation context. Identify the topic, affected feature, or
page to investigate.
## 2. Locate affected files
Search `content/` using the URL or topic from the issue. Remember the
`/manuals` prefix mapping when converting URLs to file paths.
For each candidate file, read the relevant section to confirm it contains
the reported problem.
## 3. Check vendored ownership
Before planning any edit, verify the file is editable locally:
- `_vendor/` — read-only, vendored via Hugo modules
- `data/cli/` — read-only, generated from upstream YAML
- `content/reference/cli/` — read-only, generated from `data/cli/`
- Everything else in `content/` — editable
If the fix requires upstream changes, identify the upstream repo and note
it as out of scope. See the vendored content table in CLAUDE.md.
## 4. Find related content
Look for pages that may need updating alongside the primary fix:
- Pages that link to the affected content
- Include files (`content/includes/`) referenced by the page
- Related pages in the same section describing the same feature
## 5. Verify facts
If the issue makes a factual claim about how a feature behaves, verify it.
Follow external links, read upstream source, check release notes. Do not
plan a fix based on an unverified claim.
If the fix requires a replacement URL and that URL cannot be verified (e.g.
network restrictions), report it as a blocker rather than guessing.
## 6. Check the live site (if needed)
For URL or rendering issues, fetch the live page:
```
https://docs.docker.com/<path>/
```
## 7. Report findings
Summarize what you found — files to change, the specific problem in each,
what the fix should be, and any constraints. This context feeds directly
into the write step.
Be specific: name the file, the section or element within it, and the
verified content needed. "Fix the broken link in networking.md" is not
specific enough. "In `compose/networking.md`, the 'Custom networks' section,
remove the note about `driver_opts` being ignored — this was fixed in
Compose 2.24" is.
## Notes
- Research quality bounds write quality. Vague research produces broad
changes; precise research produces minimal ones.
- Do not create standalone research files — findings stay in conversation
context for the write step.

View File

@@ -0,0 +1,107 @@
---
name: review-changes
description: >
Review uncommitted or recently committed documentation changes for
correctness, coherence, and style compliance. Use before creating a PR
to catch issues. "review my changes", "review the diff", "check the fix
before submitting", "does this look right".
context: fork
model: opus
---
# Review Changes
Evaluate whether the changes correctly and completely solve the stated
problem, without introducing new issues. Start with no assumptions — the
change may contain mistakes. Your job is to catch what the writer missed,
not to rubber-stamp the diff.
## 1. Identify what changed
Determine the scope of changes to review:
```bash
# Uncommitted changes
git diff --name-only
# Last commit
git diff --name-only HEAD~1
# Entire branch vs main
git diff --name-only main...HEAD
```
Pick the right comparison for what's being reviewed. If reviewing a branch,
use `main...HEAD` to see all changes since the branch diverged.
## 2. Read each changed file in full
Do not just read the diff. For every changed file, read the entire file to
understand the full context the change lives in. A diff can look correct in
isolation but contradict something earlier on the same page.
Then read the diff for the detailed changes:
```bash
# Adjust the comparison to match step 1
git diff --unified=10 # uncommitted
git diff --unified=10 HEAD~1 # last commit
git diff --unified=10 main...HEAD # branch
```
## 3. Follow cross-references
For each changed file, check what links to it and what it links to:
- Search for other pages that reference the changed content (grep for the
filename, heading anchors, or key phrases)
- Read linked pages to verify the change doesn't create contradictions
across pages
- Check that anchor links in cross-references still match heading IDs
A change that's correct on its own page can break the story told by a
related page.
## 4. Verify factual accuracy
Don't assume the change is factually correct just because it reads well.
- If the change describes how a feature behaves, verify against upstream
docs or source code
- If the change includes a URL, check that it resolves
- If the change references a CLI flag, option, or API field, confirm it
exists
## 5. Evaluate as a reader
Consider someone landing on this page from a search result, with no prior
context:
- Does the page make sense on its own?
- Is the changed section clear without having read the issue or diff?
- Would a reader be confused by anything the change introduces or leaves
out?
## 6. Review code and template changes
For non-Markdown changes (JS, HTML, CSS, Hugo templates):
- Trace through the common execution path
- Trace through at least one edge case (no stored preference, Alpine fails
to load, first visit vs returning visitor)
- Ask whether the change could produce unexpected browser or runtime
behavior that no automated tool would catch
## 7. Decision
**Approve** if the change is correct, coherent, complete, and factually
accurate.
**Request changes** if:
- The change does not correctly solve the stated problem
- There is a factual error or contradiction (on-page or cross-page)
- A cross-reference is broken or misleading
- A reader would be confused
When requesting changes, be specific: quote the exact text that is wrong,
explain why, and suggest the correct fix.

View File

@@ -0,0 +1,136 @@
---
name: triage-issue
description: >
Analyze a single GitHub issue for docker/docs — check whether the problem
still exists, determine a verdict, and report findings. Use when asked to
triage, assess, or review an issue, even if the user doesn't say "triage"
explicitly: "triage issue 1234", "is issue 500 still valid", "should we
close #200", "look at this issue", "what's going on with #200".
argument-hint: "<issue-number>"
context: fork
---
# Triage Issue
Given GitHub issue **$ARGUMENTS** from docker/docs, figure out whether
it's still a real problem and say what should happen next.
## 1. Fetch the issue
```bash
gh issue view $ARGUMENTS --repo docker/docs \
--json number,title,body,state,labels,createdAt,updatedAt,closedAt,assignees,author,comments
```
## 2. Understand the problem
Read the issue body and all comments. Identify:
- What is the reported problem?
- What content, URL, or file does it reference?
- Are there linked PRs? Check whether they were merged or closed without merge.
- Has anyone already proposed a fix or workaround in the comments?
## 3. Follow URLs
Find all `docs.docker.com` URLs in the issue body and comments. For each:
- Fetch the URL to check if it still exists (404 = content removed or moved)
- Check whether the content still contains the problem described
- Note when the page was last updated relative to when the issue was filed
For non-docs URLs (GitHub links, external references), fetch them too if
they are central to understanding the issue.
## 4. Check the repository
If the issue references specific files, content sections, or code:
- Find and read the current version of that content
- Check whether the problem has been fixed, content moved, or file removed
- Remember the `/manuals` prefix mapping when looking up files
## 5. Check for upstream ownership
If the issue is about content in `_vendor/` or `data/cli/`, it cannot be
fixed here. Identify which upstream repo owns it (see the vendored content
table in CLAUDE.md).
## 6. Decide and act
After investigating, pick one of these verdicts and take the corresponding
action on the issue:
- **Close it** — the problem is already fixed, the content no longer exists,
or the issue is too outdated to be useful. Close the issue with a comment
explaining why:
```bash
gh issue close $ARGUMENTS --repo docker/docs \
--comment "Closing: <one-sentence reason>"
```
- **Fix it** — the problem is real and fixable in this repo. Name the
file(s) and what needs to change. Label the issue `status/confirmed` and
remove `status/triage` if present:
```bash
gh api repos/docker/docs/issues/$ARGUMENTS/labels \
--method POST --field 'labels[]=status/confirmed'
gh api repos/docker/docs/issues/$ARGUMENTS/labels/status%2Ftriage \
--method DELETE || true
```
- **Escalate upstream** — the problem is real but lives in vendored content.
Name the upstream repo. Label the issue `status/upstream` and remove
`status/triage` if present:
```bash
gh api repos/docker/docs/issues/$ARGUMENTS/labels \
--method POST --field 'labels[]=status/upstream'
gh api repos/docker/docs/issues/$ARGUMENTS/labels/status%2Ftriage \
--method DELETE || true
```
- **Leave it open** — you can't determine the current state, or the issue
needs human judgment. Label the issue `status/needs-analysis`:
```bash
gh api repos/docker/docs/issues/$ARGUMENTS/labels \
--method POST --field 'labels[]=status/needs-analysis'
gh api repos/docker/docs/issues/$ARGUMENTS/labels/status%2Ftriage \
--method DELETE || true
```
Don't overthink the classification. An old issue isn't stale if the problem
still exists. An upstream issue is still valid — it's just not fixable here.
Also apply the most relevant `area/` label based on the content affected.
Available area labels: `area/accounts`, `area/admin`, `area/ai`,
`area/api`, `area/billing`, `area/build`, `area/build-cloud`, `area/cli`,
`area/compose`, `area/compose-spec`, `area/config`, `area/contrib`,
`area/copilot`, `area/desktop`, `area/dhi`, `area/engine`,
`area/enterprise`, `area/extensions`, `area/get-started`, `area/guides`,
`area/hub`, `area/install`, `area/networking`, `area/offload`,
`area/release-notes`, `area/samples`, `area/scout`, `area/security`,
`area/storage`, `area/subscription`, `area/swarm`, `area/ux`. Pick one
(or at most two if the issue clearly spans areas). Skip if none fit.
```bash
gh api repos/docker/docs/issues/$ARGUMENTS/labels \
--method POST --field 'labels[]=area/<name>'
```
## 7. Report
Write a short summary: what the issue reports, what you found, and what
should happen next. Reference the specific files, URLs, or PRs that support
your conclusion. Skip metadata fields — the issue itself has the dates and
labels. Mention the action you took (closed, labeled, etc.).
## Notes
- A merged PR linked to an issue is strong evidence the issue is fixed
- A closed-without-merge PR means the issue is likely still open
- Do not narrate your process — produce the final report
- End every issue comment with a `Generated by [Claude Code](https://claude.com/claude-code)` footer

View File

@@ -0,0 +1,87 @@
---
name: write
description: >
Write a documentation fix on a branch. Makes the minimal change, formats,
self-reviews, and commits. Use after research has identified what to change.
"write the fix", "make the changes", "implement the fix for #1234".
hooks:
PostToolUse:
- matcher: "Edit|Write"
hooks:
- type: command
command: "bash ${CLAUDE_SKILL_DIR}/scripts/post-edit.sh"
---
# Write
Make the minimal change that resolves the issue. Research has already
identified what to change — this skill handles the edit, formatting,
self-review, and commit.
## 1. Create a branch
```bash
git checkout -b fix/issue-<number>-<short-desc> main
```
Use a short kebab-case description derived from the issue title (3-5 words).
## 2. Read then edit
Always read each file before modifying it. Make the minimal change that
fixes the issue. Do not improve surrounding content, add comments, or
address adjacent problems.
Follow the writing guidelines in CLAUDE.md, STYLE.md, and COMPONENTS.md.
## 3. Front matter check
Every content page requires `title`, `description`, and `keywords` in its
front matter. If any are missing from a file you touch, add them.
## 4. Validate
Prettier runs automatically after each edit via the PostToolUse hook.
Run lint manually after all edits are complete:
```bash
${CLAUDE_SKILL_DIR}/scripts/lint.sh <changed-files>
```
The lint script runs markdownlint and vale on only the files you pass it,
so the output is scoped to your changes. Fix any errors it reports.
## 5. Self-review
Re-read each changed file: right file, right lines, change is complete,
front matter is present. Run `git diff` and verify only intended changes
are present.
## 6. Commit
Stage only the changed files:
```bash
git add <files>
git diff --cached --name-only # verify — no package-lock.json or other noise
git commit -m "$(cat <<'EOF'
docs: <short description under 72 chars> (fixes #NNNN)
<What was wrong: one sentence citing the specific problem.>
<What was changed: one sentence describing the exact edit.>
Co-Authored-By: Claude <noreply@anthropic.com>
EOF
)"
```
The commit body is mandatory. A reviewer reading only the commit should
understand the problem and the fix without opening the issue.
## Notes
- Never edit `_vendor/` or `data/cli/` — these are vendored
- If a file doesn't exist, check for renames:
`git log --all --full-history -- "**/filename.md"`
- If the fix requires a URL that cannot be verified, stop and report a
blocker rather than guessing

View File

@@ -0,0 +1,27 @@
#!/bin/bash
# Run markdownlint and vale on specific files.
# Usage: .agents/skills/write/scripts/lint.sh <file> [file...]
#
# Designed for agent workflows — scoped output, no repo-wide noise.
# For full repo validation, use: docker buildx bake validate
set -uo pipefail
if [ $# -eq 0 ]; then
echo "Usage: $0 <file> [file...]" >&2
exit 1
fi
exit_code=0
echo "=== markdownlint ==="
if ! npx markdownlint-cli "$@" 2>&1; then
exit_code=1
fi
echo ""
echo "=== vale ==="
if ! vale "$@" 2>&1; then
exit_code=1
fi
exit $exit_code

View File

@@ -0,0 +1,12 @@
#!/bin/bash
# PostToolUse hook for Edit/Write in the write skill.
# Auto-formats Markdown files with prettier after each edit.
set -euo pipefail
input=$(cat)
file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
[ -z "$file_path" ] && exit 0
[[ "$file_path" != *.md ]] && exit 0
npx prettier --write "$file_path" 2>/dev/null

1
.claude Symbolic link
View File

@@ -0,0 +1 @@
.agents

View File

@@ -1,127 +0,0 @@
---
name: fix-issues
description: >
Fix one or more GitHub issues by creating branches, writing fixes, and opening PRs.
Use this skill whenever the user provides GitHub issue numbers and wants them fixed,
or says things like "fix issue 1234", "address these issues", "create PRs for issues
1234 and 5678". Triggers on any request involving GitHub issue numbers paired with
fixing, addressing, resolving, or creating PRs. Also triggers for "fix #1234" shorthand.
---
# Fix Issues
Given one or more GitHub issue numbers from the docker/docs repository, fix each
issue end-to-end: analyze, branch, fix, commit, push, and open a PR.
## Workflow
### 1. Fetch all issues in parallel
Use `gh issue view <number> --repo docker/docs --json title,body,labels` for each
issue number. Launch all fetches in parallel since they're independent.
### 2. Fix each issue sequentially
Process each issue one at a time in the main context. Do NOT use background
subagents for this — they can't get interactive Bash permission approval, which
blocks all git operations. Sequential processing in the main context is faster
than agents that stall on permissions.
For each issue:
#### a. Analyze
Read the issue body to understand:
- Which file(s) need changes
- What the problem is
- What the fix should be
#### b. Create a branch
```bash
git checkout -b fix/issue-<number>-<short-description> main
```
Use a short kebab-case description derived from the issue title (3-5 words max).
#### c. Read and fix
- Read each affected file before editing
- Make the minimal change that addresses the issue
- Don't over-engineer or add unrequested improvements
- Follow the repository's STYLE.md and COMPONENTS.md guidelines
#### d. Format
Run prettier on every changed file:
```bash
npx prettier --write <file>
```
#### e. Self-review
Re-read the changed file to verify:
- The fix addresses the issue correctly
- No unintended changes were introduced
- Formatting looks correct
#### f. Commit
Stage only the changed files (not `git add -A`).
#### g. Push and create PR
```bash
git push -u origin fix/issue-<number>-<short-description>
```
Then create the PR:
```bash
gh pr create --repo docker/docs \
--title "<Short summary matching commit>" \
--body "$(cat <<'EOF'
## Summary
- <1-3 bullet points describing what changed and why>
Closes #<issue-number>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
EOF
)"
```
#### h. Label and assign reviewers
```bash
gh pr edit <pr-number> --repo docker/docs \
--add-label "status/review" \
--add-reviewer docker/docs-team
```
### 3. Switch back to main
After all issues are processed:
```bash
git checkout main
```
### 4. Report results
Present a summary table:
| Issue | PR | Change |
|-------|-----|--------|
| #N | #M | Brief description |
## Important notes
- Always work from `main` as the base for each branch
- Each issue gets its own branch and PR — don't combine issues
- If an issue references a file that doesn't exist, check for renames or
reorganization before giving up (files move around in this repo)
- Validation commands (`docker buildx bake lint vale`) are available but slow;
only run them if the user asks or the changes are complex enough to warrant it

View File

@@ -1,150 +0,0 @@
---
name: process-issues
description: >
Process a batch of GitHub issues end-to-end: fetch unlabeled issues, triage
them, apply labels, fix the actionable ones, and babysit the resulting PRs.
Use this skill when the user wants to work through the issue backlog
autonomously — e.g. "process issues", "work through the backlog", "run the
issue pipeline". Accepts an optional batch size: "process issues 5".
Pairs well with /loop for continuous background processing.
---
# Process Issues
Fetches a batch of unprocessed GitHub issues, triages each one, labels it,
fixes the actionable ones, and babysits the resulting PRs.
## Arguments
- Batch size (optional, default 10): number of issues to process per run.
E.g. "process issues 5" or "process issues --batch 5".
## Labels
These labels track agent processing state. Create any that don't exist yet
with `gh label create --repo docker/docs <name> --color <hex>`.
| Label | Meaning |
|-------|---------|
| `agent/triaged` | Agent has analyzed this issue; verdict in a comment |
| `agent/fix` | Agent has opened a PR for this issue |
| `agent/skip` | Triaged; not actionable (STALE, UPSTREAM, INDETERMINATE, CLOSEABLE_FIXED) |
## Workflow
### 1. Resolve fork username and fetch a batch
Get the authenticated user's GitHub login — don't hardcode it:
```bash
FORK_USER=$(gh api user --jq '.login')
```
Fetch up to N open issues that have none of the `agent/*` labels:
```bash
gh issue list --repo docker/docs \
--state open \
--limit <N> \
--json number,title,labels \
--jq '[.[] | select(
([.labels[].name] | map(startswith("agent/")) | any) | not
)]'
```
If there are no unprocessed issues, report that the backlog is clear and stop.
### 2. Triage each issue
Follow the full **triage-issues** skill workflow for all fetched issues,
running fetches in parallel. Produce a verdict for each:
OPEN, CLOSEABLE_FIXED, UPSTREAM, INDETERMINATE, or STALE.
### 3. Label each issue immediately after verdict
Apply `agent/triaged` to every issue regardless of verdict — it means "we
looked at it." Then apply a second label based on the outcome:
```bash
# All issues — mark as triaged
gh issue edit <number> --repo docker/docs --add-label "agent/triaged"
# Non-actionable (STALE, UPSTREAM, INDETERMINATE)
gh issue edit <number> --repo docker/docs --add-label "agent/skip"
# Already resolved (CLOSEABLE_FIXED) — close with explanation
gh issue close <number> --repo docker/docs \
--comment "Closing — <one sentence explaining what resolved it>."
# Actionable (OPEN, no existing PR) — no extra label yet; agent/fix applied after PR is created
```
Leave a comment on every issue summarising the verdict and reasoning in one
sentence. Do this immediately — don't batch it for the end.
### 4. Fix actionable issues
For each issue with verdict OPEN and no existing open PR, follow the
**fix-issues** skill workflow.
Skip issues where:
- An open PR already exists
- Verdict is anything other than OPEN
- The fix requires changes to `_vendor/` or `data/cli/` (upstream owned)
After the PR is created, apply `agent/fix` to the issue:
```bash
gh issue edit <number> --repo docker/docs --add-label "agent/fix"
```
### 5. Babysit PRs
After opening PRs, schedule a recurring check with `/loop` so babysitting
continues asynchronously after the batch summary is reported:
```
/loop 5m babysit PRs <#N, #M, …> in docker/docs — check for failing checks,
new review comments, and requested changes; investigate and fix anything that
needs attention; stop looping once all PRs are merged or closed
```
At each check, for every open PR:
- **Failing checks**: investigate the failure, fix the cause, force-push an
updated commit to the branch via the GitHub API
- **Review comments**: read them, address the feedback, push an update, reply
to the comment
- **All clear**: note it and move on
Don't just report status — act on anything that needs attention.
### 6. Report results
```
## Batch summary
Processed: <N> issues
PRs opened: <n>
Skipped: <n> (STALE: n, UPSTREAM: n, INDETERMINATE: n)
Closed: <n> (already resolved)
### PRs opened
| Issue | PR | Checks | Review |
|-------|-----|--------|--------|
| #N | #M | ✅ | pending |
### Skipped
| Issue | Verdict | Reason |
|-------|---------|--------|
| #N | STALE | ... |
```
## Notes
- **Fork username**: always resolve dynamically with `gh api user --jq '.login'`
- **One issue, one PR**: never combine multiple issues in a single branch
- **Validation**: skip `docker buildx bake lint vale` unless the change is
complex — it's slow and the basic checks run automatically on the PR
- **Resumability**: labels are applied immediately at triage time, so if the
session ends mid-run the next run skips already-processed issues automatically

View File

@@ -1,135 +0,0 @@
---
name: triage-issues
description: >
Triage one or more GitHub issues for the docker/docs repository. Analyzes each
issue's content, checks whether the problem still exists in the repo and on the
live site, and produces a structured verdict. Use this skill whenever the user
asks to triage, analyze, review, or assess GitHub issues — e.g. "triage issue
1234", "what's the status of these issues", "which of these can be closed",
"look at issues 100 200 300 and tell me what to do with them".
---
# Triage Issues
Given one or more GitHub issue numbers from the docker/docs repository, analyze
each issue and produce a structured verdict on its current status.
## Workflow
### 1. Fetch all issues in parallel
For each issue number, fetch everything in a single call:
```bash
gh issue view <number> --repo docker/docs \
--json number,title,body,state,labels,createdAt,updatedAt,closedAt,assignees,author,comments
```
When triaging multiple issues, fetch all of them in parallel before starting
analysis — don't process one at a time.
### 2. Analyze each issue
For each issue, work through these checks:
#### a. Understand the problem
Read the issue body and all comments. Identify:
- What is the reported problem?
- What content, URL, or file does it reference?
- Are there linked PRs? Check whether they were merged or closed without merge.
- Has anyone already proposed a fix or workaround in the comments?
#### b. Follow URLs
Find all `docs.docker.com` URLs in the issue body and comments. For each one:
- Fetch the URL to check if it still exists (404 = content removed or moved)
- Check whether the content still contains the problem described
- Note when the page was last updated relative to when the issue was filed
For non-docs URLs (GitHub links, external references), fetch them too if they
are central to understanding the issue.
#### c. Check the repository
If the issue references specific files, content sections, or code:
- Use file tools to find and read the current version of that content
- Check whether the problem has been fixed, the content moved, or the file removed
- Remember the `/manuals` prefix mapping when looking up files
#### d. Check for upstream ownership
If the issue is about content in `_vendor/` or `data/cli/`, it cannot be fixed
here. Identify which upstream repo owns it and note that in your analysis.
### 3. Determine verdict
Assign one of these verdicts based on what you found:
| Verdict | When to use |
|---------|-------------|
| **OPEN** | Issue is valid and still unfixed in this repo |
| **CLOSEABLE_FIXED** | Content has been updated, corrected, or removed since the issue was filed |
| **UPSTREAM** | Problem exists but originates in vendored/upstream content |
| **INDETERMINATE** | Not enough information to determine current state |
| **STALE** | Outdated with no recent activity; references content or features that no longer exist; context has changed enough that a new issue would be more appropriate |
Be confident when evidence is clear. Use INDETERMINATE only when you genuinely
cannot determine the current state after checking.
### 4. Report results
#### Single issue
Print a structured report:
```
## Issue #<number>: <title>
**Verdict:** <VERDICT>
**Confidence:** <high|medium|low>
**Filed:** <creation date>
**Last activity:** <last comment or update date>
**Labels:** <labels or "none">
### Summary
<One or two sentences describing the reported problem.>
### Analysis
<What you checked and what you found. Reference specific URLs, files, or
content. Note any linked PRs or related issues.>
### Recommendation
<Concrete next step: close with a comment, fix specific content, escalate
to upstream repo, request more info from reporter, etc.>
```
#### Multiple issues
Start with a summary table, then print the full report for each issue:
```
| Issue | Title | Verdict | Confidence |
|-------|-------|---------|------------|
| #123 | ... | OPEN | high |
| #456 | ... | STALE | medium |
---
## Issue #123: ...
[full report]
---
## Issue #456: ...
[full report]
```
## Notes
- A merged PR linked to an issue is strong evidence the issue is fixed
- A closed-without-merge PR means the issue is likely still open
- Check creation date and last activity to assess staleness — issues with no
activity in over a year that reference old product versions are candidates
for STALE
- Do not narrate your process; just produce the final report(s)

211
AGENTS.md
View File

@@ -1,7 +1,7 @@
# AGENTS.md
Instructions for AI agents working on the Docker documentation
repository. This site builds https://docs.docker.com/ using Hugo.
Instructions for AI agents working on Docker documentation.
This site builds https://docs.docker.com/ using Hugo.
## Project structure
@@ -18,27 +18,94 @@ static/ # Images, fonts
_vendor/ # Vendored Hugo modules (read-only)
```
## URL prefix stripping
The `/manuals` prefix is stripped from published URLs:
`content/manuals/desktop/` `/desktop/` on the live site.
`content/manuals/desktop/install.md` becomes `/desktop/install/` on the live
site.
When writing internal cross-references in source files, keep the `/manuals/`
prefix in the path — Hugo requires the full source path. The stripping only
affects the published URL, not the internal link target. Anchor links must
exactly match the generated heading ID (Hugo lowercases and slugifies
headings).
## Vendored content (do not edit)
Content in `_vendor/` and CLI reference data in `data/cli/` are vendored
from upstream repos. Content pages under `content/reference/cli/` are
generated from `data/cli/` YAML. Do not edit any of these files — changes
must go to the source repository:
| Content | Source repo |
|---------|-------------|
| CLI reference (`docker`, `docker build`, etc.) | docker/cli |
| Buildx reference | docker/buildx |
| Compose reference | docker/compose |
| Model Runner reference | docker/model-runner |
| Dockerfile reference | moby/buildkit |
| Engine API reference | moby/moby |
If a validation failure or broken link traces back to vendored content, note
the upstream repo that needs fixing. Do not attempt to fix it locally.
## Writing guidelines
Read and follow [STYLE.md](STYLE.md) and [COMPONENTS.md](COMPONENTS.md).
These contain all style rules, shortcode syntax, and front matter
requirements.
These contain all style rules, shortcode syntax, and front matter requirements.
## Vendored content (do not edit)
### Style violations to avoid
Content in `_vendor/` and CLI reference pages generated from
`data/cli/` are vendored from upstream repos. Don't edit these
files — changes must go to the source repository:
Every piece of writing must avoid these words and patterns (enforced by Vale):
- docker/cli, docker/buildx, docker/compose, docker/model-runner → CLI reference YAML in `data/cli/`
- moby/buildkit → Dockerfile reference in `_vendor/`
- moby/moby → Engine API docs in `_vendor/`
- Hedge words: "simply", "easily", "just", "seamlessly"
- Meta-commentary: "it's worth noting", "it's important to understand"
- "allows you to" or "enables you to" — use "lets you" or rephrase
- "we" — use "you" or "Docker"
- "click" — use "select"
- Bold for emphasis or product names — only bold UI elements
- Time-relative language: "currently", "new", "recently", "now"
If a validation failure traces back to vendored content, note the
upstream repo that needs fixing but don't block on it.
### Version-introduction notes
Explicit version anchors ("Starting with Docker Desktop version X...") are
different from time-relative language — they mark when a feature was
introduced, which is permanently true.
- Recent releases (~6 months): leave version callouts in place
- Old releases: consider removing if the callout adds little value
- When in doubt, keep the callout and flag for maintainer review
### Vale gotchas
- Use lowercase "config" in prose — `vale.Terms` flags a capital-C "Config"
## Alpine.js patterns
Do not combine Alpine's `x-show` with the HTML `hidden` attribute on the
same element. `x-show` toggles inline `display` styles, but `hidden` applies
`display: none` via the user-agent stylesheet — the element stays hidden
regardless of `x-show` state. Use `x-cloak` for pre-Alpine hiding instead.
The site defines `[x-cloak=""] { display: none !important }` in `global.css`.
## Front matter requirements
Every content page under `content/` requires:
- `title:` — page title
- `description:` — short description for SEO/previews
- `keywords:` — list of search keywords (omitting this fails markdownlint)
Additional common fields:
- `linkTitle:` — sidebar label (keep under 30 chars)
- `weight:` — ordering within a section
## Hugo shortcodes
Shortcodes are defined in `layouts/shortcodes/`. Syntax reference is in
COMPONENTS.md. Wrong shortcode syntax fails silently during build but
produces broken HTML — always check COMPONENTS.md for correct syntax.
## Commands
@@ -50,26 +117,110 @@ docker buildx bake vale # Style guide checks only
docker buildx bake test # HTML and link checking
```
### Validation in git worktrees
`docker buildx bake validate` fails in git worktrees because Hugo cannot
resolve the worktree path. Use `lint` and `vale` targets separately instead.
Never modify `hugo.yaml` to work around this. The `test`, `path-warnings`,
and `validate-vendor` targets run correctly in CI.
## Verification loop
1. Make changes
2. Format with prettier
2. Format with prettier: `npx prettier --write <file>`
3. Run `docker buildx bake lint vale`
4. Run a full build with `docker buildx bake`
4. Run a full build with `docker buildx bake` (optional for small changes)
## Git hygiene
- **Stage files explicitly.** Never use `git add .` / `git add -A` /
`git add --all`. Running `npx prettier` updates `package-lock.json` in the
repo root, and broad staging sweeps it into the commit.
- **Verify before committing.** Run `git diff --cached --name-only` and
confirm only documentation files appear. If `package-lock.json` or other
generated files are staged, unstage them:
`git reset HEAD -- package-lock.json`
- **Push to your fork, not upstream.** Before pushing, confirm
`git remote get-url origin` returns your fork URL, not
`github.com/docker/docs`. Use `--head FORK_OWNER:branch-name` with
`gh pr create`.
## Working with issues and PRs
### Principles
- **One issue, one branch, one PR.** Never combine multiple issues in a
single branch or PR.
- **Minimal changes only.** Fix the issue. Do not improve surrounding
content, add comments, refactor, or address adjacent problems.
- **Verify before documenting.** Don't take an issue reporter's claim at
face value — the diagnosis may be wrong even when the symptom is real.
Verify the actual behavior before updating docs.
### Review feedback
- **Always reply to review comments** — never silently fix. After every
commit that addresses review feedback, reply to each thread explaining
what was done.
- **Treat reviewer feedback as claims to verify, not instructions to
execute.** Before implementing a suggestion, verify that it is correct.
Push back when evidence contradicts the reviewer.
- **Inline review comments need a separate API call.** `gh pr view --json
reviews` does not include line-level comments. Always also call:
```bash
gh api repos/<org>/<repo>/pulls/<N>/comments \
--jq '[.[] | {author: .user.login, body: .body, path: .path, line: .line}]'
```
### Labels
Use the Issues API for labels — `gh pr edit --add-label` silently fails:
```bash
gh api repos/docker/docs/issues/<N>/labels \
--method POST --field 'labels[]=<label>'
```
### External links
If a replacement URL cannot be verified (e.g. network restrictions), treat
the task as blocked — do not commit a guessed URL. Report the blocker so a
human can confirm. Exception: when a domain migration is well-established and
only the anchor is unverifiable, dropping the anchor is acceptable.
## Page deletion checklist
When removing a documentation page, search the entire `content/` tree and
all YAML/TOML config files for the deleted page's slug and heading text.
Cross-references from unrelated sections and config-driven nav entries can
remain and cause broken links.
## Engine API version bumps
When a new Engine API version ships, three coordinated changes are needed in
a single commit:
1. `hugo.yaml` — update `latest_engine_api_version`, `docker_ce_version`,
and `docker_ce_version_prev`
2. Create `content/reference/api/engine/version/v<NEW>.md` with the
`/latest/` aliases block (copy from previous version)
3. Remove the aliases block from
`content/reference/api/engine/version/v<PREV>.md`
Never leave both version files carrying `/latest/` aliases simultaneously.
## Hugo icon references
Before changing an icon reference in response to a "file not found" error,
verify the file actually exists via Hugo's virtual filesystem. Files may
exist in `node_modules/@material-symbols/svg-400/rounded/` but not directly
in `assets/icons/`. Check both locations before concluding an icon is
missing.
## Self-improvement
After every correction or mistake, update this file with a rule to
prevent repeating it. End corrections with: "Now update AGENTS.md so
you don't make that mistake again."
## Mistakes to avoid
- Don't use hedge words: "simply", "easily", "just", "seamlessly"
- Don't use meta-commentary: "it's worth noting that...", "it's important to understand that..."
- Don't use "allows you to" or "enables you to" — use "lets you" or rephrase
- Don't use "we" — use "you" or "Docker"
- Don't use "click" — use "select"
- Don't bold product names or for emphasis — only bold UI elements
- Don't use time-relative language: "currently", "new", "recently", "now"
- Don't edit vendored content in `_vendor/` or `data/cli/`
After completing work that reveals a non-obvious pattern or repo quirk not
already documented here, propose an update to this file. For automated
sessions, note the learning in a comment on the issue. For human-supervised
sessions, discuss with the user whether to update CLAUDE.md directly.