library/specializations/technical-documentation/skills/confluence-docs/SKILL.md
Atlassian Confluence integration for enterprise documentation. Create and update pages via API, manage spaces and permissions, handle content migration, and sync between Markdown and Confluence.
npx skillsauth add a5c-ai/babysitter confluence-docsInstall 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.
Atlassian Confluence integration for enterprise documentation.
Invoke this skill when you need to:
| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | action | string | Yes | create, update, migrate, export | | baseUrl | string | Yes | Confluence instance URL | | spaceKey | string | Yes | Target space key | | sourcePath | string | No | Source Markdown files | | pageId | string | No | Specific page ID for updates | | parentPageId | string | No | Parent page for hierarchy |
{
"action": "migrate",
"baseUrl": "https://company.atlassian.net/wiki",
"spaceKey": "DOCS",
"sourcePath": "./docs",
"parentPageId": "123456"
}
{
"baseUrl": "https://company.atlassian.net/wiki",
"auth": {
"type": "token",
"email": "${CONFLUENCE_EMAIL}",
"token": "${CONFLUENCE_TOKEN}"
},
"space": {
"key": "DOCS",
"name": "Documentation"
},
"migration": {
"preserveStructure": true,
"convertTables": true,
"uploadImages": true,
"macroMapping": {
"note": "info",
"warning": "warning",
"code": "code"
}
},
"sync": {
"dryRun": false,
"updateExisting": true,
"createMissing": true,
"archiveRemoved": false
}
}
const ConfluenceClient = require('confluence-api');
class ConfluenceManager {
constructor(config) {
this.client = new ConfluenceClient({
username: config.email,
password: config.token,
baseUrl: config.baseUrl
});
}
// Create a new page
async createPage(spaceKey, title, content, parentId = null) {
const page = {
type: 'page',
title,
space: { key: spaceKey },
body: {
storage: {
value: content,
representation: 'storage'
}
}
};
if (parentId) {
page.ancestors = [{ id: parentId }];
}
return await this.client.postContent(page);
}
// Update existing page
async updatePage(pageId, title, content, version) {
const page = {
id: pageId,
type: 'page',
title,
version: { number: version + 1 },
body: {
storage: {
value: content,
representation: 'storage'
}
}
};
return await this.client.putContent(page);
}
// Get page by title
async getPageByTitle(spaceKey, title) {
const result = await this.client.getContentBySpaceKey(spaceKey, {
title,
expand: 'version,body.storage'
});
return result.results[0] || null;
}
// Upload attachment
async uploadAttachment(pageId, filePath, comment = '') {
const form = new FormData();
form.append('file', fs.createReadStream(filePath));
form.append('comment', comment);
return await this.client.createAttachment(pageId, form);
}
// Add labels
async addLabels(pageId, labels) {
const labelPayload = labels.map(name => ({
prefix: 'global',
name
}));
return await this.client.postLabels(pageId, labelPayload);
}
}
const marked = require('marked');
class MarkdownToConfluence {
constructor(options = {}) {
this.options = options;
this.attachments = [];
}
convert(markdown, metadata = {}) {
// Parse front matter
const { content, frontMatter } = this.parseFrontMatter(markdown);
// Convert markdown to HTML
let html = marked.parse(content);
// Convert to Confluence storage format
html = this.convertToStorageFormat(html);
// Handle macros
html = this.convertMacros(html);
// Handle code blocks
html = this.convertCodeBlocks(html);
// Handle images
html = this.convertImages(html);
// Handle tables
html = this.convertTables(html);
return {
title: frontMatter.title || metadata.title,
content: html,
labels: frontMatter.tags || [],
attachments: this.attachments
};
}
convertMacros(html) {
// Convert admonitions to Confluence macros
const macroMap = {
'note': 'info',
'warning': 'warning',
'tip': 'tip',
'danger': 'warning'
};
for (const [mdType, confType] of Object.entries(macroMap)) {
const regex = new RegExp(`<div class="${mdType}">([\\s\\S]*?)</div>`, 'g');
html = html.replace(regex, (match, content) => {
return `<ac:structured-macro ac:name="${confType}">
<ac:rich-text-body>${content}</ac:rich-text-body>
</ac:structured-macro>`;
});
}
return html;
}
convertCodeBlocks(html) {
// Convert code blocks to Confluence code macro
return html.replace(
/<pre><code class="language-(\w+)">([\s\S]*?)<\/code><\/pre>/g,
(match, language, code) => {
const decodedCode = this.decodeHtml(code);
return `<ac:structured-macro ac:name="code">
<ac:parameter ac:name="language">${language}</ac:parameter>
<ac:plain-text-body><![CDATA[${decodedCode}]]></ac:plain-text-body>
</ac:structured-macro>`;
}
);
}
convertImages(html) {
// Convert images to Confluence attachments
return html.replace(
/<img src="([^"]+)" alt="([^"]*)"[^>]*>/g,
(match, src, alt) => {
if (src.startsWith('http')) {
// External image
return `<ac:image><ri:url ri:value="${src}" /></ac:image>`;
} else {
// Local attachment
const filename = path.basename(src);
this.attachments.push({ src, filename });
return `<ac:image><ri:attachment ri:filename="${filename}" /></ac:image>`;
}
}
);
}
convertTables(html) {
// Confluence uses standard HTML tables but needs specific attributes
return html.replace(/<table>/g, '<table class="wrapped">');
}
}
class ConfluenceToMarkdown {
constructor(client) {
this.client = client;
}
async exportSpace(spaceKey, outputDir) {
const pages = await this.getAllPages(spaceKey);
const structure = this.buildHierarchy(pages);
for (const page of pages) {
const markdown = await this.exportPage(page);
const filePath = this.getFilePath(page, structure, outputDir);
await fs.mkdir(path.dirname(filePath), { recursive: true });
await fs.writeFile(filePath, markdown);
}
return { exported: pages.length };
}
async exportPage(page) {
const content = page.body.storage.value;
// Convert Confluence storage format to Markdown
let markdown = this.convertToMarkdown(content);
// Add front matter
const frontMatter = {
title: page.title,
confluence_id: page.id,
last_modified: page.version.when
};
return `---
${yaml.stringify(frontMatter)}---
${markdown}`;
}
convertToMarkdown(storage) {
let md = storage;
// Convert code macro
md = md.replace(
/<ac:structured-macro ac:name="code"[^>]*>[\s\S]*?<ac:parameter ac:name="language">(\w+)<\/ac:parameter>[\s\S]*?<ac:plain-text-body><!\[CDATA\[([\s\S]*?)\]\]><\/ac:plain-text-body>[\s\S]*?<\/ac:structured-macro>/g,
(match, lang, code) => `\`\`\`${lang}\n${code}\n\`\`\``
);
// Convert info macro
md = md.replace(
/<ac:structured-macro ac:name="(info|warning|tip)"[^>]*>[\s\S]*?<ac:rich-text-body>([\s\S]*?)<\/ac:rich-text-body>[\s\S]*?<\/ac:structured-macro>/g,
(match, type, content) => `> **${type.toUpperCase()}:** ${this.stripHtml(content)}`
);
// Convert headings, lists, etc.
md = this.convertHtmlToMarkdown(md);
return md;
}
}
async function syncDocumentation(config) {
const confluence = new ConfluenceManager(config);
const converter = new MarkdownToConfluence(config.migration);
// Get local files
const localFiles = await glob('docs/**/*.md');
// Get Confluence pages
const pages = await confluence.getSpaceContent(config.space.key);
const results = {
created: [],
updated: [],
skipped: [],
errors: []
};
for (const file of localFiles) {
try {
const markdown = await fs.readFile(file, 'utf8');
const converted = converter.convert(markdown, { file });
// Check if page exists
const existing = await confluence.getPageByTitle(
config.space.key,
converted.title
);
if (existing) {
if (config.sync.updateExisting) {
await confluence.updatePage(
existing.id,
converted.title,
converted.content,
existing.version.number
);
results.updated.push(file);
} else {
results.skipped.push(file);
}
} else if (config.sync.createMissing) {
await confluence.createPage(
config.space.key,
converted.title,
converted.content,
config.parentPageId
);
results.created.push(file);
}
// Upload attachments
for (const attachment of converted.attachments) {
await confluence.uploadAttachment(
existing?.id || results.created[results.created.length - 1].id,
attachment.src
);
}
} catch (error) {
results.errors.push({ file, error: error.message });
}
}
return results;
}
async function createDocumentationSpace(config) {
const client = new ConfluenceManager(config);
const space = await client.client.postSpace({
key: config.space.key,
name: config.space.name,
description: {
plain: { value: config.space.description, representation: 'plain' }
},
permissions: [
{
subjects: { group: { name: 'confluence-users' } },
operation: { key: 'read', target: 'space' }
}
]
});
// Create home page
await client.createPage(
config.space.key,
'Home',
'<h1>Welcome to Documentation</h1>',
null
);
return space;
}
{
"devDependencies": {
"confluence-api": "^1.4.0",
"marked": "^12.0.0",
"gray-matter": "^4.0.0",
"form-data": "^4.0.0"
}
}
# Sync Markdown to Confluence
node scripts/confluence-sync.js --config confluence.config.json
# Export Confluence to Markdown
node scripts/confluence-export.js --space DOCS --output ./exported
# Create new space
node scripts/confluence-space.js create --key NEWDOCS --name "New Documentation"
development
Model documentation skill for generating model cards following Google's model card framework.
development
MLflow integration skill for experiment tracking, model registry, and artifact management. Enables LLMs to log experiments, compare runs, manage model lifecycle, and retrieve artifacts through the MLflow API.
data-ai
LIME-based local explanation skill for individual predictions across tabular, text, and image data.
devops
Kubeflow Pipelines skill for ML workflow orchestration, component management, and Kubernetes-native ML.