Documentation pipeline automation and docs-as-code workflows
Automates documentation-as-code pipelines with SSG setup, CI/CD configurations, and linting. Use when building docs pipelines, configuring Docusaurus/MkDocs, or setting up automated publishing workflows.
/plugin marketplace add melodic-software/claude-code-plugins/plugin install documentation-standards@melodic-softwareThis skill is limited to using the following tools:
Use this skill when:
Implement documentation-as-code workflows with automated pipelines, version control, and CI/CD integration.
Before implementing docs-as-code:
docs-management skill for documentation patternsDocs-as-Code Principles:
┌─────────────────────────────────────────────────────────────────────────────┐
│ 1. Version Control │
│ - Docs live alongside code in the same repository │
│ - Changes tracked with meaningful commit messages │
│ - Branch-based workflow for documentation updates │
├─────────────────────────────────────────────────────────────────────────────┤
│ 2. Review Process │
│ - Pull requests for documentation changes │
│ - Technical review by subject matter experts │
│ - Style review by technical writers │
├─────────────────────────────────────────────────────────────────────────────┤
│ 3. Automated Testing │
│ - Linting for style and grammar │
│ - Link validation │
│ - Build verification │
├─────────────────────────────────────────────────────────────────────────────┤
│ 4. Continuous Deployment │
│ - Automatic publishing on merge │
│ - Preview deployments for PRs │
│ - Versioned documentation releases │
└─────────────────────────────────────────────────────────────────────────────┘
| Tool | Language | Best For | Build Speed |
|---|---|---|---|
| Docusaurus | JS/React | Product docs, versioning | Fast |
| MkDocs | Python | Technical docs, Material theme | Fast |
| Sphinx | Python | API docs, reStructuredText | Medium |
| Hugo | Go | Large sites, speed | Very Fast |
| Astro | JS | Modern sites, MDX | Fast |
| VitePress | JS/Vue | Vue ecosystem | Very Fast |
| Jekyll | Ruby | GitHub Pages native | Medium |
docs/
├── docusaurus.config.js # Main configuration
├── sidebars.js # Navigation structure
├── package.json # Dependencies
├── docs/ # Documentation content
│ ├── intro.md
│ ├── getting-started/
│ │ ├── installation.md
│ │ └── configuration.md
│ ├── guides/
│ │ ├── quick-start.md
│ │ └── advanced.md
│ └── api/
│ └── reference.md
├── blog/ # Blog posts (optional)
├── src/
│ ├── components/ # React components
│ ├── css/ # Custom styles
│ └── pages/ # Custom pages
├── static/ # Static assets
│ └── img/
└── versioned_docs/ # Version snapshots
├── version-1.0/
└── version-2.0/
docs/
├── mkdocs.yml # Configuration
├── docs/
│ ├── index.md
│ ├── getting-started/
│ │ ├── installation.md
│ │ └── configuration.md
│ ├── user-guide/
│ │ └── features.md
│ ├── reference/
│ │ └── api.md
│ └── about/
│ └── changelog.md
├── overrides/ # Theme customization
│ ├── main.html
│ └── partials/
└── requirements.txt # Python dependencies
// docusaurus.config.js
const config = {
title: 'Project Name',
tagline: 'Project tagline',
url: 'https://docs.example.com',
baseUrl: '/',
onBrokenLinks: 'throw',
onBrokenMarkdownLinks: 'warn',
favicon: 'img/favicon.ico',
organizationName: 'organization',
projectName: 'project',
i18n: {
defaultLocale: 'en',
locales: ['en'],
},
presets: [
[
'classic',
{
docs: {
sidebarPath: require.resolve('./sidebars.js'),
editUrl: 'https://github.com/org/project/edit/main/docs/',
showLastUpdateAuthor: true,
showLastUpdateTime: true,
versions: {
current: {
label: 'Next',
path: 'next',
},
},
},
blog: {
showReadingTime: true,
editUrl: 'https://github.com/org/project/edit/main/docs/',
},
theme: {
customCss: require.resolve('./src/css/custom.css'),
},
},
],
],
themeConfig: {
navbar: {
title: 'Project',
logo: {
alt: 'Project Logo',
src: 'img/logo.svg',
},
items: [
{ type: 'doc', docId: 'intro', position: 'left', label: 'Docs' },
{ to: '/blog', label: 'Blog', position: 'left' },
{ type: 'docsVersionDropdown', position: 'right' },
{ href: 'https://github.com/org/project', label: 'GitHub', position: 'right' },
],
},
footer: {
style: 'dark',
links: [
{
title: 'Docs',
items: [{ label: 'Getting Started', to: '/docs/intro' }],
},
{
title: 'Community',
items: [{ label: 'Discord', href: 'https://discord.gg/xxx' }],
},
],
copyright: `Copyright © ${new Date().getFullYear()} Organization.`,
},
prism: {
theme: require('prism-react-renderer/themes/github'),
darkTheme: require('prism-react-renderer/themes/dracula'),
additionalLanguages: ['csharp', 'powershell', 'bash'],
},
algolia: {
appId: 'YOUR_APP_ID',
apiKey: 'YOUR_SEARCH_API_KEY',
indexName: 'project',
},
},
};
module.exports = config;
# mkdocs.yml
site_name: Project Documentation
site_url: https://docs.example.com
repo_url: https://github.com/org/project
repo_name: org/project
edit_uri: edit/main/docs/
theme:
name: material
palette:
- scheme: default
primary: indigo
accent: indigo
toggle:
icon: material/brightness-7
name: Switch to dark mode
- scheme: slate
primary: indigo
accent: indigo
toggle:
icon: material/brightness-4
name: Switch to light mode
features:
- navigation.instant
- navigation.tracking
- navigation.tabs
- navigation.sections
- navigation.expand
- navigation.indexes
- toc.follow
- content.code.copy
- content.code.annotate
- search.suggest
- search.highlight
plugins:
- search
- git-revision-date-localized:
enable_creation_date: true
- minify:
minify_html: true
- mike:
version_selector: true
css_dir: css
javascript_dir: js
markdown_extensions:
- pymdownx.highlight:
anchor_linenums: true
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format
- pymdownx.tabbed:
alternate_style: true
- admonition
- pymdownx.details
- attr_list
- md_in_html
- toc:
permalink: true
nav:
- Home: index.md
- Getting Started:
- Installation: getting-started/installation.md
- Configuration: getting-started/configuration.md
- User Guide:
- Features: user-guide/features.md
- Reference:
- API: reference/api.md
- About:
- Changelog: about/changelog.md
extra:
version:
provider: mike
social:
- icon: fontawesome/brands/github
link: https://github.com/org/project
# .github/workflows/docs.yml
name: Documentation
on:
push:
branches: [main]
paths:
- 'docs/**'
- '.github/workflows/docs.yml'
pull_request:
branches: [main]
paths:
- 'docs/**'
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: docs/package-lock.json
- name: Install dependencies
working-directory: docs
run: npm ci
- name: Lint Markdown
run: npx markdownlint-cli2 "docs/**/*.md"
- name: Check links
working-directory: docs
run: npm run check-links
build:
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: docs/package-lock.json
- name: Install dependencies
working-directory: docs
run: npm ci
- name: Build
working-directory: docs
run: npm run build
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: docs/build
deploy:
if: github.ref == 'refs/heads/main'
needs: build
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
preview:
if: github.event_name == 'pull_request'
needs: build
runs-on: ubuntu-latest
steps:
- name: Deploy preview
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: docs/build
destination_dir: pr-preview/${{ github.event.number }}
- name: Comment preview URL
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '📚 Documentation preview: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/pr-preview/${{ github.event.number }}/'
})
# azure-pipelines.yml
trigger:
branches:
include:
- main
paths:
include:
- docs/**
pool:
vmImage: 'ubuntu-latest'
variables:
pythonVersion: '3.11'
stages:
- stage: Validate
jobs:
- job: Lint
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '$(pythonVersion)'
- script: |
pip install -r docs/requirements.txt
pip install markdownlint-cli2
displayName: 'Install dependencies'
- script: markdownlint-cli2 "docs/**/*.md"
displayName: 'Lint Markdown'
- stage: Build
dependsOn: Validate
jobs:
- job: Build
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '$(pythonVersion)'
- script: pip install -r docs/requirements.txt
displayName: 'Install dependencies'
- script: mkdocs build --strict
displayName: 'Build documentation'
- publish: site
artifact: documentation
- stage: Deploy
dependsOn: Build
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
jobs:
- deployment: Deploy
environment: production
strategy:
runOnce:
deploy:
steps:
- download: current
artifact: documentation
- task: AzureStaticWebApp@0
inputs:
app_location: '$(Pipeline.Workspace)/documentation'
azure_static_web_apps_api_token: $(AZURE_STATIC_WEB_APPS_TOKEN)
// .markdownlint.json
{
"default": true,
"MD001": true,
"MD003": { "style": "atx" },
"MD004": { "style": "dash" },
"MD007": { "indent": 2 },
"MD009": true,
"MD010": true,
"MD012": true,
"MD013": { "line_length": 120, "code_blocks": false, "tables": false },
"MD022": { "lines_above": 1, "lines_below": 1 },
"MD024": { "siblings_only": true },
"MD025": { "front_matter_title": "" },
"MD026": { "punctuation": ".,;:!" },
"MD029": { "style": "ordered" },
"MD030": { "ul_single": 1, "ol_single": 1, "ul_multi": 1, "ol_multi": 1 },
"MD033": { "allowed_elements": ["details", "summary", "kbd", "br"] },
"MD035": { "style": "---" },
"MD036": false,
"MD040": true,
"MD041": true,
"MD046": { "style": "fenced" },
"MD048": { "style": "backtick" }
}
# .vale.ini
StylesPath = styles
MinAlertLevel = suggestion
Packages = Microsoft, write-good, proselint
[*.md]
BasedOnStyles = Vale, Microsoft, write-good
# Ignore code blocks
BlockIgnores = (?s) *(`{3}.*?`{3})
# Custom rules
Microsoft.Contractions = NO
Microsoft.HeadingPunctuation = YES
write-good.Passive = YES
write-good.Weasel = YES
write-good.TooWordy = YES
// API Documentation Generation with XML Comments
/// <summary>
/// Manages user authentication and authorization.
/// </summary>
/// <remarks>
/// <para>
/// This service handles all authentication flows including:
/// <list type="bullet">
/// <item>Password-based authentication</item>
/// <item>OAuth 2.0 / OpenID Connect</item>
/// <item>API key authentication</item>
/// </list>
/// </para>
/// </remarks>
/// <example>
/// <code>
/// var result = await authService.AuthenticateAsync(credentials);
/// if (result.IsSuccess)
/// {
/// var token = result.Value.AccessToken;
/// }
/// </code>
/// </example>
public interface IAuthenticationService
{
/// <summary>
/// Authenticates a user with the provided credentials.
/// </summary>
/// <param name="credentials">The authentication credentials.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>
/// A result containing the authentication response on success,
/// or an error on failure.
/// </returns>
/// <exception cref="AuthenticationException">
/// Thrown when authentication fails due to invalid credentials.
/// </exception>
Task<Result<AuthenticationResponse>> AuthenticateAsync(
AuthenticationCredentials credentials,
CancellationToken cancellationToken = default);
}
// Program.cs - Swagger/OpenAPI configuration
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Project API",
Version = "v1",
Description = "API documentation for the Project",
Contact = new OpenApiContact
{
Name = "API Support",
Email = "api@example.com",
Url = new Uri("https://docs.example.com")
},
License = new OpenApiLicense
{
Name = "MIT",
Url = new Uri("https://opensource.org/licenses/MIT")
}
});
// Include XML comments
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
options.IncludeXmlComments(xmlPath);
// Security definitions
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme.",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "bearer"
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
Array.Empty<string>()
}
});
});
// check-links.mjs
import { remark } from 'remark';
import remarkLintNoDeadUrls from 'remark-lint-no-dead-urls';
import { read } from 'to-vfile';
import { glob } from 'glob';
import { reporter } from 'vfile-reporter';
const files = await glob('docs/**/*.md');
for (const file of files) {
const result = await remark()
.use(remarkLintNoDeadUrls, {
skipLocalhost: true,
skipOffline: true,
})
.process(await read(file));
console.error(reporter(result));
}
// docs.spec.ts - Playwright screenshot tests
import { test, expect } from '@playwright/test';
test.describe('Documentation Screenshots', () => {
test('homepage renders correctly', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveScreenshot('homepage.png');
});
test('API reference page', async ({ page }) => {
await page.goto('/docs/api/reference');
await expect(page.locator('.api-content')).toBeVisible();
await expect(page).toHaveScreenshot('api-reference.png');
});
test('dark mode toggle', async ({ page }) => {
await page.goto('/');
await page.click('[data-theme-toggle]');
await expect(page).toHaveScreenshot('homepage-dark.png');
});
});
| Practice | Description |
|---|---|
| Single Source of Truth | Each concept documented in one place |
| Progressive Disclosure | Start simple, link to details |
| Consistent Structure | Same sections across similar pages |
| Clear Navigation | Logical hierarchy, breadcrumbs |
| Search Optimization | Good headings, keywords, descriptions |
Documentation Versioning:
Option 1: Version Folders (Docusaurus/MkDocs mike)
├── docs/
│ ├── version-1.0/
│ ├── version-2.0/
│ └── current/
Option 2: Branch-based
├── main (current)
├── v1.x
└── v2.x
Option 3: Git Tags
├── v1.0.0
├── v2.0.0
└── latest
Recommendation: Use tool-native versioning (Docusaurus versioned_docs, mike for MkDocs)
When implementing docs-as-code:
For detailed guidance:
Last Updated: 2025-12-26
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.